Hi,

our patch implementation supports lines with a maximum of 8192 chars,
which should be reasonably large enough.  If files cannot be patched in
memory, they are written into temporary files -- also known as "plan b".
For plan b, the maximum line length is 1024, which is still more than
enough.  At least for real life use cases.

FYI: The buffer size is used to create the temporary files in a "RAM"
way. Every line takes 1024 bytes so it's quite easy to jump around in
that file and fetch lines.

Unfortunately this limit is not properly checked while performing a
plan b patch.  Here is how to reproduce:

First, prepare two files which are 1024 chars in size in one line and
differ.  The first one contains lots of c's, the other lots of d's.
Finally, create a diff.

$ tr '\0' c < /dev/zero | dd bs=1k count=1 of=file1
$ tr '\0' d < /dev/zero | dd bs=1k count=1 of=file2
$ diff -Nau file1 file2 > file.diff

Now try to patch it (-x 8 enforces plan b):

$ patch -x 8 -Np1 -i file.diff
Hmm...  Looks like a unified diff to me...
The text leading up to this was:
--------------------------
|--- file1      Sun Nov  9 17:49:14 2014
|+++ file2      Sun Nov  9 17:49:18 2014
--------------------------
Floating point exception (core dumped)
$ _

This fix is not as comprehensive as what GNU patch did.  They totally
removed the line length limitations -- something that sounds interesting
but probably not common enough to encounter.  At least not common enough
for me to actually address the limitations.

Instead, just make sure that we properly bail out if lines are too
long.


Tobias

Index: inp.c
===================================================================
RCS file: /cvs/src/usr.bin/patch/inp.c,v
retrieving revision 1.38
diff -u -p -r1.38 inp.c
--- inp.c       8 Oct 2014 04:06:23 -0000       1.38
+++ inp.c       9 Nov 2014 16:45:33 -0000
@@ -381,6 +381,8 @@ plan_b(const char *filename)
                            revision);
        }
        fseek(ifp, 0L, SEEK_SET);       /* rewind file */
+       if (maxlen > BUFFERSIZE)
+               fatal("lines too long\n");
        lines_per_buf = BUFFERSIZE / maxlen;
        tireclen = maxlen;
        tibuf[0] = malloc(BUFFERSIZE + 1);

Reply via email to