Skip to content

global buffer overflow in JS_ReadObjectRec #1017

@AlonZa

Description

@AlonZa

The Bug

There is a global buffer overflow in JS_ReadObjectRec function.

ASAN Report

INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 311050782
INFO: Loaded 1 modules   (80420 inline 8-bit counters): 80420 [0x5584ea629bc0, 0x5584ea63d5e4),
INFO: Loaded 1 PC tables (80420 PCs): 80420 [0x5584ea63d5e8,0x5584ea777828),
./fuzz: Running 1 inputs 1 time(s) each.
Running: crash-420240802a9692d300d1f67777e255373bb31083
quickjs.c:37040:32: runtime error: index 128 out of bounds for type 'const char *const[24]'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior quickjs.c:37040:32 in
=================================================================
==2943628==ERROR: AddressSanitizer: global-buffer-overflow on address 0x5584ea53ace0 at pc 0x5584ea1bf8c2 bp 0x7ffcc37a9cd0 sp 0x7ffcc37a9cc8
READ of size 8 at 0x5584ea53ace0 thread T0
    #0 0x5584ea1bf8c1 in JS_ReadObjectRec /home/quickjs-ng/quickjs/./quickjs.c:37040:32
    #1 0x5584ea1bcad4 in JS_ReadObject2 /home/quickjs-ng/quickjs/./quickjs.c:37260:15
    #2 0x5584ea230e88 in JS_ReadObject /home/quickjs-ng/quickjs/./quickjs.c:37275:12
    #3 0x5584ea230e88 in LLVMFuzzerTestOneInput /home/quickjs-ng/quickjs/fuzz.c:18:19
    #4 0x5584e9fe1663 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned long) (/home/quickjs-ng/quickjs/fuzz+0x373663) (BuildId: 7a87fb5d1bd097c8db62c64f6f9c0cad19fffa70)
    #5 0x5584e9fcb3df in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned long) (/home/quickjs-ng/quickjs/fuzz+0x35d3df) (BuildId: 7a87fb5d1bd097c8db62c64f6f9c0cad19fffa70)
    #6 0x5584e9fd1136 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned long)) (/home/quickjs-ng/quickjs/fuzz+0x363136) (BuildId: 7a87fb5d1bd097c8db62c64f6f9c0cad19fffa70)
    #7 0x5584e9ffaf52 in main (/home/quickjs-ng/quickjs/fuzz+0x38cf52) (BuildId: 7a87fb5d1bd097c8db62c64f6f9c0cad19fffa70)
    #8 0x7fd277ecfd8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #9 0x7fd277ecfe3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #10 0x5584e9fc5ca4 in _start (/home/quickjs-ng/quickjs/fuzz+0x357ca4) (BuildId: 7a87fb5d1bd097c8db62c64f6f9c0cad19fffa70)

0x5584ea53ace0 is located 32 bytes to the left of global variable 'js_regexp_string_iterator_proto_funcs' defined in './quickjs.c:46455:35' (0x5584ea53ad00) of size 64
0x5584ea53ace0 is located 0 bytes to the right of global variable 'js_regexp_funcs' defined in './quickjs.c:46428:35' (0x5584ea53aca0) of size 64
SUMMARY: AddressSanitizer: global-buffer-overflow /home/quickjs-ng/quickjs/./quickjs.c:37040:32 in JS_ReadObjectRec
Shadow bytes around the buggy address:
  0x0ab11d49f540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f570: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f580: 00 00 00 00 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9 f9
=>0x0ab11d49f590: f9 f9 f9 f9 00 00 00 00 00 00 00 00[f9]f9 f9 f9
  0x0ab11d49f5a0: 00 00 00 00 00 00 00 00 f9 f9 f9 f9 00 00 00 00
  0x0ab11d49f5b0: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f5c0: f9 f9 f9 f9 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f5d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0ab11d49f5e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==2943628==ABORTING

Description

In the JS_ReadObjectRec function, an out-of-bounds array access occurs when processing invalid tag values in binary data. The function reads a tag value from user input and directly uses it as an index into the bc_tag_str array without bounds checking.

Code

static JSValue JS_ReadObjectRec(BCReaderState *s)
{
    JSContext *ctx = s->ctx;
    uint8_t tag;
    JSValue obj = JS_UNDEFINED;

    if (js_check_stack_overflow(ctx->rt, 0))
        return JS_ThrowStackOverflow(ctx);

    if (bc_get_u8(s, &tag)) // #1 <-- tag is being read here
        return JS_EXCEPTION;

    bc_read_trace(s, "%s {\n", bc_tag_str[tag]); // #2 <-- tag is being used here
   
    ...
}

Reproducing

fuzz.c:


// clang -g -O1 -fsanitize=fuzzer -o fuzz fuzz.c
#include "quickjs.h"
#include "quickjs.c"
#include "cutils.c"
#include "libregexp.c"
#include "libunicode.c"
#include "xsum.c"
#include <stdlib.h>

int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t len)
{
    JSRuntime *rt = JS_NewRuntime();
    if (!rt)
        exit(1);
    JSContext *ctx = JS_NewContext(rt);
    if (!ctx)
        exit(1);
    JSValue val = JS_ReadObject(ctx, buf, len, /*flags*/0);
    JS_FreeValue(ctx, val);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
    return 0;
}

and use the following input:

echo -ne "\x14\x00\x80\x00\x26\x00\xFF\x7F\xAB" > crashing_input.bin

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