robobun
|
8826b4f5f5
|
Fix WTFTimer issues with Atomics.waitAsync (#23442)
## Summary
Fixes two critical issues in `WTFTimer` when `Atomics.waitAsync` creates
multiple timer instances.
## Problems
### 1. Use-After-Free in `WTFTimer.fire()`
**Location:** `/workspace/bun/src/bun.js/api/Timer/WTFTimer.zig:70-82`
```zig
pub fn fire(this: *WTFTimer, _: *const bun.timespec, _: *VirtualMachine) EventLoopTimer.Arm {
this.event_loop_timer.state = .FIRED;
this.imminent.store(null, .seq_cst);
this.runWithoutRemoving(); // ← Callback might destroy `this`
return if (this.repeat) // ← UAF: accessing freed memory
.{ .rearm = this.event_loop_timer.next }
else
.disarm;
}
```
When `Atomics.waitAsync` creates a `DispatchTimer` with a timeout, the
timer fires and the callback destroys `this`, but we continue to access
it.
### 2. Imminent Pointer Corruption
**Location:** `/workspace/bun/src/bun.js/api/Timer/WTFTimer.zig:36-42`
```zig
pub fn update(this: *WTFTimer, seconds: f64, repeat: bool) void {
// Multiple WTFTimers unconditionally overwrite the shared imminent pointer
this.imminent.store(if (seconds == 0) this else null, .seq_cst);
// ...
}
```
All `WTFTimer` instances share the same
`vm.eventLoop().imminent_gc_timer` atomic pointer. When multiple timers
are created (GC timer + Atomics.waitAsync timers), they stomp on each
other's imminent state.
## Solutions
### 1. UAF Fix
Read `this.repeat` and `this.event_loop_timer.next` **before** calling
`runWithoutRemoving()`:
```zig
const should_repeat = this.repeat;
const next_time = this.event_loop_timer.next;
this.runWithoutRemoving();
return if (should_repeat)
.{ .rearm = next_time }
else
.disarm;
```
### 2. Imminent Pointer Fix
Use compare-and-swap to only set imminent if it's null, and only clear
it if this timer was the one that set it:
```zig
if (seconds == 0) {
_ = this.imminent.cmpxchgStrong(null, this, .seq_cst, .seq_cst);
return;
} else {
_ = this.imminent.cmpxchgStrong(this, null, .seq_cst, .seq_cst);
}
```
## Test Plan
Added regression test at
`test/regression/issue/atomics-waitasync-wtftimer-uaf.test.ts`:
```javascript
const buffer = new SharedArrayBuffer(16);
const view = new Int32Array(buffer);
Atomics.store(view, 0, 0);
const result = Atomics.waitAsync(view, 0, 0, 10);
setTimeout(() => {
console.log("hi");
}, 100);
```
**Before:** Crashes with UAF under ASAN
**After:** Runs cleanly
All existing atomics tests pass.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Bot <claude-bot@bun.sh>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com>
|
2025-10-10 03:47:38 -07:00 |
|