-
Notifications
You must be signed in to change notification settings - Fork 688
Description
Hi, there is a potential bug in the way externally backed SharedArrayBuffers are destroyed during garbage collection.
This bug was reproduced on b706935.
Description
In the normal process of allocating, then freeing and garbage collecting an externally-backed SharedArrayBuffer, I noticed that buffers smaller than 8 bytes lead to a heap overflow during the garbage collection process.
After investigating the issue, I found that in the process of garbage collection, it actually tries to insert this externally-backed pointer into the heap list (via jmem_heap_free_block_internal). As a result, during this process it will cast the pointer to a jmem_heap_free_t * stub (which implicitly assumes the size is at least 8). Then during the linking, it will write to the size and/or next_offset fields in this chunk which will overflow in the case that the backing memory was not at least 8 bytes:
jerryscript/jerry-core/jmem/jmem-heap.c
Lines 436 to 437 in b706935
| block_p->size = (uint32_t) size; | |
| prev_p->next_offset = block_offset; |
POC
The following testcase demonstrates the bug:
testcase.cpp
#include <cstdio>
#include <cstdlib>
#include <cstdint>
#include <cstring>
extern "C" {
#include "/fuzz/install/include/jerryscript-types.h"
#include "/fuzz/install/include/jerryscript-core.h"
#include "/fuzz/install/include/jerryscript.h"
}
int main(){
jerry_init((jerry_init_flag_t)0);
jerry_size_t sz = 7;
uint8_t *buf = (uint8_t*)malloc(sz);
memset(buf, 0xA5, sz);
// Create a SharedArrayBuffer backed by external memory
jerry_value_t sab = jerry_shared_arraybuffer_external(buf, sz, NULL);
// Drop the JS wrapper to make it collectible
jerry_value_free(sab);
// Force a GC cycle; this deterministically triggers the bug
jerry_heap_gc(JERRY_GC_PRESSURE_HIGH);
return 0;
}
stdout
stderr
=================================================================
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000014 at pc 0x55b2ca065804 bp 0x7ffceb1323b0 sp 0x7ffceb1323a8
WRITE of size 4 at 0x502000000014 thread T0
#0 0x55b2ca065803 in jmem_heap_free_block_internal (/fuzz/test+0x3e803) (BuildId: 32fc6a54c4437d40f91715e431fa5cbb5820f7e7)
#1 0x55b2ca14c1e4 in ecma_gc_run (/fuzz/test+0x1251e4) (BuildId: 32fc6a54c4437d40f91715e431fa5cbb5820f7e7)
#2 0x55b2ca14de64 in ecma_free_unused_memory (/fuzz/test+0x126e64) (BuildId: 32fc6a54c4437d40f91715e431fa5cbb5820f7e7)
#3 0x55b2ca141c42 in main /fuzz/testcase.cpp:21:3
#4 0x7fca43468d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
#5 0x7fca43468e3f in __libc_start_main csu/../csu/libc-start.c:392:3
#6 0x55b2ca066ae4 in _start (/fuzz/test+0x3fae4) (BuildId: 32fc6a54c4437d40f91715e431fa5cbb5820f7e7)
0x502000000017 is located 0 bytes after 7-byte region [0x502000000010,0x502000000017)
allocated by thread T0 here:
#0 0x55b2ca102e8e in malloc (/fuzz/test+0xdbe8e) (BuildId: 32fc6a54c4437d40f91715e431fa5cbb5820f7e7)
#1 0x55b2ca141c06 in main /fuzz/testcase.cpp:13:28
#2 0x7fca43468d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
SUMMARY: AddressSanitizer: heap-buffer-overflow (/fuzz/test+0x3e803) (BuildId: 32fc6a54c4437d40f91715e431fa5cbb5820f7e7) in jmem_heap_free_block_internal
Shadow bytes around the buggy address:
0x501ffffffd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501ffffffe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501ffffffe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501fffffff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x501fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x502000000000: fa fa[07]fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000000100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000000180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000000200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x502000000280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
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
==1==ABORTING
Steps to Reproduce
The crash was triaged with the following Dockerfile:
Dockerfile
# Ubuntu 22.04 with some packages pre-installed
FROM hgarrereyn/stitch_repro_base@sha256:3ae94cdb7bf2660f4941dc523fe48cd2555049f6fb7d17577f5efd32a40fdd2c
RUN git clone https://github.com/jerryscript-project/jerryscript /fuzz/src && \
cd /fuzz/src && \
git checkout b7069350c2e52e7dc721dfb75f067147bd79b39b && \
git submodule update --init --remote --recursive
ENV LD_LIBRARY_PATH=/fuzz/install/lib
ENV ASAN_OPTIONS=hard_rss_limit_mb=1024:detect_leaks=0
RUN echo '#!/bin/bash\nexec clang-17 -fsanitize=address -O0 "$@"' > /usr/local/bin/clang_wrapper && \
chmod +x /usr/local/bin/clang_wrapper && \
echo '#!/bin/bash\nexec clang++-17 -fsanitize=address -O0 "$@"' > /usr/local/bin/clang_wrapper++ && \
chmod +x /usr/local/bin/clang_wrapper++
# Install dependencies needed by the build
RUN apt-get update && apt-get install -y --no-install-recommends \
python3 \
cmake \
ninja-build \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /fuzz
ENV CC=clang_wrapper \
CXX=clang_wrapper++
# Configure
RUN cmake -S /fuzz/src -B /fuzz/build -G Ninja \
-DCMAKE_C_COMPILER=clang_wrapper \
-DCMAKE_CXX_COMPILER=clang_wrapper++ \
-DCMAKE_INSTALL_PREFIX=/fuzz/install \
-DBUILD_SHARED_LIBS=OFF \
-DJERRY_CMDLINE=OFF \
-DJERRY_CMDLINE_TEST=OFF \
-DJERRY_CMDLINE_SNAPSHOT=OFF \
-DUNITTESTS=OFF \
-DDOCTESTS=OFF \
-DENABLE_LTO=OFF \
-DJERRY_EXT=ON \
-DJERRY_PORT=ON
# Build and install
RUN cmake --build /fuzz/build --target install -- -vBuild Command
clang++-17 -fsanitize=address -g -O0 -o /fuzz/test /fuzz/testcase.cpp -I/fuzz/install/include -L/fuzz/install/lib -ljerry-core -ljerry-port -ljerry-ext -lm && /fuzz/testReproduce
- Copy
Dockerfileandtestcase.cppinto a local folder. - Build the repro image:
docker build . -t repro --platform=linux/amd64- Compile and run the testcase in the image:
docker run \
-it --rm \
--platform linux/amd64 \
--mount type=bind,source="$(pwd)/testcase.cpp",target=/fuzz/testcase.cpp \
repro \
bash -c "clang++-17 -fsanitize=address -g -O0 -o /fuzz/test /fuzz/testcase.cpp -I/fuzz/install/include -L/fuzz/install/lib -ljerry-core -ljerry-port -ljerry-ext -lm && /fuzz/test"Additional Info
This testcase was discovered by STITCH, an autonomous fuzzing system. All reports are reviewed manually (by a human) before submission.