Skip to content

Commit 80169b1

Browse files
jhamhaderrvagg
authored andcommitted
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: #2043 PR-URL: #2595 Reviewed-By: Trevor Norris <[email protected]>
1 parent 412252c commit 80169b1

File tree

3 files changed

+102
-34
lines changed

3 files changed

+102
-34
lines changed

src/node_zlib.cc

+5-1
Original file line numberDiff line numberDiff line change
@@ -271,8 +271,12 @@ class ZCtx : public AsyncWrap {
271271
// Acceptable error states depend on the type of zlib stream.
272272
switch (ctx->err_) {
273273
case Z_OK:
274-
case Z_STREAM_END:
275274
case Z_BUF_ERROR:
275+
if (ctx->strm_.avail_out != 0 && ctx->flush_ == Z_FINISH) {
276+
ZCtx::Error(ctx, "unexpected end of file");
277+
return false;
278+
}
279+
case Z_STREAM_END:
276280
// normal statuses, not fatal
277281
break;
278282
case Z_NEED_DICT:

test/parallel/test-zlib-dictionary.js

+48-33
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
'use strict';
22
// test compression/decompression with dictionary
33

4-
var common = require('../common');
5-
var assert = require('assert');
6-
var zlib = require('zlib');
7-
var path = require('path');
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const zlib = require('zlib');
7+
const path = require('path');
88

9-
var spdyDict = new Buffer([
9+
const spdyDict = new Buffer([
1010
'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-',
1111
'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi',
1212
'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser',
@@ -22,54 +22,69 @@ var spdyDict = new Buffer([
2222
'.1statusversionurl\0'
2323
].join(''));
2424

25-
var deflate = zlib.createDeflate({ dictionary: spdyDict });
26-
27-
var input = [
25+
const input = [
2826
'HTTP/1.1 200 Ok',
2927
'Server: node.js',
3028
'Content-Length: 0',
3129
''
3230
].join('\r\n');
3331

34-
var called = 0;
35-
36-
//
37-
// We'll use clean-new inflate stream each time
38-
// and .reset() old dirty deflate one
39-
//
40-
function run(num) {
41-
var inflate = zlib.createInflate({ dictionary: spdyDict });
42-
43-
if (num === 2) {
44-
deflate.reset();
45-
deflate.removeAllListeners('data');
46-
}
32+
function basicDictionaryTest() {
33+
let output = '';
34+
const deflate = zlib.createDeflate({ dictionary: spdyDict });
35+
const inflate = zlib.createInflate({ dictionary: spdyDict });
4736

48-
// Put data into deflate stream
4937
deflate.on('data', function(chunk) {
5038
inflate.write(chunk);
5139
});
5240

53-
// Get data from inflate stream
54-
var output = [];
5541
inflate.on('data', function(chunk) {
56-
output.push(chunk);
42+
output += chunk;
43+
});
44+
45+
deflate.on('end', function() {
46+
inflate.end();
5747
});
48+
5849
inflate.on('end', function() {
59-
called++;
50+
assert.equal(input, output);
51+
});
52+
53+
deflate.write(input);
54+
deflate.end();
55+
}
6056

61-
assert.equal(output.join(''), input);
57+
function deflateResetDictionaryTest() {
58+
let doneReset = false;
59+
let output = '';
60+
const deflate = zlib.createDeflate({ dictionary: spdyDict });
61+
const inflate = zlib.createInflate({ dictionary: spdyDict });
6262

63-
if (num < 2) run(num + 1);
63+
deflate.on('data', function(chunk) {
64+
if (doneReset)
65+
inflate.write(chunk);
66+
});
67+
68+
inflate.on('data', function(chunk) {
69+
output += chunk;
70+
});
71+
72+
deflate.on('end', function() {
73+
inflate.end();
74+
});
75+
76+
inflate.on('end', function() {
77+
assert.equal(input, output);
6478
});
6579

6680
deflate.write(input);
6781
deflate.flush(function() {
68-
inflate.end();
82+
deflate.reset();
83+
doneReset = true;
84+
deflate.write(input);
85+
deflate.end();
6986
});
7087
}
71-
run(1);
7288

73-
process.on('exit', function() {
74-
assert.equal(called, 2);
75-
});
89+
basicDictionaryTest();
90+
deflateResetDictionaryTest();

test/parallel/test-zlib-truncated.js

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
'use strict';
2+
// tests zlib streams with truncated compressed input
3+
4+
const common = require('../common');
5+
const assert = require('assert');
6+
const zlib = require ('zlib');
7+
8+
const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing el' +
9+
'it. Morbi faucibus, purus at gravida dictum, libero arcu convallis la' +
10+
'cus, in commodo libero metus eu nisi. Nullam commodo, neque nec porta' +
11+
' placerat, nisi est fermentum augue, vitae gravida tellus sapien sit ' +
12+
'amet tellus. Aenean non diam orci. Proin quis elit turpis. Suspendiss' +
13+
'e non diam ipsum. Suspendisse nec ullamcorper odio. Vestibulum arcu m' +
14+
'i, sodales non suscipit id, ultrices ut massa. Sed ac sem sit amet ar' +
15+
'cu malesuada fermentum. Nunc sed. ';
16+
17+
[
18+
{ comp: 'gzip', decomp: 'gunzip', decompSync: 'gunzipSync' },
19+
{ comp: 'gzip', decomp: 'unzip', decompSync: 'unzipSync' },
20+
{ comp: 'deflate', decomp: 'inflate', decompSync: 'inflateSync' },
21+
{ comp: 'deflateRaw', decomp: 'inflateRaw', decompSync: 'inflateRawSync' }
22+
].forEach(function(methods) {
23+
zlib[methods.comp](inputString, function(err, compressed) {
24+
assert(!err);
25+
let truncated = compressed.slice(0, compressed.length / 2);
26+
27+
// sync sanity
28+
assert.doesNotThrow(function() {
29+
let decompressed = zlib[methods.decompSync](compressed);
30+
assert.equal(decompressed, inputString);
31+
});
32+
33+
// async sanity
34+
zlib[methods.decomp](compressed, function(err, result) {
35+
assert.ifError(err);
36+
assert.equal(result, inputString);
37+
});
38+
39+
// sync truncated input test
40+
assert.throws(function() {
41+
zlib[methods.decompSync](truncated);
42+
}, /unexpected end of file/);
43+
44+
// async truncated input test
45+
zlib[methods.decomp](truncated, function(err, result) {
46+
assert(/unexpected end of file/.test(err.message));
47+
});
48+
});
49+
});

0 commit comments

Comments
 (0)