Hi,

there is a hard to reach but possible buffer overflow when using
patch with a very large (modified) input file.  I doubt you will ever
see this with a 64 bit system, but it's possible with 32 bit:

$ echo hello > file1
$ echo world > file2
$ diff -Nau file1 file2 > file.diff

Nothing fancy so far.  Adjust file1 so it contains at least one line that
is 2 GB in size.  Larger is fine too, but stay below 4 GB.

$ tr '\0' c < /dev/zero | dd bs=1K count=2097152 of=file1

Now try to patch it.

$ patch -Np0 -i file.diff
Segmentation fault

The issue is in patch's "plan b" strategy  (If your system would still
want to use "plan a", force patch to use "plan b" through debug flag).

Plan b writes lines into a temporary file, with equally long lines, so
it can use a buffer mechanism to access them in a kind of randomly
fassion.  In order to do that, it retrieves the longest line.

In this example, it will encounter the 2 GB line and stores that as the
longest one.  Afterwards it will adjust the tibufsize variable to be
large enough:

  for (tibufsize = TIBUFSIZE_MINIMUM;  tibufsize < maxlen;  tibufsize <<= 1)
    /* do nothing */ ;

Due to maxlen's size (2 GB), tibufsize will be SIZE_T_MAX, i.e. 4 GB.
A few lines later it allocates space for the tibuf buffers:

  tibuf[0] = xmalloc (2 * tibufsize);
  tibuf[1] = tibuf[0] + tibufsize;

This will allocate 0 bytes because tibufsize overflowed.  The next
time patch writes into the buffer, a segmentation fault will occur...
Depends on your system how long it takes until that happens. ;)

The fix is simple:  Bail out on lines that are too long.  Patch already
does that for files that have too many lines.


Tobias

--- patch-2.7.1.4-2f40-dirty/src/inp.c~ 2012-09-19 03:07:31.000000000 +0200
+++ patch-2.7.1.4-2f40-dirty/src/inp.c  2014-10-26 13:13:13.240167836 +0100
@@ -45,6 +45,7 @@
 static bool plan_a (char const *);     /* yield false if memory runs out */
 static void plan_b (char const *);
 static void report_revision (bool);
+static void too_long_lines (char const *) __attribute__((noreturn));
 static void too_many_lines (char const *) __attribute__((noreturn));
 
 /* New patch--prepare to edit another file. */
@@ -123,6 +124,12 @@
 
 
 static void
+too_long_lines (char const *filename)
+{
+  fatal ("File %s has too long lines", quotearg (filename));
+}
+
+static void
 too_many_lines (char const *filename)
 {
   fatal ("File %s has too many lines", quotearg (filename));
@@ -367,7 +374,8 @@
 
   while ((c = getc (ifp)) != EOF)
     {
-      len++;
+      if (++len > ((size_t)-1) / 2)
+       too_long_lines (filename);
 
       if (c == '\n')
        {

Reply via email to