From 71017c30807c5d0ee3ed5e81dd0cb36328797bfb Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 1 Feb 2026 14:19:24 +0100 Subject: [PATCH] Check for NULL object references in deserializer JS_ReadTypedArray contains a hack where it briefly puts a NULL object pointer in the object reference table to work around a chicken-and-egg problem. Malicious or corrupt BJSON could reference that entry while it was still NULL and trigger a segfault. Guard against that. Fixes: https://github.com/quickjs-ng/quickjs/issues/1321 --- quickjs.c | 2 +- tests/test_bjson.js | 17 +++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/quickjs.c b/quickjs.c index 09da2be78..05716dd48 100644 --- a/quickjs.c +++ b/quickjs.c @@ -38649,7 +38649,7 @@ static JSValue JS_ReadObjectRec(BCReaderState *s) if (bc_get_leb128(s, &val)) return JS_EXCEPTION; bc_read_trace(s, "%u\n", val); - if (val >= s->objects_count) { + if (val >= s->objects_count || !s->objects[val]) { return JS_ThrowSyntaxError(ctx, "invalid object reference (%u >= %u)", val, s->objects_count); } diff --git a/tests/test_bjson.js b/tests/test_bjson.js index 467db68b5..0aad010b6 100644 --- a/tests/test_bjson.js +++ b/tests/test_bjson.js @@ -268,7 +268,7 @@ function bjson_test_bytecode() var buf, o, r, e, i; o = std.evalScript(";(function f(o){ return o.i })", {compile_only: true}); - buf = bjson.write(o, /*JS_WRITE_OBJ_BYTECODE*/(1 << 0)); + buf = bjson.write(o, bjson.WRITE_OBJ_BYTECODE); try { bjson.read(buf, 0, buf.byteLength); } catch (_e) { @@ -276,7 +276,7 @@ function bjson_test_bytecode() } assert(String(e), "SyntaxError: no bytecode allowed"); - o = bjson.read(buf, 0, buf.byteLength, /*JS_READ_OBJ_BYTECODE*/(1 << 0)); + o = bjson.read(buf, 0, buf.byteLength, bjson.READ_OBJ_BYTECODE); assert(String(o), "[function bytecode]"); o = std.evalScript(o, {eval_function: true}); for (i = 0; i < 42; i++) o({i}); // exercise o.i IC @@ -285,15 +285,16 @@ function bjson_test_bytecode() function bjson_test_fuzz() { var corpus = [ - "FxAAAAAABGA=", - "F+bm5oIt", - "FwARABMGBgYGBgYGBgYGBv////8QABEALxH/vy8R/78=", - "FwAIfwAK/////3//////////////////////////////3/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAD5+fn5+fn5+fn5+fkAAAAAAAYAqw==", + ["FxAAAAAABGA="], + ["F+bm5oIt"], + ["FwARABMGBgYGBgYGBgYGBv////8QABEALxH/vy8R/78="], + ["FwAIfwAK/////3//////////////////////////////3/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAD5+fn5+fn5+fn5+fkAAAAAAAYAqw=="], + ["FwAOAAAAFAA=", bjson.READ_OBJ_REFERENCE], ]; - for (var input of corpus) { + for (var [input, flags] of corpus) { var buf = base64decode(input); try { - bjson.read(buf, 0, buf.byteLength); + bjson.read(buf, 0, buf.byteLength, flags); } catch (e) { if (/invalid version/.test(e.message)) throw e; // corpus needs update }