Skip to content

Commit fcde7b6

Browse files
committed
Fix thread-safety issue in quickjs-libc
`JS_NewClassID(rt, &class_id)` where `class_id` is a global variable is unsafe when called from multiple threads but that is exactly what quickjs-libc.c did. Change the class ids in quickjs-libc.c to thread-locals, and change the function prototype of JS_NewClassID to hopefully make it harder for downstream users to introduce such bugs. The gcc48 and tcc buildbots don't support _Thread_local and that is why this commit removes them. I toyed with the idea of porting the uv_key_create/uv_key_get/etc. functions from libuv but because quickjs-libc doesn't know when it's safe to free the thread-local keys, that leads to memory leaks, and support for ancient or niche compilers isn't worth that. Fixes: #577
1 parent ddabcf5 commit fcde7b6

File tree

6 files changed

+9
-70
lines changed

6 files changed

+9
-70
lines changed

.github/workflows/ci.yml

-47
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,6 @@ jobs:
5555
- { os: ubuntu-latest, configType: asan, runTest262: true }
5656
- { os: ubuntu-latest, configType: ubsan, runTest262: true }
5757
- { os: ubuntu-latest, configType: msan }
58-
- { os: ubuntu-latest, configType: tcc }
5958
- { os: ubuntu-latest, arch: x86 }
6059
- { os: ubuntu-latest, arch: riscv64 }
6160
- { os: ubuntu-latest, arch: s390x }
@@ -84,20 +83,6 @@ jobs:
8483
run: |
8584
sudo sysctl -w kernel.randomize_va_space=0
8685
87-
- name: install TCC
88-
if: ${{ matrix.config.configType == 'tcc' }}
89-
run: |
90-
pushd /tmp
91-
git clone https://repo.or.cz/tinycc.git
92-
cd tinycc
93-
git checkout 9d2068c6309dc50dfdbbc30a5d6757683d3f884c
94-
./configure
95-
make
96-
sudo make install
97-
tcc -v
98-
popd
99-
echo "CC=tcc" >> $GITHUB_ENV;
100-
10186
- name: Setup Alpine
10287
if: ${{ matrix.config.arch != '' }}
10388
uses: jirutka/setup-alpine@v1
@@ -160,38 +145,6 @@ jobs:
160145
run: |
161146
time make test262
162147
163-
linux-gcc48:
164-
runs-on: ubuntu-latest
165-
container:
166-
image: ubuntu:18.04
167-
# Work around an issue where the node from actions/checkout is too new
168-
# to run inside the old container image.
169-
env:
170-
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
171-
steps:
172-
- name: install dependencies
173-
run: |
174-
apt update && apt -y install make gcc-4.8 cmake software-properties-common
175-
# git in default ppa repository is too old to run submodule checkout
176-
add-apt-repository -y ppa:git-core/ppa
177-
apt update && apt install -y git
178-
- name: checkout
179-
uses: actions/checkout@v3
180-
with:
181-
submodules: true
182-
- name: build
183-
env:
184-
CC: gcc-4.8
185-
run: |
186-
mkdir build
187-
cd build
188-
cmake ..
189-
cd ..
190-
make -C build -j$(getconf _NPROCESSORS_ONLN)
191-
- name: stats
192-
run: |
193-
./build/qjs -qd
194-
195148
windows-msvc:
196149
runs-on: windows-latest
197150
strategy:

CMakeLists.txt

+1-9
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,7 @@ endif()
282282
# Test262 runner
283283
#
284284

285-
if(WIN32
286-
OR EMSCRIPTEN
287-
OR CMAKE_C_COMPILER_ID STREQUAL "TinyCC"
288-
OR CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_LESS 5)
289-
# Empty. run-test262 uses pthreads, sorry Windows users.
290-
# tcc and gcc 4.8 don't understand _Thread_local, whereas I
291-
# don't understand why people still use 4.8 in this day and age
292-
# but hey, here we are.
293-
else()
285+
if(NOT WIN32)
294286
add_executable(run-test262
295287
run-test262.c
296288
)

