zlib: decompression throw on truncated input
Check for unexpected end-of-file error when decompressing. If the output buffer still has space after decompressing and deflate returned Z_OK or Z_BUF_ERROR - that means unexpected end-of-file. Added test-zlib-truncated.js for the case of truncated input. Fixed the zlib dictionary test to not end the inflate stream on a truncated output (no crc) of deflate Fixes: https://github.com/nodejs/node/issues/2043 PR-URL: https://github.com/nodejs/node/pull/2595 Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This commit is contained in:
parent
8b4adb267b
commit
0fc0902c21
@ -271,8 +271,12 @@ class ZCtx : public AsyncWrap {
|
|||||||
// Acceptable error states depend on the type of zlib stream.
|
// Acceptable error states depend on the type of zlib stream.
|
||||||
switch (ctx->err_) {
|
switch (ctx->err_) {
|
||||||
case Z_OK:
|
case Z_OK:
|
||||||
case Z_STREAM_END:
|
|
||||||
case Z_BUF_ERROR:
|
case Z_BUF_ERROR:
|
||||||
|
if (ctx->strm_.avail_out != 0 && ctx->flush_ == Z_FINISH) {
|
||||||
|
ZCtx::Error(ctx, "unexpected end of file");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
case Z_STREAM_END:
|
||||||
// normal statuses, not fatal
|
// normal statuses, not fatal
|
||||||
break;
|
break;
|
||||||
case Z_NEED_DICT:
|
case Z_NEED_DICT:
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
// test compression/decompression with dictionary
|
// test compression/decompression with dictionary
|
||||||
|
|
||||||
var common = require('../common');
|
const common = require('../common');
|
||||||
var assert = require('assert');
|
const assert = require('assert');
|
||||||
var zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
var spdyDict = new Buffer([
|
const spdyDict = new Buffer([
|
||||||
'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-',
|
'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-',
|
||||||
'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi',
|
'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi',
|
||||||
'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser',
|
'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser',
|
||||||
@ -22,54 +22,69 @@ var spdyDict = new Buffer([
|
|||||||
'.1statusversionurl\0'
|
'.1statusversionurl\0'
|
||||||
].join(''));
|
].join(''));
|
||||||
|
|
||||||
var deflate = zlib.createDeflate({ dictionary: spdyDict });
|
const input = [
|
||||||
|
|
||||||
var input = [
|
|
||||||
'HTTP/1.1 200 Ok',
|
'HTTP/1.1 200 Ok',
|
||||||
'Server: node.js',
|
'Server: node.js',
|
||||||
'Content-Length: 0',
|
'Content-Length: 0',
|
||||||
''
|
''
|
||||||
].join('\r\n');
|
].join('\r\n');
|
||||||
|
|
||||||
var called = 0;
|
function basicDictionaryTest() {
|
||||||
|
let output = '';
|
||||||
|
const deflate = zlib.createDeflate({ dictionary: spdyDict });
|
||||||
|
const inflate = zlib.createInflate({ dictionary: spdyDict });
|
||||||
|
|
||||||
//
|
|
||||||
// We'll use clean-new inflate stream each time
|
|
||||||
// and .reset() old dirty deflate one
|
|
||||||
//
|
|
||||||
function run(num) {
|
|
||||||
var inflate = zlib.createInflate({ dictionary: spdyDict });
|
|
||||||
|
|
||||||
if (num === 2) {
|
|
||||||
deflate.reset();
|
|
||||||
deflate.removeAllListeners('data');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put data into deflate stream
|
|
||||||
deflate.on('data', function(chunk) {
|
deflate.on('data', function(chunk) {
|
||||||
inflate.write(chunk);
|
inflate.write(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Get data from inflate stream
|
|
||||||
var output = [];
|
|
||||||
inflate.on('data', function(chunk) {
|
inflate.on('data', function(chunk) {
|
||||||
output.push(chunk);
|
output += chunk;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
deflate.on('end', function() {
|
||||||
|
inflate.end();
|
||||||
|
});
|
||||||
|
|
||||||
inflate.on('end', function() {
|
inflate.on('end', function() {
|
||||||
called++;
|
assert.equal(input, output);
|
||||||
|
});
|
||||||
|
|
||||||
assert.equal(output.join(''), input);
|
deflate.write(input);
|
||||||
|
deflate.end();
|
||||||
|
}
|
||||||
|
|
||||||
if (num < 2) run(num + 1);
|
function deflateResetDictionaryTest() {
|
||||||
|
let doneReset = false;
|
||||||
|
let output = '';
|
||||||
|
const deflate = zlib.createDeflate({ dictionary: spdyDict });
|
||||||
|
const inflate = zlib.createInflate({ dictionary: spdyDict });
|
||||||
|
|
||||||
|
deflate.on('data', function(chunk) {
|
||||||
|
if (doneReset)
|
||||||
|
inflate.write(chunk);
|
||||||
|
});
|
||||||
|
|
||||||
|
inflate.on('data', function(chunk) {
|
||||||
|
output += chunk;
|
||||||
|
});
|
||||||
|
|
||||||
|
deflate.on('end', function() {
|
||||||
|
inflate.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
inflate.on('end', function() {
|
||||||
|
assert.equal(input, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
deflate.write(input);
|
deflate.write(input);
|
||||||
deflate.flush(function() {
|
deflate.flush(function() {
|
||||||
inflate.end();
|
deflate.reset();
|
||||||
|
doneReset = true;
|
||||||
|
deflate.write(input);
|
||||||
|
deflate.end();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
run(1);
|
|
||||||
|
|
||||||
process.on('exit', function() {
|
basicDictionaryTest();
|
||||||
assert.equal(called, 2);
|
deflateResetDictionaryTest();
|
||||||
});
|
|
||||||
|
49
test/parallel/test-zlib-truncated.js
Normal file
49
test/parallel/test-zlib-truncated.js
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
'use strict';
|
||||||
|
// tests zlib streams with truncated compressed input
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const assert = require('assert');
|
||||||
|
const zlib = require ('zlib');
|
||||||
|
|
||||||
|
const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing el' +
|
||||||
|
'it. Morbi faucibus, purus at gravida dictum, libero arcu convallis la' +
|
||||||
|
'cus, in commodo libero metus eu nisi. Nullam commodo, neque nec porta' +
|
||||||
|
' placerat, nisi est fermentum augue, vitae gravida tellus sapien sit ' +
|
||||||
|
'amet tellus. Aenean non diam orci. Proin quis elit turpis. Suspendiss' +
|
||||||
|
'e non diam ipsum. Suspendisse nec ullamcorper odio. Vestibulum arcu m' +
|
||||||
|
'i, sodales non suscipit id, ultrices ut massa. Sed ac sem sit amet ar' +
|
||||||
|
'cu malesuada fermentum. Nunc sed. ';
|
||||||
|
|
||||||
|
[
|
||||||
|
{ comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' },
|
||||||
|
{ comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' },
|
||||||
|
{ comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' },
|
||||||
|
{ comp: 'deflateRaw', decomp: 'inflateRaw', decompSync: 'inflateRawSync' }
|
||||||
|
].forEach(function(methods) {
|
||||||
|
zlib[methods.comp](inputString, function(err, compressed) {
|
||||||
|
assert(!err);
|
||||||
|
let truncated = compressed.slice(0, compressed.length / 2);
|
||||||
|
|
||||||
|
// sync sanity
|
||||||
|
assert.doesNotThrow(function() {
|
||||||
|
let decompressed = zlib[methods.decompSync](compressed);
|
||||||
|
assert.equal(decompressed, inputString);
|
||||||
|
});
|
||||||
|
|
||||||
|
// async sanity
|
||||||
|
zlib[methods.decomp](compressed, function(err, result) {
|
||||||
|
assert.ifError(err);
|
||||||
|
assert.equal(result, inputString);
|
||||||
|
});
|
||||||
|
|
||||||
|
// sync truncated input test
|
||||||
|
assert.throws(function() {
|
||||||
|
zlib[methods.decompSync](truncated);
|
||||||
|
}, /unexpected end of file/);
|
||||||
|
|
||||||
|
// async truncated input test
|
||||||
|
zlib[methods.decomp](truncated, function(err, result) {
|
||||||
|
assert(/unexpected end of file/.test(err.message));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user