From: Dmitry Volyntsev Date: Thu, 21 May 2026 23:01:56 +0000 (-0700) Subject: QuickJS: fixed Buffer.from() and encoding error paths X-Git-Tag: 1.0.0~53 X-Git-Url: http://git.kaiwu.me/postgresql/log/contrib/postgres_fdw/postgres_fdw.c?a=commitdiff_plain;h=a1d2131f664f725bb9c187225ab7582f2a5efa6c;p=njs.git QuickJS: fixed Buffer.from() and encoding error paths Freed the typed array constructor object after reading constructor.name while detecting Float32Array in Buffer.from(). This keeps the exception path from leaking the constructor object. Handled JS_ToCString() failure for constructor.name before comparing the name, avoiding a NULL dereference when the property converts to an exception, such as a Symbol value. Divided the source offset by the element size in qjs_buffer_from() typed-array path so the offset addresses the right element for 2-, 4-, and 8-byte element types (previously the offset was left in byte units while size was already in element units). Added a NULL check on JS_ToCStringLen() in qjs_buffer_encoding(), and moved the JS_FreeCString() call after the JS_ThrowTypeError() so the encoding name remains valid while the error message is formatted. Routed array source errors in qjs_buffer_from_object() through a single fail label so the destination buffer is freed once on every failure path (previously leaked on three of them). --- diff --git a/src/qjs_buffer.c b/src/qjs_buffer.c index 890b2028..90515f74 100644 --- a/src/qjs_buffer.c +++ b/src/qjs_buffer.c @@ -635,13 +635,18 @@ qjs_buffer_from(JSContext *ctx, JSValueConst this_val, int argc, } name = JS_GetPropertyStr(ctx, ctor, "name"); + JS_FreeValue(ctx, ctor); if (JS_IsException(name)) { JS_FreeValue(ctx, ret); return name; } - JS_FreeValue(ctx, ctor); str = JS_ToCString(ctx, name); + if (str == NULL) { + JS_FreeValue(ctx, name); + JS_FreeValue(ctx, ret); + return JS_EXCEPTION; + } if (strncmp(str, "Float32Array", 12) == 0) { float32 = 1; @@ -1909,6 +1914,7 @@ qjs_buffer_from_typed_array(JSContext *ctx, JSValueConst arr_buf, njs_str_t src, dst; size = size / bytes; + offset = offset / bytes; buffer = qjs_buffer_alloc(ctx, size); if (JS_IsException(buffer)) { JS_FreeValue(ctx, arr_buf); @@ -2045,7 +2051,7 @@ reject: ret = qjs_typed_array_data(ctx, buffer, &dst); if (JS_IsException(ret)) { - return ret; + goto fail; } p = dst.start; @@ -2053,11 +2059,12 @@ reject: for (i = 0; i < len; i++) { ret = JS_GetPropertyUint32(ctx, obj, i); if (njs_slow_path(JS_IsException(ret))) { - return ret; + goto fail; } if (njs_slow_path(JS_ToInt32(ctx, &v, ret))) { - return JS_EXCEPTION; + JS_FreeValue(ctx, ret); + goto fail; } JS_FreeValue(ctx, ret); @@ -2066,6 +2073,12 @@ reject: } return buffer; + +fail: + + JS_FreeValue(ctx, buffer); + + return JS_EXCEPTION; } @@ -2085,6 +2098,9 @@ qjs_buffer_encoding(JSContext *ctx, JSValueConst value, JS_BOOL thrw) } name.start = (u_char *) JS_ToCStringLen(ctx, &name.length, value); + if (name.start == NULL) { + return NULL; + } for (encoding = &qjs_buffer_encodings[0]; encoding->name.length != 0; @@ -2096,13 +2112,13 @@ qjs_buffer_encoding(JSContext *ctx, JSValueConst value, JS_BOOL thrw) } } - JS_FreeCString(ctx, (char *) name.start); - if (thrw) { JS_ThrowTypeError(ctx, "\"%.*s\" encoding is not supported", (int) name.length, name.start); } + JS_FreeCString(ctx, (char *) name.start); + return NULL; } diff --git a/test/buffer.t.js b/test/buffer.t.js index ee7831d9..71aa73a2 100644 --- a/test/buffer.t.js +++ b/test/buffer.t.js @@ -290,6 +290,18 @@ let fill_tsuite = { }; +function typedArrayWithOffset(TypedArray, prefix, values) { + let bytes = TypedArray.BYTES_PER_ELEMENT; + let buffer = new ArrayBuffer((prefix.length + values.length) * bytes); + let view = new TypedArray(buffer, prefix.length * bytes, values.length); + + new TypedArray(buffer, 0, prefix.length).set(prefix); + view.set(values); + + return view; +} + + let from_tsuite = { name: "Buffer.from() tests", skip: () => (!has_buffer()), @@ -344,6 +356,19 @@ let from_tsuite = { { args: [new Float32Array([234.001, 123.11])], fmt: "hex", expected: 'ea7b' }, { args: [new Uint32Array([234, 123])], fmt: "hex", expected: 'ea7b' }, { args: [new Float64Array([234.001, 123.11])], fmt: "hex", expected: 'ea7b' }, + { args: [typedArrayWithOffset(Uint16Array, + [0xaaaa, 0xbbbb, 0xcccc, 0xdddd], + [0x1234, 0x00ff, 0x0100, 0x017f])], + fmt: "hex", expected: '34ff007f' }, + { args: [typedArrayWithOffset(Uint32Array, + [0xaaaaaaaa, 0xbbbbbbbb, + 0xcccccccc, 0xdddddddd], + [0x12345678, 0x000000ff, + 0x00000100, 0x0000017f])], + fmt: "hex", expected: '78ff007f' }, + { args: [typedArrayWithOffset(Float64Array, + [1000.01, 1001.01], [234.001, 123.11])], + fmt: "hex", expected: 'ea7b' }, { args: [(new Uint8Array(2)).buffer, -1], exception: 'RangeError: invalid index' }, @@ -369,6 +394,10 @@ let from_tsuite = { fmt: "hex", expected: '010203' }, { args: [(function() {var a = [1,2,3,4]; a[1] = { valueOf() { a.length = 3; return 1; } }; return a})()], fmt: "hex", expected: '01010300' }, + { args: [{length: 3, get 0() { throw Error('boom') }}], + exception: 'Error: boom' }, + { args: [{length: 3, 0: { valueOf() { throw Error('boom') } }}], + exception: 'Error: boom' }, { args: [{type: 'B'}], exception: 'TypeError: first argument is not a string or Buffer-like object' },