fix: full reset 清空未捕获异常侧通道(跨 epoch 残留悬垂)

clear_execution_state 漏清 last_uncaught_value:原生错误后残留的原值
持 epoch 对象指针,reset/full_reset 的池回收后悬垂,后续原生错误经
raise_call_error 消费残留值即 UAF。侧通道属执行期状态,随异常状态一并
清空;补 reset/full_reset 两边界钉。
This commit is contained in:
2026-09-12 08:27:42 +00:00
parent 4538dcd9a8
commit e520b0dd93
+19
View File
@@ -474,6 +474,9 @@ impl Vm {
self.cell_stack.clear();
self.try_stack.clear();
self.exception_value = None;
// 未捕获异常侧通道持原始 epoch 对象指针:执行期状态,跨 run/reset 不保留,
// 池回收后残留将悬垂。
self.last_uncaught_value = None;
self.pending_exception = None;
self.pending_error_kind = None;
self.pending_completion = None;
@@ -1260,6 +1263,22 @@ mod tests {
assert!(unsafe { *session_ptr } == 123);
}
/// 未捕获异常侧通道是执行期状态:原生错误后可能残留原值,reset/full_reset
/// 边界须随执行状态一并清空——否则其持有的 epoch 对象指针在池回收后悬垂
/// (后续原生错误经 raise_call_error 消费残留值即 UAF)。
#[test]
fn reset_drops_stale_uncaught_value() {
let mut vm = Vm::new();
let _ = run_source(&mut vm, "0");
vm.last_uncaught_value = Some(JsValue::float(42.0));
vm.reset();
assert!(vm.last_uncaught_value.is_none(), "reset 应清空未捕获异常侧通道");
vm.last_uncaught_value = Some(JsValue::float(42.0));
vm.full_reset();
assert!(vm.last_uncaught_value.is_none(), "full_reset 应清空未捕获异常侧通道");
}
/// run 边界换新 Bump 后双 arena 保留锚恰 0:重源 run 冲高水位,
/// full_reset 后 epoch/session 两 arena 均空(容量不跨 reset 保留)。
#[test]