Blob Blame History Raw
diff -ru mercurial-2.6.2/mercurial/mpatch.c mercurial-2.6.2_patched/mercurial/mpatch.c
--- mercurial-2.6.2/mercurial/mpatch.c	2013-06-02 00:10:16.000000000 +0200
+++ mercurial-2.6.2_patched/mercurial/mpatch.c	2019-05-07 16:51:13.631774481 +0200
@@ -74,6 +74,35 @@
 	return a->tail - a->head;
 }
 
+/* add helper to add src and *dest iff it won't overflow */
+static inline int safeadd(int src, int *dest)
+{
+	if ((src > 0) == (*dest > 0)) {
+		if (*dest > 0) {
+			if (src > (INT_MAX - *dest)) {
+				return 0;
+			}
+		} else {
+			if (src < (INT_MIN - *dest)) {
+				return 0;
+			}
+		}
+	}
+	*dest += src;
+	return 1;
+}
+
+/* subtract src from dest and store result in dest */
+static inline int safesub(int src, int *dest)
+{
+	if (((src > 0) && (*dest < INT_MIN + src)) ||
+	    ((src < 0) && (*dest > INT_MAX + src))) {
+		return 0;
+	}
+	*dest -= src;
+	return 1;
+}
+
 /* move hunks in source that are less cut to dest, compensating
    for changes in offset. the last hunk may be split if necessary.
 */
@@ -83,18 +112,37 @@
 	int postend, c, l;
 
 	while (s != src->tail) {
-		if (s->start + offset >= cut)
+		int soffset = s->start;
+		if (!safeadd(offset, &soffset))
+			break; /* add would overflow, oh well */
+		if (soffset >= cut)
 			break; /* we've gone far enough */
 
-		postend = offset + s->start + s->len;
+		postend = offset;
+		if (!safeadd(s->start, &postend) ||
+		    !safeadd(s->len, &postend)) {
+			break;
+		}
 		if (postend <= cut) {
 			/* save this hunk */
-			offset += s->start + s->len - s->end;
+			int tmp = s->start;
+			if (!safesub(s->end, &tmp)) {
+				break;
+			}
+			if (!safeadd(s->len, &tmp)) {
+				break;
+			}
+			if (!safeadd(tmp, &offset)) {
+				break; /* add would overflow, oh well */
+			}
 			*d++ = *s++;
 		}
 		else {
 			/* break up this hunk */
-			c = cut - offset;
+			c = cut;
+			if (!safesub(offset, &c)) {
+				break;
+			}
 			if (s->end < c)
 				c = s->end;
 			l = cut - offset - s->start;
@@ -128,16 +176,40 @@
 	int postend, c, l;
 
 	while (s != src->tail) {
-		if (s->start + offset >= cut)
+		int cmpcut = s->start;
+		if (!safeadd(offset, &cmpcut)) {
+			break;
+		}
+		if (cmpcut >= cut)
 			break;
 
-		postend = offset + s->start + s->len;
+		postend = offset;
+		if (!safeadd(s->start, &postend)) {
+			break;
+		}
+		if (!safeadd(s->len, &postend)) {
+			break;
+		}
 		if (postend <= cut) {
-			offset += s->start + s->len - s->end;
+			/* do the subtraction first to avoid UB integer overflow
+			 */
+			int tmp = s->start;
+			if (!safesub(s->end, &tmp)) {
+				break;
+			}
+			if (!safeadd(s->len, &tmp)) {
+				break;
+			}
+			if (!safeadd(tmp, &offset)) {
+				break;
+			}
 			s++;
 		}
 		else {
-			c = cut - offset;
+			c = cut;
+			if (!safesub(offset, &c)) {
+				break;
+			}
 			if (s->end < c)
 				c = s->end;
 			l = cut - offset - s->start;
@@ -179,8 +251,18 @@
 
 			/* insert new hunk */
 			ct = c->tail;
-			ct->start = bh->start - offset;
-			ct->end = bh->end - post;
+			ct->start = bh->start;
+			ct->end = bh->end;
+			if (!safesub(offset, &(ct->start)) ||
+			    !safesub(post, &(ct->end))) {
+				/* It was already possible to exit
+				 * this function with a return value
+				 * of NULL before the safesub()s were
+				 * added, so this should be fine. */
+				lfree(c);
+				c = NULL;
+				goto done;
+			}
 			ct->len = bh->len;
 			ct->data = bh->data;
 			c->tail++;
@@ -191,7 +273,7 @@
 		memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a));
 		c->tail += lsize(a);
 	}
-
+done:
 	lfree(a);
 	lfree(b);
 	return c;
@@ -215,13 +297,17 @@
 		lt->start = getbe32(bin);
 		lt->end = getbe32(bin + 4);
 		lt->len = getbe32(bin + 8);
-		if (lt->start > lt->end)
-			break; /* sanity check */
-		bin = data + lt->len;
-		if (bin < data)
+		if (lt->start < 0 || lt->start > lt->end || lt->len < 0)
+ 			break; /* sanity check */
+		bin = data;
+		if (!safeadd(lt->len, &bin)) {
 			break; /* big data + big (bogus) len can wrap around */
+		}
 		lt->data = data;
-		data = bin + 12;
+		data = bin;
+		if (!safeadd(12, &data)) {
+			break;
+		}
 		lt++;
 	}
 
@@ -266,7 +352,8 @@
 	char *p = buf;
 
 	while (f != l->tail) {
-		if (f->start < last || f->end > len) {
+		if (f->start < last || f->start > len || f->end > len ||
+            last < 0) {
 			if (!PyErr_Occurred())
 				PyErr_SetString(mpatch_Error,
 				                "invalid patch");
@@ -279,6 +366,12 @@
 		p += f->len;
 		f++;
 	}
+	if (last < 0) {
+		if (!PyErr_Occurred())
+			PyErr_SetString(mpatch_Error,
+			                "invalid patch");
+		return 0;
+	}
 	memcpy(p, orig + last, len - last);
 	return 1;
 }