From 70a52642971daeb7f952cf8878643cd23fc2af6b Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Mon, 21 Sep 2026 21:03:53 -0700 Subject: [PATCH] Zlib: validate NJS chunk sizes --- external/njs_zlib_module.c | 10 +++++----- external/qjs_zlib_module.c | 21 +++++++++++++++------ src/njs.h | 1 + src/njs_number.c | 18 ++++++++++++++++++ src/njs_number.h | 18 ------------------ test/zlib.t.mjs | 8 ++++++++ 6 files changed, 47 insertions(+), 29 deletions(-) diff --git a/external/njs_zlib_module.c b/external/njs_zlib_module.c index 46202fb9..a7c0b769 100644 --- a/external/njs_zlib_module.c +++ b/external/njs_zlib_module.c @@ -5,6 +5,7 @@ #include +#include #include #include @@ -221,9 +222,9 @@ njs_zlib_ext_deflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, if (njs_value_is_object(options)) { value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue); if (value != NULL) { - chunk_size = njs_value_number(value); + chunk_size = njs_number_to_length(njs_value_number(value)); - if (njs_slow_path(chunk_size < 64)) { + if (njs_slow_path(chunk_size < 64 || chunk_size > UINT_MAX)) { njs_vm_range_error(vm, "chunkSize must be >= 64"); return NJS_ERROR; } @@ -404,9 +405,9 @@ njs_zlib_ext_inflate(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, if (njs_value_is_object(options)) { value = njs_vm_object_prop(vm, options, &chunk_size_key, &lvalue); if (value != NULL) { - chunk_size = njs_value_number(value); + chunk_size = njs_number_to_length(njs_value_number(value)); - if (njs_slow_path(chunk_size < 64)) { + if (njs_slow_path(chunk_size < 64 || chunk_size > UINT_MAX)) { njs_vm_range_error(vm, "chunkSize must be >= 64"); return NJS_ERROR; } @@ -570,4 +571,3 @@ njs_zlib_free(void *opaque, void *address) { /* Do nothing. */ } - diff --git a/external/qjs_zlib_module.c b/external/qjs_zlib_module.c index caeb2761..a980c9b2 100644 --- a/external/qjs_zlib_module.c +++ b/external/qjs_zlib_module.c @@ -5,6 +5,7 @@ */ #include +#include #include #define NJS_ZLIB_CHUNK_SIZE 1024 @@ -68,7 +69,9 @@ static JSValue qjs_zlib_ext_deflate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int raw) { - int rc, chunk_size, level, mem_level, strategy, window_bits; + int rc, level, mem_level, strategy, window_bits; + size_t chunk_size; + uint64_t chunk_size_value; JSValue ret, options; z_stream stream; njs_chb_t chain; @@ -94,16 +97,18 @@ qjs_zlib_ext_deflate(JSContext *ctx, JSValueConst this_val, int argc, } if (!JS_IsUndefined(ret)) { - rc = JS_ToInt32(ctx, &chunk_size, ret); + rc = qjs_to_length(ctx, ret, &chunk_size_value); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } - if (chunk_size < 64) { + if (chunk_size_value < 64 || chunk_size_value > UINT_MAX) { JS_ThrowRangeError(ctx, "chunkSize must be >= 64"); return JS_EXCEPTION; } + + chunk_size = chunk_size_value; } ret = JS_GetPropertyStr(ctx, options, "level"); @@ -296,7 +301,9 @@ static JSValue qjs_zlib_ext_inflate(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv, int raw) { - int rc, chunk_size, window_bits; + int rc, window_bits; + size_t chunk_size; + uint64_t chunk_size_value; JSValue ret, options; z_stream stream; njs_chb_t chain; @@ -319,16 +326,18 @@ qjs_zlib_ext_inflate(JSContext *ctx, JSValueConst this_val, int argc, } if (!JS_IsUndefined(ret)) { - rc = JS_ToInt32(ctx, &chunk_size, ret); + rc = qjs_to_length(ctx, ret, &chunk_size_value); JS_FreeValue(ctx, ret); if (rc != 0) { return JS_EXCEPTION; } - if (chunk_size < 64) { + if (chunk_size_value < 64 || chunk_size_value > UINT_MAX) { JS_ThrowRangeError(ctx, "chunkSize must be >= 64"); return JS_EXCEPTION; } + + chunk_size = chunk_size_value; } ret = JS_GetPropertyStr(ctx, options, "windowBits"); diff --git a/src/njs.h b/src/njs.h index b6fa1b47..be0ec224 100644 --- a/src/njs.h +++ b/src/njs.h @@ -489,6 +489,7 @@ NJS_EXPORT void njs_value_external_set(njs_value_t *value, NJS_EXPORT uint8_t njs_value_bool(const njs_value_t *value); NJS_EXPORT double njs_value_number(const njs_value_t *value); +NJS_EXPORT uint64_t njs_number_to_length(double num); NJS_EXPORT njs_function_t *njs_value_function(const njs_value_t *value); NJS_EXPORT njs_function_native_t njs_value_native_function( const njs_value_t *value); diff --git a/src/njs_number.c b/src/njs_number.c index dc2001d2..94f4693e 100644 --- a/src/njs_number.c +++ b/src/njs_number.c @@ -20,6 +20,24 @@ static njs_int_t njs_number_to_string_radix(njs_vm_t *vm, njs_value_t *string, double number, uint32_t radix); +uint64_t +njs_number_to_length(double num) +{ + if (isnan(num)) { + return 0; + } + + if (num > NJS_MAX_LENGTH) { + return NJS_MAX_LENGTH; + + } else if (num < 0.0) { + return 0; + } + + return (uint64_t) num; +} + + double njs_key_to_index(const njs_value_t *value) { diff --git a/src/njs_number.h b/src/njs_number.h index 3a03302a..ce195ba5 100644 --- a/src/njs_number.h +++ b/src/njs_number.h @@ -155,24 +155,6 @@ njs_number_to_uint16(double num) } -njs_inline uint64_t -njs_number_to_length(double num) -{ - if (isnan(num)) { - return 0; - } - - if (num > NJS_MAX_LENGTH) { - return NJS_MAX_LENGTH; - - } else if (num < 0.0) { - return 0; - } - - return (uint64_t) num; -} - - njs_inline njs_int_t njs_char_to_hex(u_char c) { diff --git a/test/zlib.t.mjs b/test/zlib.t.mjs index 612a4198..69352ec1 100644 --- a/test/zlib.t.mjs +++ b/test/zlib.t.mjs @@ -43,6 +43,10 @@ let deflateSync_tsuite = { { value: 'αβγ', raw: false, expected: 'eJw7t/HcpnObAQ/sBIE=' }, { value: 'WAKA', options: {level: 10}, exception: 'RangeError: level must be in the range -1..9' }, + { value: 'WAKA', options: {chunkSize: Infinity}, + exception: 'RangeError: chunkSize must be >= 64' }, + { value: 'WAKA', options: {chunkSize: NaN}, + exception: 'RangeError: chunkSize must be >= 64' }, { value: 'WAKA', options: {strategy: 10}, exception: 'RangeError: unknown strategy: 10' }, { value: 'WAKA', options: {memLevel: 10}, exception: 'RangeError: memLevel must be in the range 1..9' }, { value: 'WAKA', options: {windowBits: 99}, exception: 'RangeError: windowBits must be in the range -15..-9' }, @@ -88,6 +92,10 @@ let inflateSync_tsuite = { { value: Buffer.from('C3f0dgQA', 'base64'), options: {chunkSize: 0}, exception: 'RangeError: chunkSize must be >= 64' }, + { value: Buffer.from('C3f0dgQA', 'base64'), options: {chunkSize: Infinity}, + exception: 'RangeError: chunkSize must be >= 64' }, + { value: Buffer.from('C3f0dgQA', 'base64'), options: {chunkSize: NaN}, + exception: 'RangeError: chunkSize must be >= 64' }, { value: Buffer.from('C3f0dgQA', 'base64'), options: {windowBits: 0}, exception: 'RangeError: windowBits must be in the range -15..-8' }, -- 2.47.3