examples/point.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ static int js_point_init(JSContext *ctx, JSModuleDef *m)
126126
JSRuntime *rt = JS_GetRuntime(ctx);
127127

128128
/* create the Point class */
129-
JS_NewClassID(rt, &js_point_class_id);
129+
js_point_class_id = JS_NewClassID(rt);
130130
JS_NewClass(rt, js_point_class_id, &js_point_class);
131131

132132
point_proto = JS_NewObject(ctx);

quickjs-libc.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@ static JSValue js_evalScript(JSContext *ctx, JSValue this_val,
874874
return ret;
875875
}
876876

877-
static JSClassID js_std_file_class_id;
877+
static _Thread_local JSClassID js_std_file_class_id;
878878

879879
typedef struct {
880880
FILE *f;
@@ -1633,7 +1633,7 @@ static int js_std_init(JSContext *ctx, JSModuleDef *m)
16331633

16341634
/* FILE class */
16351635
/* the class ID is created once */
1636-
JS_NewClassID(rt, &js_std_file_class_id);
1636+
js_std_file_class_id = JS_NewClassID(rt);
16371637
/* the class is created once per runtime */
16381638
JS_NewClass(rt, js_std_file_class_id, &js_std_file_class);
16391639
proto = JS_NewObject(ctx);
@@ -3275,7 +3275,7 @@ typedef struct {
32753275
uint64_t buf[];
32763276
} JSSABHeader;
32773277

3278-
static JSClassID js_worker_class_id;
3278+
static _Thread_local JSClassID js_worker_class_id;
32793279
static JSContext *(*js_worker_new_context_func)(JSRuntime *rt);
32803280

32813281
static int atomic_add_int(int *ptr, int v)
@@ -3835,7 +3835,7 @@ static int js_os_init(JSContext *ctx, JSModuleDef *m)
38353835
JSThreadState *ts = JS_GetRuntimeOpaque(rt);
38363836
JSValue proto, obj;
38373837
/* Worker class */
3838-
JS_NewClassID(rt, &js_worker_class_id);
3838+
js_worker_class_id = JS_NewClassID(rt);
38393839
JS_NewClass(rt, js_worker_class_id, &js_worker_class);
38403840
proto = JS_NewObject(ctx);
38413841
JS_SetPropertyFunctionList(ctx, proto, js_worker_proto_funcs, countof(js_worker_proto_funcs));

quickjs.c

+2-8
Original file line numberDiff line numberDiff line change
@@ -3441,15 +3441,9 @@ static inline BOOL JS_IsEmptyString(JSValue v)
34413441

34423442
/* JSClass support */
34433443

3444-
/* a new class ID is allocated if *pclass_id != 0 */
3445-
JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id)
3444+
JSClassID JS_NewClassID(JSRuntime *rt)
34463445
{
3447-
JSClassID class_id = *pclass_id;
3448-
if (class_id == 0) {
3449-
class_id = rt->js_class_id_alloc++;
3450-
*pclass_id = class_id;
3451-
}
3452-
return class_id;
3446+
return rt->js_class_id_alloc++;
34533447
}
34543448

34553449
JSClassID JS_GetClassID(JSValue v)

quickjs.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,7 @@ typedef struct JSClassDef {
466466
} JSClassDef;
467467

468468
#define JS_INVALID_CLASS_ID 0
469-
JS_EXTERN JSClassID JS_NewClassID(JSRuntime *rt, JSClassID *pclass_id);
469+
JS_EXTERN JSClassID JS_NewClassID(JSRuntime *rt);
470470
/* Returns the class ID if `v` is an object, otherwise returns JS_INVALID_CLASS_ID. */
471471
JS_EXTERN JSClassID JS_GetClassID(JSValue v);
472472
JS_EXTERN int JS_NewClass(JSRuntime *rt, JSClassID class_id, const JSClassDef *class_def);

0 commit comments

Comments
 (0)