From: Mark Adler Date: Sun, 6 Nov 2016 05:55:34 +0000 (-0700) Subject: Fix bugs in creating a very large gzip header. X-Git-Tag: 1.9.9-b1~727 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=344448d44353b8d153eb2aa5db2fb3e49bb47a57;p=thirdparty%2Fzlib-ng.git Fix bugs in creating a very large gzip header. --- diff --git a/deflate.c b/deflate.c index 4c0e55a48..123cb42aa 100644 --- a/deflate.c +++ b/deflate.c @@ -281,6 +281,9 @@ static int deflateStateCheck (z_stream *strm) { return 1; s = strm->state; if (s == NULL || s->strm != strm || (s->status != INIT_STATE && +#ifdef GZIP + s->status != GZIP_STATE && +#endif s->status != EXTRA_STATE && s->status != NAME_STATE && s->status != COMMENT_STATE && @@ -368,7 +371,12 @@ int ZEXPORT deflateResetKeep(z_stream *strm) { if (s->wrap < 0) { s->wrap = -s->wrap; /* was made negative by deflate(..., Z_FINISH); */ } - s->status = s->wrap ? INIT_STATE : BUSY_STATE; + s->status = +#ifdef GZIP + s->wrap == 2 ? GZIP_STATE : +#endif + s->wrap ? INIT_STATE : BUSY_STATE; + #ifdef GZIP strm->adler = s->wrap == 2 ? crc32(0L, NULL, 0) : adler32(0L, NULL, 0); #else @@ -501,7 +509,6 @@ int ZEXPORT deflateTune(z_stream *strm, int good_length, int max_lazy, int nice_ unsigned long ZEXPORT deflateBound(z_stream *strm, unsigned long sourceLen) { deflate_state *s; unsigned long complen, wraplen; - unsigned char *str; /* conservative upper bound for compressed data */ complen = sourceLen + ((sourceLen + 7) >> 3) + ((sourceLen + 63) >> 6) + 5; @@ -519,9 +526,11 @@ unsigned long ZEXPORT deflateBound(z_stream *strm, unsigned long sourceLen) { case 1: /* zlib wrapper */ wraplen = 6 + (s->strstart ? 4 : 0); break; +#ifdef GZIP case 2: /* gzip wrapper */ wraplen = 18; if (s->gzhead != NULL) { /* user-supplied gzip header */ + unsigned char *str; if (s->gzhead->extra != NULL) { wraplen += 2 + s->gzhead->extra_len; } @@ -541,6 +550,7 @@ unsigned long ZEXPORT deflateBound(z_stream *strm, unsigned long sourceLen) { wraplen += 2; } break; +#endif default: /* for compiler happiness */ wraplen = 6; } @@ -584,13 +594,22 @@ ZLIB_INTERNAL void flush_pending(z_stream *strm) { strm->next_out += len; s->pending_out += len; strm->total_out += len; - strm->avail_out -= len; - s->pending -= len; + strm->avail_out -= len; + s->pending -= len; if (s->pending == 0) { s->pending_out = s->pending_buf; } } +/* =========================================================================== + * Update the header CRC with the bytes s->pending_buf[beg..s->pending - 1]. + */ +#define HCRC_UPDATE(beg) \ + do { \ + if (s->gzhead->hcrc && s->pending > (beg)) \ + strm->adler = crc32(strm->adler, s->pending_buf + (beg), s->pending - (beg)); \ + } while (0) + /* ========================================================================= */ int ZEXPORT deflate(z_stream *strm, int flush) { int old_flush; /* value of flush param for previous deflate call */ @@ -608,137 +627,169 @@ int ZEXPORT deflate(z_stream *strm, int flush) { if (strm->avail_out == 0) ERR_RETURN(strm, Z_BUF_ERROR); - s->strm = strm; /* just in case */ old_flush = s->last_flush; s->last_flush = flush; + /* Flush as much pending output as possible */ + if (s->pending != 0) { + flush_pending(strm); + if (strm->avail_out == 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s->last_flush = -1; + return Z_OK; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && + flush != Z_FINISH) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + + /* User must not provide more input after the first FINISH: */ + if (s->status == FINISH_STATE && strm->avail_in != 0) { + ERR_RETURN(strm, Z_BUF_ERROR); + } + /* Write the header */ if (s->status == INIT_STATE) { -#ifdef GZIP - if (s->wrap == 2) { - crc_reset(s); - put_byte(s, 31); - put_byte(s, 139); - put_byte(s, 8); - if (s->gzhead == NULL) { - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, 0); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, OS_CODE); - s->status = BUSY_STATE; - } else { - put_byte(s, (s->gzhead->text ? 1 : 0) + - (s->gzhead->hcrc ? 2 : 0) + - (s->gzhead->extra == NULL ? 0 : 4) + - (s->gzhead->name == NULL ? 0 : 8) + - (s->gzhead->comment == NULL ? 0 : 16) ); - put_byte(s, (unsigned char)(s->gzhead->time & 0xff)); - put_byte(s, (unsigned char)((s->gzhead->time >> 8) & 0xff)); - put_byte(s, (unsigned char)((s->gzhead->time >> 16) & 0xff)); - put_byte(s, (unsigned char)((s->gzhead->time >> 24) & 0xff)); - put_byte(s, s->level == 9 ? 2 : - (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? - 4 : 0)); - put_byte(s, s->gzhead->os & 0xff); - if (s->gzhead->extra != NULL) { - put_byte(s, s->gzhead->extra_len & 0xff); - put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); - } - if (s->gzhead->hcrc) - strm->adler = crc32(strm->adler, s->pending_buf, s->pending); - s->gzindex = 0; - s->status = EXTRA_STATE; - } - } else -#endif - { - unsigned int header = (Z_DEFLATED + ((s->w_bits-8) << 4)) << 8; - unsigned int level_flags; - - if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) - level_flags = 0; - else if (s->level < TRIGGER_LEVEL) - level_flags = 1; - else if (s->level == TRIGGER_LEVEL) - level_flags = 2; - else - level_flags = 3; - header |= (level_flags << 6); - if (s->strstart != 0) - header |= PRESET_DICT; - header += 31 - (header % 31); + /* zlib header */ + unsigned int header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8; + unsigned int level_flags; + + if (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2) + level_flags = 0; + else if (s->level < 6) + level_flags = 1; + else if (s->level == 6) + level_flags = 2; + else + level_flags = 3; + header |= (level_flags << 6); + if (s->strstart != 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + putShortMSB(s, header); + /* Save the adler32 of the preset dictionary: */ + if (s->strstart != 0) { + putShortMSB(s, (uint16_t)(strm->adler >> 16)); + putShortMSB(s, (uint16_t)(strm->adler)); + } + strm->adler = adler32(0L, NULL, 0); + s->status = BUSY_STATE; + + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } +#ifdef GZIP + if (s->status == GZIP_STATE) { + /* gzip header */ + strm->adler = crc32(0L, NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (s->gzhead == NULL) { + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); + put_byte(s, OS_CODE); s->status = BUSY_STATE; - putShortMSB(s, header); - /* Save the adler32 of the preset dictionary: */ - if (s->strstart != 0) { - putShortMSB(s, (uint16_t)(strm->adler >> 16)); - putShortMSB(s, (uint16_t)strm->adler); + /* Compression must start with an empty pending buffer */ + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } + } + else { + put_byte(s, (s->gzhead->text ? 1 : 0) + + (s->gzhead->hcrc ? 2 : 0) + + (s->gzhead->extra == NULL ? 0 : 4) + + (s->gzhead->name == NULL ? 0 : 8) + + (s->gzhead->comment == NULL ? 0 : 16) + ); + put_byte(s, (unsigned char)(s->gzhead->time & 0xff)); + put_byte(s, (unsigned char)((s->gzhead->time >> 8) & 0xff)); + put_byte(s, (unsigned char)((s->gzhead->time >> 16) & 0xff)); + put_byte(s, (unsigned char)((s->gzhead->time >> 24) & 0xff)); + put_byte(s, s->level == 9 ? 2 : + (s->strategy >= Z_HUFFMAN_ONLY || s->level < 2 ? 4 : 0)); + put_byte(s, s->gzhead->os & 0xff); + if (s->gzhead->extra != NULL) { + put_byte(s, s->gzhead->extra_len & 0xff); + put_byte(s, (s->gzhead->extra_len >> 8) & 0xff); } - strm->adler = adler32(0L, NULL, 0); + if (s->gzhead->hcrc) + strm->adler = crc32(strm->adler, s->pending_buf, s->pending); + s->gzindex = 0; + s->status = EXTRA_STATE; } } -#ifdef GZIP if (s->status == EXTRA_STATE) { - if (s->gzhead->extra != NULL) { - uint32_t beg = s->pending; /* start of bytes to update crc */ - - while (s->gzindex < (s->gzhead->extra_len & 0xffff)) { - if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); - flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) - break; + if (s->gzhead->extra != Z_NULL) { + uint32_t beg = s->pending; /* start of bytes to update crc */ + uint32_t left = (s->gzhead->extra_len & 0xffff) - s->gzindex; + + while (s->pending + left > s->pending_buf_size) { + uint32_t copy = s->pending_buf_size - s->pending; + memcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, copy); + s->pending = s->pending_buf_size; + HCRC_UPDATE(beg); + s->gzindex += copy; + flush_pending(strm); + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; } - put_byte(s, s->gzhead->extra[s->gzindex]); - s->gzindex++; - } - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); - if (s->gzindex == s->gzhead->extra_len) { - s->gzindex = 0; - s->status = NAME_STATE; + beg = 0; + left -= copy; } - } else { - s->status = NAME_STATE; + memcpy(s->pending_buf + s->pending, s->gzhead->extra + s->gzindex, left); + s->pending += left; + HCRC_UPDATE(beg); + s->gzindex = 0; } + s->status = NAME_STATE; } if (s->status == NAME_STATE) { if (s->gzhead->name != NULL) { - uint32_t beg = s->pending; /* start of bytes to update crc */ + uint32_t beg = s->pending; /* start of bytes to update crc */ int val; do { if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); + HCRC_UPDATE(beg); flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; } + beg = 0; } val = s->gzhead->name[s->gzindex++]; put_byte(s, val); } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); - if (val == 0) { - s->gzindex = 0; - s->status = COMMENT_STATE; - } - } else { - s->status = COMMENT_STATE; + HCRC_UPDATE(beg); + s->gzindex = 0; } + s->status = COMMENT_STATE; } if (s->status == COMMENT_STATE) { if (s->gzhead->comment != NULL) { @@ -747,68 +798,44 @@ int ZEXPORT deflate(z_stream *strm, int flush) { do { if (s->pending == s->pending_buf_size) { - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); + HCRC_UPDATE(beg); flush_pending(strm); - beg = s->pending; - if (s->pending == s->pending_buf_size) { - val = 1; - break; + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; } + beg = 0; } val = s->gzhead->comment[s->gzindex++]; put_byte(s, val); } while (val != 0); - if (s->gzhead->hcrc && s->pending > beg) - strm->adler = crc32(strm->adler, s->pending_buf + beg, s->pending - beg); - if (val == 0) - s->status = HCRC_STATE; - } else { - s->status = HCRC_STATE; + HCRC_UPDATE(beg); } + s->status = HCRC_STATE; } if (s->status == HCRC_STATE) { if (s->gzhead->hcrc) { - if (s->pending + 2 > s->pending_buf_size) + if (s->pending + 2 > s->pending_buf_size) { flush_pending(strm); - if (s->pending + 2 <= s->pending_buf_size) { - put_byte(s, (unsigned char)(strm->adler & 0xff)); - put_byte(s, (unsigned char)((strm->adler >> 8) & 0xff)); - strm->adler = crc32(0L, NULL, 0); - s->status = BUSY_STATE; + if (s->pending != 0) { + s->last_flush = -1; + return Z_OK; + } } - } else { - s->status = BUSY_STATE; + put_byte(s, (unsigned char)(strm->adler & 0xff)); + put_byte(s, (unsigned char)((strm->adler >> 8) & 0xff)); + strm->adler = crc32(0L, NULL, 0); } - } -#endif + s->status = BUSY_STATE; - /* Flush as much pending output as possible */ - if (s->pending != 0) { + /* Compression must start with an empty pending buffer */ flush_pending(strm); - if (strm->avail_out == 0) { - /* Since avail_out is 0, deflate will be called again with - * more output space, but possibly with both pending and - * avail_in equal to zero. There won't be anything to do, - * but this is not an error situation so make sure we - * return OK instead of BUF_ERROR at next call of deflate: - */ + if (s->pending != 0) { s->last_flush = -1; return Z_OK; } - - /* Make sure there is something to do and avoid duplicate consecutive - * flushes. For repeated and useless calls with Z_FINISH, we keep - * returning Z_STREAM_END instead of Z_BUF_ERROR. - */ - } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { - ERR_RETURN(strm, Z_BUF_ERROR); - } - - /* User must not provide more input after the first FINISH: */ - if (s->status == FINISH_STATE && strm->avail_in != 0) { - ERR_RETURN(strm, Z_BUF_ERROR); } +#endif /* Start a new block or continue the current one. */ diff --git a/deflate.h b/deflate.h index 5d122d803..8f96203b4 100644 --- a/deflate.h +++ b/deflate.h @@ -57,13 +57,16 @@ #define END_BLOCK 256 /* end of block literal code */ -#define INIT_STATE 42 -#define EXTRA_STATE 69 -#define NAME_STATE 73 -#define COMMENT_STATE 91 -#define HCRC_STATE 103 -#define BUSY_STATE 113 -#define FINISH_STATE 666 +#define INIT_STATE 42 /* zlib header -> BUSY_STATE */ +#ifdef GZIP +# define GZIP_STATE 57 /* gzip header -> BUSY_STATE | EXTRA_STATE */ +#endif +#define EXTRA_STATE 69 /* gzip extra block -> NAME_STATE */ +#define NAME_STATE 73 /* gzip file name -> COMMENT_STATE */ +#define COMMENT_STATE 91 /* gzip comment -> HCRC_STATE */ +#define HCRC_STATE 103 /* gzip header CRC -> BUSY_STATE */ +#define BUSY_STATE 113 /* deflate -> FINISH_STATE */ +#define FINISH_STATE 666 /* stream complete */ /* Stream status */ @@ -105,10 +108,10 @@ typedef struct internal_state { unsigned char *pending_buf; /* output still pending */ unsigned long pending_buf_size; /* size of pending_buf */ unsigned char *pending_out; /* next pending byte to output to the stream */ - unsigned int pending; /* nb of bytes in the pending buffer */ + uint32_t pending; /* nb of bytes in the pending buffer */ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ - unsigned int gzindex; /* where in extra, name, or comment */ + uint32_t gzindex; /* where in extra, name, or comment */ unsigned char method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */