From e7183940721309d5d338e934cba0941c7ac57d64 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Sat, 15 Aug 2026 18:56:25 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20full=5Freset=20=E5=AD=98=E5=9C=A8=20sess?= =?UTF-8?q?ion=20=E5=AF=B9=E8=B1=A1=E6=97=B6=E5=BC=BA=E5=88=B6=E9=87=8D?= =?UTF-8?q?=E5=BB=BA=20global=20=E5=A0=B5=E6=B1=A0=E8=B7=AF=E5=BE=84=20UAF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 覆盖既有 global 槽的写入不递增 generation,脏检测漏判导致保留的 global 持有已释放的 session 对象指针。session 对象非空即强制 bump global generation,借既有快照机制触发重建。 --- crates/oxide_vm/src/vm_support.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/oxide_vm/src/vm_support.rs b/crates/oxide_vm/src/vm_support.rs index bc16213..ed84e0b 100644 --- a/crates/oxide_vm/src/vm_support.rs +++ b/crates/oxide_vm/src/vm_support.rs @@ -253,6 +253,13 @@ impl Vm { /// /// 用于在多次 JS 执行之间达到完全隔离:session 内未被污染的 builtin 保留原指针。 pub fn full_reset(&mut self) { + // session 对象只能来自用户写 + promote,跨 full_reset 若保留 global 会悬垂: + // 覆盖既有 global 槽的写入不递增 generation(脏检测依赖 generation 对比), + // 此处强制 bump 使带 session 对象时 global 必然重建。 + if !self.gc_state.session_object_ptrs.is_empty() { + let global_ptr = self.session.global_object().as_ptr() as *mut JsObject; + unsafe { &mut *global_ptr }.bump_generation(); + } let dirty = self.session.selective_reset(&self.kernel_core); if dirty.any_builtin_dirty() { bindings::rebind_dirty_builtins(&self.kernel_core, &mut self.session, Some(&dirty)); @@ -633,6 +640,26 @@ mod tests { vm.run(&module).expect("vm run failed") } + #[test] + fn full_reset_with_session_objects_forces_global_rebuild() { + let mut vm = Vm::new(); + // 池路径场景:`globalThis.Array = {}` 覆盖既有 global 槽(不递增 generation), + // 新值 `{}` 经 promote 进入 session——global 保留时该指针将悬垂。 + let _ = run_source(&mut vm, "globalThis.Array = {}; 0"); + assert!(!vm.gc_state.session_object_ptrs.is_empty(), "覆盖写应触发 promote 进入 session"); + let old_global = vm.session.global_object.as_ptr(); + + vm.full_reset(); + + // global 必须重建:旧 global 与其 session 对象随 epoch 释放,Array 恢复内置构造器。 + assert!(!std::ptr::eq(old_global, vm.session.global_object.as_ptr())); + assert!(std::ptr::eq( + global_prop(&vm, "Array").as_js_object_ptr(), + vm.session.builtin_world().array_constructor.as_ptr() as *mut JsObject + )); + assert!(!vm.session.is_dirty_since_snapshot()); + } + #[test] fn full_reset_clean_keeps_session_objects() { let mut vm = Vm::new(); @@ -952,3 +979,4 @@ mod tests { assert_eq!(vm.lookup_str(te).as_deref(), Some("TypeError")); } } +