diagnose unexpected EOF and zero lengths in packed data
authorPaul Eggert <eggert@cs.ucla.edu>
Sat, 8 Dec 2012 18:45:18 +0000 (10:45 -0800)
committerPaul Eggert <eggert@cs.ucla.edu>
Mon, 10 Dec 2012 17:43:21 +0000 (09:43 -0800)
Problem reported by Aki Helin.
* NEWS: Mention Aki's reports.
* tests/unpack-invalid: New file,
with test data suggested by Aki.
* tests/Makefile.am (TESTS): Add it.
* unpack.c (read_byte): New function.
(look_bits, read_tree): Use it.
(read_tree): Check against zero bit length Huffman code.

NEWS
tests/Makefile.am
tests/unpack-invalid [new file with mode: 0755]
unpack.c

diff --git a/NEWS b/NEWS
index 79c111022b689e6d6a54000fc4570266a932e40a..d184346e0dc03201559cbb6f81ff75b6d2092957 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,6 +4,9 @@ GNU gzip NEWS                                    -*- outline -*-
 
 ** Bug fixes
 
+  gzip -d no longer malfunctions with certain invalid data in 'pack' format.
+  Problem reported by Aki Helin.  [bug introduced in gzip-0.8]
+
   zgrep no longer malfunctions with a multi-digit context option like -15.
   Now, it passes that option to grep (equivalent to -C15) just as it does
   for single-digit options. [bug introduced in gzip-1.3.12]
index ddab493bd76cea2e76640b3b83dc6f1a3bf361d2..6578682e2bc44e05f275f884744f334bd161f869 100644 (file)
@@ -23,6 +23,7 @@ TESTS =                                       \
   null-suffix-clobber                  \
   stdin                                        \
   trailing-nul                         \
+  unpack-invalid                       \
   zdiff                                        \
   zgrep-f                              \
   zgrep-context                                \
diff --git a/tests/unpack-invalid b/tests/unpack-invalid
new file mode 100755 (executable)
index 0000000..eb862df
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+# gzip should report invalid 'unpack' input when uncompressing.
+# With gzip-1.5, it would output invalid data instead.
+
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+# limit so don't run it by default.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ..
+
+for input in \
+  '\037\036\000\000\037\213\010\000\000\000\000\000\002\003\036\000\000\000\002\003\037\213\010\000\000\000\000\000\002\003\355\301\001\015\000\000\000\302\240\037\000\302\240\037\213\010\000\000\000\000\000\002\003\355\301' \
+  '\037\213\010\000\000\000\000\000\002\003\355\301\001\015\000\000\000\302\240\076\366\017\370\036\016\030\000\000\000\000\000\000\000\000\000\034\010\105\140\104\025\020\047\000\000\037\036\016\030\000\000\000'; do
+
+  printf "$input" >in || framework_failure_
+
+  if gzip -d <in >/tmp/out 2>/tmp/err; then
+    fail=1
+  else
+    fail=0
+  fi
+done
+
+Exit $fail
index 44a232f8caf281ae30170277394fc54099e0a67e..8189aadca3ac45d1a58f02cb005057b55cec2751 100644 (file)
--- a/unpack.c
+++ b/unpack.c
@@ -76,6 +76,16 @@ local ulg bitbuf;
 local int valid;                  /* number of valid bits in bitbuf */
 /* all bits above the last valid bit are always zero */
 
+/* Read an input byte, reporting an error at EOF.  */
+static unsigned char
+read_byte (void)
+{
+  int b = get_byte ();
+  if (b < 0)
+    gzip_error ("invalid compressed data -- unexpected end of file");
+  return b;
+}
+
 /* Set code to the next 'bits' input bits without skipping them. code
  * must be the name of a simple variable and bits must not have side effects.
  * IN assertions: bits <= 25 (so that we still have room for an extra byte
@@ -83,7 +93,7 @@ local int valid;                  /* number of valid bits in bitbuf */
  */
 #define look_bits(code,bits,mask) \
 { \
-  while (valid < (bits)) bitbuf = (bitbuf<<8) | (ulg)get_byte(), valid += 8; \
+  while (valid < (bits)) bitbuf = (bitbuf<<8) | read_byte(), valid += 8; \
   code = (bitbuf >> (valid-(bits))) & (mask); \
 }
 
@@ -109,17 +119,19 @@ local void read_tree()
 
     /* Read the original input size, MSB first */
     orig_len = 0;
-    for (n = 1; n <= 4; n++) orig_len = (orig_len << 8) | (ulg)get_byte();
+    for (n = 1; n <= 4; n++)
+      orig_len = (orig_len << 8) | read_byte ();
 
-    max_len = (int)get_byte(); /* maximum bit length of Huffman codes */
-    if (max_len > MAX_BITLEN) {
-        gzip_error ("invalid compressed data -- Huffman code > 32 bits");
-    }
+    /* Read the maximum bit length of Huffman codes.  */
+    max_len = read_byte ();
+    if (! (0 < max_len && max_len <= MAX_BITLEN))
+      gzip_error ("invalid compressed data -- "
+                  "Huffman code bit length out of range");
 
     /* Get the number of leaves at each bit length */
     n = 0;
     for (len = 1; len <= max_len; len++) {
-        leaves[len] = (int)get_byte();
+        leaves[len] = read_byte ();
         if (max_leaves - (len == max_len) < leaves[len])
           gzip_error ("too many leaves in Huffman tree");
         max_leaves = (max_leaves - leaves[len] + 1) * 2 - 1;
@@ -146,7 +158,7 @@ local void read_tree()
         lit_base[len] = base;
         /* And read the literals: */
         for (n = leaves[len]; n > 0; n--) {
-            literal[base++] = (uch)get_byte();
+            literal[base++] = read_byte ();
         }
     }
     leaves[max_len]++; /* Now include the EOB code in the Huffman tree */