From f2be148c3d956c2dd19bd6fdbe6de977e86cba35 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 8 Dec 2012 10:45:18 -0800 Subject: [PATCH] diagnose unexpected EOF and zero lengths in packed data 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 | 3 +++ tests/Makefile.am | 1 + tests/unpack-invalid | 36 ++++++++++++++++++++++++++++++++++++ unpack.c | 28 ++++++++++++++++++++-------- 4 files changed, 60 insertions(+), 8 deletions(-) create mode 100755 tests/unpack-invalid diff --git a/NEWS b/NEWS index 79c1110..d184346 100644 --- 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] diff --git a/tests/Makefile.am b/tests/Makefile.am index ddab493..6578682 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 0000000..eb862df --- /dev/null +++ b/tests/unpack-invalid @@ -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 . +# 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 /tmp/out 2>/tmp/err; then + fail=1 + else + fail=0 + fi +done + +Exit $fail diff --git a/unpack.c b/unpack.c index 44a232f..8189aad 100644 --- 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 */ -- 2.47.2