Fix bugs in creating a very large gzip header.

9674807c82
This commit is contained in:
Alex Kocharin 2022-06-09 19:36:02 +03:00
parent 1154023108
commit bee5019c40
2 changed files with 241 additions and 215 deletions

View file

@ -67,13 +67,16 @@ const MIN_LOOKAHEAD = (MAX_MATCH + MIN_MATCH + 1);
const PRESET_DICT = 0x20;
const INIT_STATE = 42;
const EXTRA_STATE = 69;
const NAME_STATE = 73;
const COMMENT_STATE = 91;
const HCRC_STATE = 103;
const BUSY_STATE = 113;
const FINISH_STATE = 666;
const INIT_STATE = 42; /* zlib header -> BUSY_STATE */
//#ifdef GZIP
const GZIP_STATE = 57; /* gzip header -> BUSY_STATE | EXTRA_STATE */
//#endif
const EXTRA_STATE = 69; /* gzip extra block -> NAME_STATE */
const NAME_STATE = 73; /* gzip file name -> COMMENT_STATE */
const COMMENT_STATE = 91; /* gzip comment -> HCRC_STATE */
const HCRC_STATE = 103; /* gzip header CRC -> BUSY_STATE */
const BUSY_STATE = 113; /* deflate -> FINISH_STATE */
const FINISH_STATE = 666; /* stream complete */
const BS_NEED_MORE = 1; /* block not completed, need more input or more output */
const BS_BLOCK_DONE = 2; /* block flush performed */
@ -120,11 +123,11 @@ const flush_pending = (strm) => {
if (len === 0) { return; }
strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);
strm.next_out += len;
s.pending_out += len;
strm.next_out += len;
s.pending_out += len;
strm.total_out += len;
strm.avail_out -= len;
s.pending -= len;
s.pending -= len;
if (s.pending === 0) {
s.pending_out = 0;
}
@ -1268,6 +1271,9 @@ const deflateStateCheck = (strm) => {
}
const s = strm.state;
if (!s || 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 &&
@ -1297,7 +1303,11 @@ const deflateResetKeep = (strm) => {
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 = (s.wrap === 2) ?
0 // crc32(0, Z_NULL, 0)
:
@ -1414,10 +1424,9 @@ const deflateInit = (strm, level) => {
};
/* ========================================================================= */
const deflate = (strm, flush) => {
let beg, val; // for gzip header write only
if (deflateStateCheck(strm) || flush > Z_BLOCK || flush < 0) {
return strm ? err(strm, Z_STREAM_ERROR) : Z_STREAM_ERROR;
}
@ -1430,210 +1439,9 @@ const deflate = (strm, flush) => {
return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR : Z_STREAM_ERROR);
}
s.strm = strm; /* just in case */
const old_flush = s.last_flush;
s.last_flush = flush;
/* Write the header */
if (s.status === INIT_STATE) {
if (s.wrap === 2) { // GZIP header
strm.adler = 0; //crc32(0L, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (!s.gzhead) { // 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 ? 0 : 4) +
(!s.gzhead.name ? 0 : 8) +
(!s.gzhead.comment ? 0 : 16)
);
put_byte(s, s.gzhead.time & 0xff);
put_byte(s, (s.gzhead.time >> 8) & 0xff);
put_byte(s, (s.gzhead.time >> 16) & 0xff);
put_byte(s, (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 && s.gzhead.extra.length) {
put_byte(s, s.gzhead.extra.length & 0xff);
put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
}
if (s.gzhead.hcrc) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
}
s.gzindex = 0;
s.status = EXTRA_STATE;
}
}
else // DEFLATE header
{
let header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
let level_flags = -1;
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, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
strm.adler = 1; // adler32(0L, Z_NULL, 0);
}
}
//#ifdef GZIP
if (s.status === EXTRA_STATE) {
if (s.gzhead.extra/* != Z_NULL*/) {
beg = s.pending; /* start of bytes to update crc */
while (s.gzindex < (s.gzhead.extra.length & 0xffff)) {
if (s.pending === s.pending_buf_size) {
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
break;
}
}
put_byte(s, s.gzhead.extra[s.gzindex] & 0xff);
s.gzindex++;
}
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
if (s.gzindex === s.gzhead.extra.length) {
s.gzindex = 0;
s.status = NAME_STATE;
}
}
else {
s.status = NAME_STATE;
}
}
if (s.status === NAME_STATE) {
if (s.gzhead.name/* != Z_NULL*/) {
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, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
val = 1;
break;
}
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.name.length) {
val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, 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*/) {
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, s.pending - beg, beg);
}
flush_pending(strm);
beg = s.pending;
if (s.pending === s.pending_buf_size) {
val = 1;
break;
}
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.comment.length) {
val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, 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, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
strm.adler = 0; //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);
@ -1662,6 +1470,224 @@ const deflate = (strm, flush) => {
return err(strm, Z_BUF_ERROR);
}
/* Write the header */
if (s.status === INIT_STATE) {
/* zlib header */
let header = (Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
let level_flags = -1;
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, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
strm.adler = 1; // 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 = 0; //crc32(0L, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (!s.gzhead) { // 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 ? 0 : 4) +
(!s.gzhead.name ? 0 : 8) +
(!s.gzhead.comment ? 0 : 16)
);
put_byte(s, s.gzhead.time & 0xff);
put_byte(s, (s.gzhead.time >> 8) & 0xff);
put_byte(s, (s.gzhead.time >> 16) & 0xff);
put_byte(s, (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 && s.gzhead.extra.length) {
put_byte(s, s.gzhead.extra.length & 0xff);
put_byte(s, (s.gzhead.extra.length >> 8) & 0xff);
}
if (s.gzhead.hcrc) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending, 0);
}
s.gzindex = 0;
s.status = EXTRA_STATE;
}
}
if (s.status === EXTRA_STATE) {
if (s.gzhead.extra/* != Z_NULL*/) {
let beg = s.pending; /* start of bytes to update crc */
let left = (s.gzhead.extra.length & 0xffff) - s.gzindex;
while (s.pending + left > s.pending_buf_size) {
let copy = s.pending_buf_size - s.pending;
// zmemcpy(s.pending_buf + s.pending,
// s.gzhead.extra + s.gzindex, copy);
s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy), s.pending);
s.pending = s.pending_buf_size;
//--- HCRC_UPDATE(beg) ---//
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, 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_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + left), s.pending);
s.pending += left;
//--- HCRC_UPDATE(beg) ---//
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
//---//
s.gzindex = 0;
}
s.status = NAME_STATE;
}
if (s.status === NAME_STATE) {
if (s.gzhead.name/* != Z_NULL*/) {
let beg = s.pending; /* start of bytes to update crc */
let val;
do {
if (s.pending === s.pending_buf_size) {
//--- HCRC_UPDATE(beg) ---//
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
//---//
flush_pending(strm);
if (s.pending !== 0) {
s.last_flush = -1;
return Z_OK;
}
beg = 0;
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.name.length) {
val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
//--- HCRC_UPDATE(beg) ---//
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
//---//
s.gzindex = 0;
}
s.status = COMMENT_STATE;
}
if (s.status === COMMENT_STATE) {
if (s.gzhead.comment/* != Z_NULL*/) {
let beg = s.pending; /* start of bytes to update crc */
let val;
do {
if (s.pending === s.pending_buf_size) {
//--- HCRC_UPDATE(beg) ---//
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, beg);
}
//---//
flush_pending(strm);
if (s.pending !== 0) {
s.last_flush = -1;
return Z_OK;
}
beg = 0;
}
// JS specific: little magic to add zero terminator to end of string
if (s.gzindex < s.gzhead.comment.length) {
val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
} else {
val = 0;
}
put_byte(s, val);
} while (val !== 0);
//--- HCRC_UPDATE(beg) ---//
if (s.gzhead.hcrc && s.pending > beg) {
strm.adler = crc32(strm.adler, s.pending_buf, s.pending - beg, 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, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
strm.adler = 0; //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

@ -38,7 +38,7 @@ describe('Gzip special cases', () => {
os: 15,
name: 'test name',
comment: 'test comment',
extra: [ 4, 5, 6 ]
extra: new Uint8Array([ 4, 5, 6 ])
}
});
deflator.push(data, true);