zlib: Fix handling of gzip magic bytes mid-file

Only treat the gzip magic bytes, when encountered within the file
after reading a single block, as the start of a new member when
the previous member has ended.

Add test files that reliably reproduce #5852. The gzipped file
in test/fixtures/pseudo-multimember-gzip.gz contains the gzip
magic bytes exactly at the position that node encounters after having
read a single block, leading it to believe that a new data
member is starting.

Fixes: https://github.com/nodejs/node/issues/5852
PR-URL: https://github.com/nodejs/node/pull/5863
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This commit is contained in:
Anna Henningsen 2016-03-23 11:45:38 +01:00 committed by Jeremiah Senkpiel
parent 7d73e60f60
commit 0b3936b49f
4 changed files with 24 additions and 1 deletions

View File

@ -258,7 +258,8 @@ class ZCtx : public AsyncWrap {
} }
} }
while (ctx->strm_.avail_in >= GZIP_MIN_HEADER_SIZE && while (ctx->strm_.avail_in >= GZIP_MIN_HEADER_SIZE &&
ctx->mode_ == GUNZIP) { ctx->mode_ == GUNZIP &&
ctx->err_ == Z_STREAM_END) {
// Bytes remain in input buffer. Perhaps this is another compressed // Bytes remain in input buffer. Perhaps this is another compressed
// member in the same archive, or just trailing garbage. // member in the same archive, or just trailing garbage.
// Check the header to find out. // Check the header to find out.

BIN
test/fixtures/pseudo-multimember-gzip.gz vendored Normal file

Binary file not shown.

BIN
test/fixtures/pseudo-multimember-gzip.z vendored Normal file

Binary file not shown.

View File

@ -4,6 +4,8 @@
const common = require('../common'); const common = require('../common');
const assert = require('assert'); const assert = require('assert');
const zlib = require('zlib'); const zlib = require('zlib');
const path = require('path');
const fs = require('fs');
const data = Buffer.concat([ const data = Buffer.concat([
zlib.gzipSync('abc'), zlib.gzipSync('abc'),
@ -16,3 +18,23 @@ zlib.gunzip(data, common.mustCall((err, result) => {
assert.ifError(err); assert.ifError(err);
assert.equal(result, 'abcdef', 'result should match original string'); assert.equal(result, 'abcdef', 'result should match original string');
})); }));
// files that have the "right" magic bytes for starting a new gzip member
// in the middle of themselves, even if they are part of a single
// regularly compressed member
const pmmFileZlib = path.join(common.fixturesDir, 'pseudo-multimember-gzip.z');
const pmmFileGz = path.join(common.fixturesDir, 'pseudo-multimember-gzip.gz');
const pmmExpected = zlib.inflateSync(fs.readFileSync(pmmFileZlib));
const pmmResultBuffers = [];
fs.createReadStream(pmmFileGz)
.pipe(zlib.createGunzip())
.on('error', (err) => {
assert.ifError(err);
})
.on('data', (data) => pmmResultBuffers.push(data))
.on('finish', common.mustCall(() => {
assert.deepStrictEqual(Buffer.concat(pmmResultBuffers), pmmExpected,
'result should match original random garbage');
}));