# HG changeset patch
# User Matt Mackall <mpm@selenic.com>
# Date 1386808422 21600
#      Wed Dec 11 18:33:42 2013 -0600
# Branch stable
# Node ID 09e41ac6289d878f2a2e4e7b7794f457ec7a069b
# Parent  1ddf4409229fd9d8c610c7d327c1a72264d107f4
mpatch: rewrite pointer overflow checks

[jcristau: backport to 2.2.2
 * Py_ssize_t -> int
 * v1_hdrsize -> hdrsize
]

--- mercurial-2.2.2.orig/mercurial/mpatch.c
+++ mercurial-2.2.2/mercurial/mpatch.c
@@ -199,34 +199,31 @@ static struct flist *combine(struct flis
 /* decode a binary patch into a hunk list */
 static struct flist *decode(const char *bin, int len)
 {
 	struct flist *l;
 	struct frag *lt;
-	const char *data = bin + 12, *end = bin + len;
+	int pos = 0;
 
 	/* assume worst case size, we won't have many of these lists */
 	l = lalloc(len / 12);
 	if (!l)
 		return NULL;
 
 	lt = l->tail;
 
-	while (data <= end) {
-		lt->start = getbe32(bin);
-		lt->end = getbe32(bin + 4);
-		lt->len = getbe32(bin + 8);
+	while (pos >= 0 && pos < len) {
+		lt->start = getbe32(bin + pos);
+		lt->end = getbe32(bin + pos + 4);
+		lt->len = getbe32(bin + pos + 8);
 		if (lt->start > lt->end)
 			break; /* sanity check */
-		bin = data + lt->len;
-		if (bin < data)
-			break; /* big data + big (bogus) len can wrap around */
-		lt->data = data;
-		data = bin + 12;
+		lt->data = bin + pos + 12;
+		pos += 12 + lt->len;
 		lt++;
 	}
 
-	if (bin != end) {
+	if (pos != len) {
 		if (!PyErr_Occurred())
 			PyErr_SetString(mpatch_Error, "patch cannot be decoded");
 		lfree(l);
 		return NULL;
 	}
@@ -354,36 +351,30 @@ cleanup:
 
 /* calculate size of a patched file directly */
 static PyObject *
 patchedsize(PyObject *self, PyObject *args)
 {
-	long orig, start, end, len, outlen = 0, last = 0;
+	long orig, start, end, len, outlen = 0, last = 0, pos = 0;
 	int patchlen;
-	char *bin, *binend, *data;
+	char *bin;
 
 	if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen))
 		return NULL;
 
-	binend = bin + patchlen;
-	data = bin + 12;
-
-	while (data <= binend) {
-		start = getbe32(bin);
-		end = getbe32(bin + 4);
-		len = getbe32(bin + 8);
+	while (pos >= 0 && pos < patchlen) {
+		start = getbe32(bin + pos);
+		end = getbe32(bin + pos + 4);
+		len = getbe32(bin + pos + 8);
 		if (start > end)
 			break; /* sanity check */
-		bin = data + len;
-		if (bin < data)
-			break; /* big data + big (bogus) len can wrap around */
-		data = bin + 12;
+		pos += 12 + len;
 		outlen += start - last;
 		last = end;
 		outlen += len;
 	}
 
-	if (bin != binend) {
+	if (pos != patchlen) {
 		if (!PyErr_Occurred())
 			PyErr_SetString(mpatch_Error, "patch cannot be decoded");
 		return NULL;
 	}
 
--- mercurial-2.2.2.orig/mercurial/parsers.c
+++ mercurial-2.2.2/mercurial/parsers.c
@@ -137,14 +137,14 @@ quit:
 
 static PyObject *parse_dirstate(PyObject *self, PyObject *args)
 {
 	PyObject *dmap, *cmap, *parents = NULL, *ret = NULL;
 	PyObject *fname = NULL, *cname = NULL, *entry = NULL;
-	char *str, *cur, *end, *cpos;
+	char *cur, *str, *cpos;
 	int state, mode, size, mtime;
 	unsigned int flen;
-	int len;
+	int len, pos = 40;
 
 	if (!PyArg_ParseTuple(args, "O!O!s#:parse_dirstate",
 			      &PyDict_Type, &dmap,
 			      &PyDict_Type, &cmap,
 			      &str, &len))
@@ -157,22 +157,21 @@ static PyObject *parse_dirstate(PyObject
 	parents = Py_BuildValue("s#s#", str, 20, str + 20, 20);
 	if (!parents)
 		goto quit;
 
 	/* read filenames */
-	cur = str + 40;
-	end = str + len;
-
-	while (cur < end - 17) {
+	while (pos >= 40 && pos < len) {
+		cur = str + pos;
 		/* unpack header */
 		state = *cur;
 		mode = getbe32(cur + 1);
 		size = getbe32(cur + 5);
 		mtime = getbe32(cur + 9);
 		flen = getbe32(cur + 13);
+		pos += 17;
 		cur += 17;
-		if (cur + flen > end || cur + flen < cur) {
+		if (flen > len - pos || flen < 0) {
 			PyErr_SetString(PyExc_ValueError, "overflow in dirstate");
 			goto quit;
 		}
 
 		entry = Py_BuildValue("ciii", state, mode, size, mtime);
@@ -194,14 +193,14 @@ static PyObject *parse_dirstate(PyObject
 			fname = PyBytes_FromStringAndSize(cur, flen);
 			if (!fname ||
 			    PyDict_SetItem(dmap, fname, entry) == -1)
 				goto quit;
 		}
-		cur += flen;
 		Py_DECREF(fname);
 		Py_DECREF(entry);
 		fname = cname = entry = NULL;
+		pos += flen;
 	}
 
 	ret = parents;
 	Py_INCREF(ret);
 quit:
@@ -928,33 +927,28 @@ static int index_assign_subscript(indexO
  * the optional "offsets" table with those entries.
  */
 static long inline_scan(indexObject *self, const char **offsets)
 {
 	const char *data = PyString_AS_STRING(self->data);
-	const char *end = data + PyString_GET_SIZE(self->data);
+	Py_ssize_t pos = 0;
+	Py_ssize_t end = PyString_GET_SIZE(self->data);
 	const long hdrsize = 64;
 	long incr = hdrsize;
 	Py_ssize_t len = 0;
 
-	while (data + hdrsize <= end) {
+	while (pos + hdrsize <= end && pos >= 0) {
 		uint32_t comp_len;
-		const char *old_data;
 		/* 3rd element of header is length of compressed inline data */
-		comp_len = getbe32(data + 8);
+		comp_len = getbe32(data + pos + 8);
 		incr = hdrsize + comp_len;
-		if (incr < hdrsize)
-			break;
 		if (offsets)
-			offsets[len] = data;
+			offsets[len] = data + pos;
 		len++;
-		old_data = data;
-		data += incr;
-		if (data <= old_data)
-			break;
+		pos += incr;
 	}
 
-	if (data != end && data + hdrsize != end) {
+	if (pos != end) {
 		if (!PyErr_Occurred())
 			PyErr_SetString(PyExc_ValueError, "corrupt index file");
 		return -1;
 	}
 
