doc gc bugs

This commit is contained in:
2026-02-21 03:01:26 -06:00
parent 81c88f9439
commit eadad194be
2 changed files with 78 additions and 16 deletions

View File

@@ -453,6 +453,53 @@ JSC_CCALL(mymod_make,
)
```
### C Argument Evaluation Order (critical)
In C, the **order of evaluation of function arguments is unspecified**. This interacts with the copying GC to create intermittent crashes that are extremely difficult to diagnose.
```c
// UNSAFE — crashes intermittently:
JS_FRAME(js);
JS_ROOT(obj, JS_NewObject(js));
JS_SetPropertyStr(js, obj.val, "format", JS_NewString(js, "rgba32"));
// ^^^^^^^ may be evaluated BEFORE JS_NewString runs
// If JS_NewString triggers GC, the already-read obj.val is a dangling pointer.
```
The compiler is free to evaluate `obj.val` into a register, *then* call `JS_NewString`. If `JS_NewString` triggers GC, the object moves to a new address. The rooted `obj` is updated by GC, but the **register copy** is not — it still holds the old address. `JS_SetPropertyStr` then writes to freed memory.
**Fix:** always separate the allocating call into a local variable:
```c
// SAFE:
JS_FRAME(js);
JS_ROOT(obj, JS_NewObject(js));
JSValue fmt = JS_NewString(js, "rgba32");
JS_SetPropertyStr(js, obj.val, "format", fmt);
// obj.val is read AFTER JS_NewString completes — guaranteed correct.
```
This applies to **any** allocating function used as an argument when another argument references a rooted `.val`:
```c
// ALL of these are UNSAFE:
JS_SetPropertyStr(js, obj.val, "pixels", js_new_blob_stoned_copy(js, data, len));
JS_SetPropertyStr(js, obj.val, "x", JS_NewFloat64(js, 3.14));
JS_SetPropertyStr(js, obj.val, "name", JS_NewString(js, name));
// SAFE versions — separate the allocation:
JSValue pixels = js_new_blob_stoned_copy(js, data, len);
JS_SetPropertyStr(js, obj.val, "pixels", pixels);
JSValue x = JS_NewFloat64(js, 3.14);
JS_SetPropertyStr(js, obj.val, "x", x);
JSValue s = JS_NewString(js, name);
JS_SetPropertyStr(js, obj.val, "name", s);
```
**Functions that allocate** (must be separated): `JS_NewString`, `JS_NewFloat64`, `JS_NewInt64`, `JS_NewObject`, `JS_NewArray`, `JS_NewCFunction`, `js_new_blob_stoned_copy`
**Functions that do NOT allocate** (safe inline): `JS_NewInt32`, `JS_NewUint32`, `JS_NewBool`, `JS_NULL`, `JS_TRUE`, `JS_FALSE`
### Macros
| Macro | Purpose |