Skip to content

Wait for off-thread jobs before freeing a terminated worker's VM - #36983

Closed
robobun wants to merge 14 commits into
mainfrom
farm/aa76752d/worker-offthread-teardown-fence
Closed

Wait for off-thread jobs before freeing a terminated worker's VM#36983
robobun wants to merge 14 commits into
mainfrom
farm/aa76752d/worker-offthread-teardown-fence

Tighten the teardown-skip comment

038c50b
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Aug 5, 2026 in 36m 3s

Code review found 2 important issues

Found 2 candidates, confirmed 2. See review comments for details.

Details

Severity Count
🔴 Important 2
🟡 Nit 0
🟣 Pre-existing 0
Severity File:Line Issue
🔴 Important src/jsc/event_loop.rs:1027-1034 fs.watchFile InitialStatTask is not bracketed by the off-thread fence — worker-terminate UAF on the pool thread's enqueu
🔴 Important src/runtime/webcore/s3/simple_request.rs:474-476 list_objects() ends the off-thread fence it never begins → outstanding_offthread underflow

Annotations

Check failure on line 1034 in src/jsc/event_loop.rs

See this annotation in the file changed.

@claude claude / Claude Code Review

fs.watchFile InitialStatTask is not bracketed by the off-thread fence — worker-terminate UAF on the pool thread's enqueue

Another missed sibling of the `KeepAlive::ref_` → `WorkPool::schedule` → pool-thread `enqueue_task_concurrent` shape this PR brackets: `InitialStatTask` in `src/runtime/node/node_fs_stat_watcher.rs`. `create_and_schedule` (:1160-1168) does `StatWatcher::ref_` + `WorkPool::schedule_new` with no `offthread_job_begin()`, and the pool-thread `run_owned` reaches `self.ctx.event_loop_shared().enqueue_task_concurrent(...)` (:666, `ctx: BackRef<VirtualMachine>`) after a stat syscall — so a worker termin

Check failure on line 476 in src/runtime/webcore/s3/simple_request.rs

See this annotation in the file changed.

@claude claude / Claude Code Review

list_objects() ends the off-thread fence it never begins → outstanding_offthread underflow

`list_objects()` (client.rs:297-370) is a second construction site for `S3HttpSimpleTask` that wires the same `http_callback` (line 354) and schedules to the HTTP thread (line 370) with no `offthread_job_begin()` / `register_terminate_cancel_hook()` — but the shared callback now unconditionally calls `offthread_job_end()` here. Every `s3.list()` completion on a worker therefore `fetch_sub(1)`s an `AtomicU32` that was never incremented: it either wraps to `u32::MAX` (10s stall + VM leak on termin