Permalink
Browse files

zlib: fix node crashing on invalid options

This commit fixes the Node process crashing when constructors of classes
of the zlib module are given invalid options.

* Throw an Error when the zlib library rejects the value of windowBits,
  instead of crashing with an assertion.

* Treat windowBits and memLevel options consistently with other ones and
  don't crash when non-numeric values are given.

* Fix bugs in the validation logic:
  - Don't conflate 0 and undefined when checking if a field of an
    options object exists.
  - Treat NaN and Infinity values the same way as values of invalid
    types instead of allowing to actually set zlib options to NaN or
    Infinity.

PR-URL: #13098
Fixes: #13082
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Sakthipriyan Vairamani <thechargingvolcano@gmail.com>
  • Loading branch information...
aqrln authored and jasnell committed May 18, 2017
1 parent b4f6ea0 commit efae43f0ee22e0fa38c5db86710ff4e6f475a584
Showing with 73 additions and 15 deletions.
  1. +4 −0 doc/api/zlib.md
  2. +24 −10 lib/zlib.js
  3. +8 −5 src/node_zlib.cc
  4. +37 −0 test/parallel/test-zlib-failed-init.js
View
@@ -437,6 +437,10 @@ added: v0.5.8
Returns a new [DeflateRaw][] object with an [options][].
**Note:** The zlib library rejects requests for 256-byte windows (i.e.,
`{ windowBits: 8 }` in `options`). An `Error` will be thrown when creating
a [DeflateRaw][] object with this specific value of the `windowBits` option.
## zlib.createGunzip([options])
<!-- YAML
added: v0.5.8
View
@@ -182,37 +182,37 @@ class Zlib extends Transform {
this._finishFlushFlag = opts.finishFlush !== undefined ?
opts.finishFlush : constants.Z_FINISH;
if (opts.chunkSize) {
if (opts.chunkSize !== undefined) {
if (opts.chunkSize < constants.Z_MIN_CHUNK) {
throw new RangeError('Invalid chunk size: ' + opts.chunkSize);
}
}
if (opts.windowBits) {
if (opts.windowBits !== undefined) {
if (opts.windowBits < constants.Z_MIN_WINDOWBITS ||
opts.windowBits > constants.Z_MAX_WINDOWBITS) {
throw new RangeError('Invalid windowBits: ' + opts.windowBits);
}
}
if (opts.level) {
if (opts.level !== undefined) {
if (opts.level < constants.Z_MIN_LEVEL ||
opts.level > constants.Z_MAX_LEVEL) {
throw new RangeError('Invalid compression level: ' + opts.level);
}
}
if (opts.memLevel) {
if (opts.memLevel !== undefined) {
if (opts.memLevel < constants.Z_MIN_MEMLEVEL ||
opts.memLevel > constants.Z_MAX_MEMLEVEL) {
throw new RangeError('Invalid memLevel: ' + opts.memLevel);
}
}
if (opts.strategy && isInvalidStrategy(opts.strategy))
if (opts.strategy !== undefined && isInvalidStrategy(opts.strategy))
throw new TypeError('Invalid strategy: ' + opts.strategy);
if (opts.dictionary) {
if (opts.dictionary !== undefined) {
if (!ArrayBuffer.isView(opts.dictionary)) {
throw new TypeError(
'Invalid dictionary: it should be a Buffer, TypedArray, or DataView');
@@ -224,14 +224,28 @@ class Zlib extends Transform {
this._hadError = false;
var level = constants.Z_DEFAULT_COMPRESSION;
if (typeof opts.level === 'number') level = opts.level;
if (Number.isFinite(opts.level)) {
level = opts.level;
}
var strategy = constants.Z_DEFAULT_STRATEGY;
if (typeof opts.strategy === 'number') strategy = opts.strategy;
if (Number.isFinite(opts.strategy)) {
strategy = opts.strategy;
}
var windowBits = constants.Z_DEFAULT_WINDOWBITS;
if (Number.isFinite(opts.windowBits)) {
windowBits = opts.windowBits;
}
var memLevel = constants.Z_DEFAULT_MEMLEVEL;
if (Number.isFinite(opts.memLevel)) {
memLevel = opts.memLevel;
}
this._handle.init(opts.windowBits || constants.Z_DEFAULT_WINDOWBITS,
this._handle.init(windowBits,
level,
opts.memLevel || constants.Z_DEFAULT_MEMLEVEL,
memLevel,
strategy,
opts.dictionary);
View
@@ -551,16 +551,19 @@ class ZCtx : public AsyncWrap {
CHECK(0 && "wtf?");
}
if (ctx->err_ != Z_OK) {
ZCtx::Error(ctx, "Init error");
}
ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
ctx->dictionary_len_ = dictionary_len;
ctx->write_in_progress_ = false;
ctx->init_done_ = true;
if (ctx->err_ != Z_OK) {
if (dictionary != nullptr) {
delete[] dictionary;
ctx->dictionary_ = nullptr;
}
ctx->env()->ThrowError("Init error");
}
}
static void SetDictionary(ZCtx* ctx) {
@@ -0,0 +1,37 @@
'use strict';
require('../common');
const assert = require('assert');
const zlib = require('zlib');
// For raw deflate encoding, requests for 256-byte windows are rejected as
// invalid by zlib.
// (http://zlib.net/manual.html#Advanced)
assert.throws(() => {
zlib.createDeflateRaw({ windowBits: 8 });
}, /^Error: Init error$/);
// Regression tests for bugs in the validation logic.
assert.throws(() => {
zlib.createGzip({ chunkSize: 0 });
}, /^RangeError: Invalid chunk size: 0$/);
assert.throws(() => {
zlib.createGzip({ windowBits: 0 });
}, /^RangeError: Invalid windowBits: 0$/);
assert.throws(() => {
zlib.createGzip({ memLevel: 0 });
}, /^RangeError: Invalid memLevel: 0$/);
{
const stream = zlib.createGzip({ level: NaN });
assert.strictEqual(stream._level, zlib.constants.Z_DEFAULT_COMPRESSION);
}
{
const stream = zlib.createGzip({ strategy: NaN });
assert.strictEqual(stream._strategy, zlib.constants.Z_DEFAULT_STRATEGY);
}

0 comments on commit efae43f

Please sign in to comment.