Hi,
it is possible to overflow line numbers in patch; this diff cares about
the lines specified in diff files. If such an overflow happens with
unified diffs, out of bound memory access can occur.
If you have a 32 bit system, take this one (LONG_MAX = 2^31 - 1):
--- a Sat Nov 15 00:25:29 2014
+++ b Sat Nov 15 00:06:50 2014
@@ -1 +2147483647,2147483647 @@
-a
+b
If you have a 64 bit system, take this one, untested (LONG_MAX = 2^63 - 1):
--- a Sat Nov 15 00:25:29 2014
+++ b Sat Nov 15 00:06:50 2014
@@ -1 +9223372036854775807,9223372036854775807 @@
-a
+b
$ arch
OpenBSD.i386
$ touch a
$ patch a arch.diff
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- a Sat Nov 15 00:25:29 2014
|+++ b Sat Nov 15 00:06:50 2014
--------------------------
Patching file a using Plan A...
Segmentation fault (core dumped)
$ _
Tobias
Index: pch.c
===================================================================
RCS file: /cvs/src/usr.bin/patch/pch.c,v
retrieving revision 1.45
diff -u -p -u -p -r1.45 pch.c
--- pch.c 25 Nov 2014 10:26:07 -0000 1.45
+++ pch.c 25 Nov 2014 15:19:59 -0000
@@ -585,12 +585,17 @@ another_hunk(void)
if (!*s)
malformed();
p_ptrn_lines = strtolinenum(s, &s) -
p_first + 1;
+ if (p_ptrn_lines < 0)
+ malformed();
} else if (p_first)
p_ptrn_lines = 1;
else {
p_ptrn_lines = 0;
p_first = 1;
}
+ if (p_first >= LINENUM_MAX - p_ptrn_lines ||
+ p_ptrn_lines >= LINENUM_MAX - 6)
+ malformed();
/* we need this much at least */
p_max = p_ptrn_lines + 6;
@@ -652,12 +657,17 @@ another_hunk(void)
malformed();
p_repl_lines = strtolinenum(s,
&s) -
p_newfirst + 1;
+ if (p_repl_lines < 0)
+ malformed();
} else if (p_newfirst)
p_repl_lines = 1;
else {
p_repl_lines = 0;
p_newfirst = 1;
}
+ if (p_newfirst >= LINENUM_MAX -
p_repl_lines ||
+ p_repl_lines >= LINENUM_MAX - p_end)
+ malformed();
p_max = p_repl_lines + p_end;
if (p_max > MAXHUNKSIZE)
fatal("hunk too large (%ld
lines) at line %ld: %s",
@@ -868,6 +878,10 @@ hunk_done:
s++;
if (*s != '@')
malformed();
+ if (p_first >= LINENUM_MAX - p_ptrn_lines ||
+ p_newfirst > LINENUM_MAX - p_repl_lines ||
+ p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
+ malformed();
if (!p_ptrn_lines)
p_first++; /* do append rather than insert */
p_max = p_ptrn_lines + p_repl_lines + 1;
@@ -1010,8 +1024,12 @@ hunk_done:
p_first = strtolinenum(buf, &s);
if (*s == ',') {
p_ptrn_lines = strtolinenum(s + 1, &s) - p_first + 1;
+ if (p_ptrn_lines < 0)
+ malformed();
} else
p_ptrn_lines = (*s != 'a');
+ if (p_first >= LINENUM_MAX - p_ptrn_lines)
+ malformed();
hunk_type = *s;
if (hunk_type == 'a')
p_first++; /* do append rather than insert */
@@ -1020,16 +1038,21 @@ hunk_done:
max = strtolinenum(s + 1, &s);
else
max = min;
+ if (min < 0 || min > max || max - min == LINENUM_MAX)
+ malformed();
if (hunk_type == 'd')
min++;
- p_end = p_ptrn_lines + 1 + max - min + 1;
+ p_newfirst = min;
+ p_repl_lines = max - min + 1;
+ if (p_newfirst > LINENUM_MAX - p_repl_lines ||
+ p_ptrn_lines >= LINENUM_MAX - p_repl_lines - 1)
+ malformed();
+ p_end = p_ptrn_lines + p_repl_lines + 1;
if (p_end > MAXHUNKSIZE)
fatal("hunk too large (%ld lines) at line %ld: %s",
p_end, p_input_line, buf);
while (p_end >= hunkmax)
grow_hunkmax();
- p_newfirst = min;
- p_repl_lines = max - min + 1;
snprintf(buf, sizeof buf, "*** %ld,%ld\n", p_first,
p_first + p_ptrn_lines - 1);
p_line[0] = savestr(buf);