From: Dmitry Volyntsev Date: Tue, 22 Sep 2026 03:28:29 +0000 (-0700) Subject: Buffer: convert QJS array-like lengths X-Git-Tag: 1.0.2~21 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=d2b6e6db651add450bfa2a8752a5feff5a0ec573;p=njs.git Buffer: convert QJS array-like lengths Convert array-like lengths through checked floating-point conversion before applying ToLength semantics. This avoids reading float-tagged QuickJS numbers through the integer accessor. The length is kept as a 64-bit value and passed to qjs_buffer_alloc() unchanged, so an oversized length is rejected before any allocation instead of being truncated. --- diff --git a/src/qjs.h b/src/qjs.h index 865db0d2..41df9046 100644 --- a/src/qjs.h +++ b/src/qjs.h @@ -114,6 +114,29 @@ typedef struct { } qjs_bytes_t; +njs_inline int +qjs_to_length(JSContext *ctx, JSValueConst value, uint64_t *length) +{ + double number; + + if (JS_ToFloat64(ctx, &number, value)) { + return -1; + } + + if (number != number || number <= 0) { + *length = 0; + + } else if (number > 0x1fffffffffffffLL) { + *length = 0x1fffffffffffffLL; + + } else { + *length = number; + } + + return 0; +} + + njs_inline int qjs_is_typed_array(JSContext *cx, JSValue val) { diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index ade8559e..9359a80e 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -2018,7 +2018,7 @@ qjs_buffer_from_object(JSContext *ctx, JSValueConst obj) { int v; u_char *p; - int64_t i, len; + uint64_t i, len; JSValue buffer, ret; njs_str_t dst; const char *str; @@ -2068,7 +2068,12 @@ reject: return JS_EXCEPTION; } - len = JS_VALUE_GET_INT(ret); + if (qjs_to_length(ctx, ret, &len)) { + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } + + JS_FreeValue(ctx, ret); buffer = qjs_buffer_alloc(ctx, len); if (JS_IsException(buffer)) { diff --git a/test/buffer.t.js b/test/buffer.t.js index 8111ffa4..1d749493 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -1,5 +1,5 @@ /*--- -includes: [compatBuffer.js, runTsuite.js, compareArray.js] +includes: [compatBuffer.js, compatNjs.js, runTsuite.js, compareArray.js] flags: [async] ---*/ @@ -35,6 +35,42 @@ let alloc_tsuite = { }; +let fromObject_tsuite = { + name: "Buffer.from() array-like length tests", + skip: () => (!has_buffer()), + T: async (params) => { + let buffer = Buffer.from(params.value); + + if (buffer.toString() !== params.expected) { + throw Error(`unexpected output "${buffer.toString()}"`); + } + + return 'SUCCESS'; + }, + + tests: [ + { value: { length: 1.5, 0: 0x41 }, expected: 'A' }, + { value: { length: -1, 0: 0x41 }, expected: '' }, + { value: { length: NaN, 0: 0x41 }, expected: '' }, + ], +}; + + +let fromObjectLarge_tsuite = { + name: "Buffer.from() oversized array-like length tests", + skip: () => (!has_buffer() || !has_njs()), + T: async () => { + assert.throws(RangeError, () => Buffer.from({ + length: 0x100000010, + get 0() { throw Error('unexpected index access'); }, + })); + + return 'SUCCESS'; + }, + tests: [{}], +}; + + let byteLength_tsuite = { name: "Buffer.byteLength() tests", skip: () => (!has_buffer()), @@ -1122,6 +1158,8 @@ let writeGeneric_tsuite = { run([ alloc_tsuite, + fromObject_tsuite, + fromObjectLarge_tsuite, byteLength_tsuite, concat_tsuite, concatRevalidate_tsuite,