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