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;
}