summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTobias Stoeckmann <tobias@stoeckmann.org>2014-10-26 13:51:55 +0100
committerAndreas Gruenbacher <agruen@linbit.com>2014-10-30 22:47:40 +0100
commite25e622dec2ff7579ba561188144d44c3ddad24f (patch)
treeddfc9244e8a69190d915826d202812633402651c
parentf926295f4fdfb2777bbd4bfa51441fd5a4f0a9b0 (diff)
downloadpatch-e25e622dec2ff7579ba561188144d44c3ddad24f.tar.gz
Buffer overflow on malicious input file
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.
-rw-r--r--src/inp.c9
1 files changed, 8 insertions, 1 deletions
diff --git a/src/inp.c b/src/inp.c
index 386dc9e..a7ad070 100644
--- a/src/inp.c
+++ b/src/inp.c
@@ -46,6 +46,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_many_lines (char const *) __attribute__((noreturn));
+static void lines_too_long (char const *) __attribute__((noreturn));
/* New patch--prepare to edit another file. */
@@ -128,6 +129,11 @@ too_many_lines (char const *filename)
fatal ("File %s has too many lines", quotearg (filename));
}
+static void
+lines_too_long (char const *filename)
+{
+ fatal ("Lines in file %s are too long", quotearg (filename));
+}
bool
get_input_file (char const *filename, char const *outname, mode_t file_type)
@@ -367,7 +373,8 @@ plan_b (char const *filename)
while ((c = getc (ifp)) != EOF)
{
- len++;
+ if (++len > ((size_t) -1) / 2)
+ lines_too_long (filename);
if (c == '\n')
{