From bc629c8b659edd31eb532ae7ccc5f7816098d5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Mon, 5 Jan 2026 11:22:53 +0100 Subject: [PATCH] Fix heap buffer overflow via side-effects in js_typed_array_constructor Fixes: https://github.com/quickjs-ng/quickjs/issues/1296 Fixes: https://github.com/bellard/quickjs/issues/478 --- Makefile | 1 + quickjs.c | 21 +++++++++++++++++++++ tests/bug1296.js | 13 +++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 tests/bug1296.js diff --git a/Makefile b/Makefile index 4f63239..59f3af2 100644 --- a/Makefile +++ b/Makefile @@ -457,6 +457,7 @@ test: qjs$(EXE) $(WINE) ./qjs$(EXE) tests/bug1301.js $(WINE) ./qjs$(EXE) tests/bug1302.js $(WINE) ./qjs$(EXE) tests/bug1305.js + $(WINE) ./qjs$(EXE) tests/bug1296.js ifndef CONFIG_WIN32 $(WINE) ./qjs$(EXE) tests/test_std.js endif diff --git a/quickjs.c b/quickjs.c index 63b8696..87b5f62 100644 --- a/quickjs.c +++ b/quickjs.c @@ -58228,6 +58228,27 @@ static JSValue js_typed_array_constructor(JSContext *ctx, JS_FreeValue(ctx, buffer); return JS_EXCEPTION; } + // Re-validate buffer after js_create_from_ctor which may have run JS code + // that resized or detached the ArrayBuffer + abuf = JS_VALUE_GET_OBJ(buffer)->u.array_buffer; + if (abuf->detached) { + JS_FreeValue(ctx, buffer); + JS_FreeValue(ctx, obj); + return JS_ThrowTypeErrorDetachedArrayBuffer(ctx); + } + if (offset > abuf->byte_length) { + JS_FreeValue(ctx, buffer); + JS_FreeValue(ctx, obj); + return JS_ThrowRangeError(ctx, "invalid offset"); + } + if (track_rab) { + // Recalculate length for RAB-backed view + len = (abuf->byte_length - offset) >> size_log2; + } else if ((offset + (len << size_log2)) > abuf->byte_length) { + JS_FreeValue(ctx, buffer); + JS_FreeValue(ctx, obj); + return JS_ThrowRangeError(ctx, "invalid length"); + } if (typed_array_init(ctx, obj, buffer, offset, len, track_rab)) { JS_FreeValue(ctx, obj); return JS_EXCEPTION; diff --git a/tests/bug1296.js b/tests/bug1296.js new file mode 100644 index 0000000..753f99f --- /dev/null +++ b/tests/bug1296.js @@ -0,0 +1,13 @@ +import {assert} from "./assert.js" + +const ab = new ArrayBuffer(10, { maxByteLength: 10 }); +function f() { + return 1337; +} +const evil = f.bind(); + +Object.defineProperty(evil, "prototype", { get: () => { + return ab.resize(); +} }); +let u8 = Reflect.construct(Uint8Array, [ab], evil); +assert(u8.length == 0);