Fix bugs in creating a very large gzip header.

This commit is contained in:
Mark Adler 2016-11-05 22:55:34 -07:00
parent fc130cdd9f
commit 9674807c82
2 changed files with 216 additions and 194 deletions

389
deflate.c
View File

@ -326,6 +326,9 @@ local int deflateStateCheck (strm)
return 1;
s = strm->state;
if (s == Z_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 &&
@ -426,7 +429,11 @@ int ZEXPORT deflateResetKeep (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;
strm->adler =
#ifdef GZIP
s->wrap == 2 ? crc32(0L, Z_NULL, 0) :
@ -584,7 +591,6 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
{
deflate_state *s;
uLong complen, wraplen;
Bytef *str;
/* conservative upper bound for compressed data */
complen = sourceLen +
@ -603,9 +609,11 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
case 1: /* zlib wrapper */
wraplen = 6 + (s->strstart ? 4 : 0);
break;
#ifdef GZIP
case 2: /* gzip wrapper */
wraplen = 18;
if (s->gzhead != Z_NULL) { /* user-supplied gzip header */
Bytef *str;
if (s->gzhead->extra != Z_NULL)
wraplen += 2 + s->gzhead->extra_len;
str = s->gzhead->name;
@ -622,6 +630,7 @@ uLong ZEXPORT deflateBound(strm, sourceLen)
wraplen += 2;
}
break;
#endif
default: /* for compiler happiness */
wraplen = 6;
}
@ -669,13 +678,23 @@ local void flush_pending(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 (strm, flush)
z_streamp strm;
@ -696,190 +715,9 @@ int ZEXPORT deflate (strm, 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;
/* Write the header */
if (s->status == INIT_STATE) {
#ifdef GZIP
if (s->wrap == 2) {
strm->adler = crc32(0L, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (s->gzhead == Z_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 == Z_NULL ? 0 : 4) +
(s->gzhead->name == Z_NULL ? 0 : 8) +
(s->gzhead->comment == Z_NULL ? 0 : 16)
);
put_byte(s, (Byte)(s->gzhead->time & 0xff));
put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
put_byte(s, (Byte)((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 != Z_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
{
uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
uInt 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);
s->status = BUSY_STATE;
putShortMSB(s, header);
/* Save the adler32 of the preset dictionary: */
if (s->strstart != 0) {
putShortMSB(s, (uInt)(strm->adler >> 16));
putShortMSB(s, (uInt)(strm->adler & 0xffff));
}
strm->adler = adler32(0L, Z_NULL, 0);
}
}
#ifdef GZIP
if (s->status == EXTRA_STATE) {
if (s->gzhead->extra != Z_NULL) {
uInt 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;
}
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;
}
}
else
s->status = NAME_STATE;
}
if (s->status == NAME_STATE) {
if (s->gzhead->name != Z_NULL) {
uInt 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);
flush_pending(strm);
beg = s->pending;
if (s->pending == s->pending_buf_size) {
val = 1;
break;
}
}
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;
}
if (s->status == COMMENT_STATE) {
if (s->gzhead->comment != Z_NULL) {
uInt 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);
flush_pending(strm);
beg = s->pending;
if (s->pending == s->pending_buf_size) {
val = 1;
break;
}
}
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;
}
if (s->status == HCRC_STATE) {
if (s->gzhead->hcrc) {
if (s->pending + 2 > s->pending_buf_size)
flush_pending(strm);
if (s->pending + 2 <= s->pending_buf_size) {
put_byte(s, (Byte)(strm->adler & 0xff));
put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
strm->adler = crc32(0L, Z_NULL, 0);
s->status = BUSY_STATE;
}
}
else
s->status = BUSY_STATE;
}
#endif
/* Flush as much pending output as possible */
if (s->pending != 0) {
flush_pending(strm);
@ -908,6 +746,187 @@ int ZEXPORT deflate (strm, flush)
ERR_RETURN(strm, Z_BUF_ERROR);
}
/* Write the header */
if (s->status == INIT_STATE) {
/* zlib header */
uInt header = (Z_DEFLATED + ((s->w_bits-8)<<4)) << 8;
uInt 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, (uInt)(strm->adler >> 16));
putShortMSB(s, (uInt)(strm->adler & 0xffff));
}
strm->adler = adler32(0L, Z_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, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (s->gzhead == Z_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;
/* 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 == Z_NULL ? 0 : 4) +
(s->gzhead->name == Z_NULL ? 0 : 8) +
(s->gzhead->comment == Z_NULL ? 0 : 16)
);
put_byte(s, (Byte)(s->gzhead->time & 0xff));
put_byte(s, (Byte)((s->gzhead->time >> 8) & 0xff));
put_byte(s, (Byte)((s->gzhead->time >> 16) & 0xff));
put_byte(s, (Byte)((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 != Z_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;
}
}
if (s->status == EXTRA_STATE) {
if (s->gzhead->extra != Z_NULL) {
ulg beg = s->pending; /* start of bytes to update crc */
uInt left = (s->gzhead->extra_len & 0xffff) - s->gzindex;
while (s->pending + left > s->pending_buf_size) {
uInt copy = s->pending_buf_size - s->pending;
zmemcpy(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;
}
beg = 0;
left -= copy;
}
zmemcpy(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 != Z_NULL) {
ulg beg = s->pending; /* start of bytes to update crc */
int val;
do {
if (s->pending == s->pending_buf_size) {
HCRC_UPDATE(beg);
flush_pending(strm);
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);
HCRC_UPDATE(beg);
s->gzindex = 0;
}
s->status = COMMENT_STATE;
}
if (s->status == COMMENT_STATE) {
if (s->gzhead->comment != Z_NULL) {
ulg beg = s->pending; /* start of bytes to update crc */
int val;
do {
if (s->pending == s->pending_buf_size) {
HCRC_UPDATE(beg);
flush_pending(strm);
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);
HCRC_UPDATE(beg);
}
s->status = HCRC_STATE;
}
if (s->status == HCRC_STATE) {
if (s->gzhead->hcrc) {
if (s->pending + 2 > s->pending_buf_size) {
flush_pending(strm);
if (s->pending != 0) {
s->last_flush = -1;
return Z_OK;
}
}
put_byte(s, (Byte)(strm->adler & 0xff));
put_byte(s, (Byte)((strm->adler >> 8) & 0xff));
strm->adler = crc32(0L, Z_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;
}
}
#endif
/* Start a new block or continue the current one.
*/
if (strm->avail_in != 0 || s->lookahead != 0 ||

View File

@ -51,13 +51,16 @@
#define Buf_size 16
/* size of bit buffer in bi_buf */
#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 */
@ -100,10 +103,10 @@ typedef struct internal_state {
Bytef *pending_buf; /* output still pending */
ulg pending_buf_size; /* size of pending_buf */
Bytef *pending_out; /* next pending byte to output to the stream */
uInt pending; /* nb of bytes in the pending buffer */
ulg 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 */
uInt gzindex; /* where in extra, name, or comment */
ulg gzindex; /* where in extra, name, or comment */
Byte method; /* can only be DEFLATED */
int last_flush; /* value of flush param for previous deflate call */