]> git.kaiwu.me - njs.git/commitdiff
Zlib: validate NJS chunk sizes
authorDmitry Volyntsev <xeioex@nginx.com>
Tue, 22 Sep 2026 04:03:53 +0000 (21:03 -0700)
committerDmitry Volyntsev <xeioexception@gmail.com>
Fri, 25 Sep 2026 23:00:29 +0000 (16:00 -0700)
external/njs_zlib_module.c
external/qjs_zlib_module.c
src/njs.h
src/njs_number.c
src/njs_number.h
test/zlib.t.mjs

index 46202fb9c4978c3090b4257ea4fa82d0d2cc189e..a7c0b769123ff9415864efe8472caf344fcf7cf6 100644 (file)
@@ -5,6 +5,7 @@
 
 
 #include <njs.h>
+#include <limits.h>
 #include <string.h>
 #include <zlib.h>
 
@@ -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. */
 }
-
index caeb2761efb7d1613bc10e1a59174846643e476b..a980c9b2f7573c15674b7ad375f74dfd1fccc0ef 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include <qjs.h>
+#include <limits.h>
 #include <zlib.h>
 
 #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");
index b6fa1b47c68a27d809a7177195831c5923b88498..be0ec2243199e72c66b83d09f0f1db02fa813e97 100644 (file)
--- 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);
index dc2001d21d036d5b30c1faa26ce45539b4796c1b..94f4693e3eeeb6f4cbf0fd62a9dd8df6a5ef6c25 100644 (file)
@@ -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)
 {
index 3a03302a1b23e4a4252498f75f1da245c23f07ad..ce195ba5153e904d71ba922b6aab0be5aa1a772a 100644 (file)
@@ -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)
 {
index 612a41984ed29893132e1e0a53e76000ad9bc16d..69352ec115d11d140f6a365473f6b317ba3e76d6 100644 (file)
@@ -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' },