From 5da802f039ddafae6d710d49514388c3a3b56805 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 16 Sep 2026 02:20:20 +0000 Subject: [PATCH] =?UTF-8?q?refactor:=20builtin=20=E8=84=8F=E9=87=8D?= =?UTF-8?q?=E5=BB=BA=E6=AE=B5=E6=8B=86=E5=87=BA=E7=8B=AC=E7=AB=8B=20rebuil?= =?UTF-8?q?d=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_kernel/src/builtin/mod.rs | 552 +------------------- crates/oxide_kernel/src/builtin/rebuild.rs | 566 +++++++++++++++++++++ 2 files changed, 568 insertions(+), 550 deletions(-) create mode 100644 crates/oxide_kernel/src/builtin/rebuild.rs diff --git a/crates/oxide_kernel/src/builtin/mod.rs b/crates/oxide_kernel/src/builtin/mod.rs index d9bf1589..b44afe9f 100644 --- a/crates/oxide_kernel/src/builtin/mod.rs +++ b/crates/oxide_kernel/src/builtin/mod.rs @@ -5,13 +5,14 @@ use oxide_types::mem::P; use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes}; use oxide_types::value::JsValue; -use crate::kernel::{BuiltinDirtySet, BuiltinId}; +use crate::kernel::BuiltinId; use crate::kernel_info; use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; use crate::string_forge::PermInterner; mod bind; mod methods; +mod rebuild; pub use methods::{ArrayMethods, ErrorMethods, FunctionMethods, ObjectMethods, RegExpMethods, StringMethods}; #[macro_export] @@ -678,113 +679,6 @@ impl BuiltinWorld { self.leaked_objects.borrow_mut().append(&mut from.leaked_objects.borrow_mut()); } - /// 选择性重建收尾:把保留对象(登记表 wrapper + 新旧 world 共用的保留 P - /// 字段)的 proto 槽从被替换旧指针重指到新指针,随后逐一释放被替换旧 P - /// 对象的属性区(含保活钉住的 Function/Object 四件;本体钉保留,见 - /// `rebuild_with_dirty` 的保活注记)。 - /// - /// # 边界与前提 - /// - 须在 `inherit_leaked_objects` 之后、旧 world 换出前调用(登记表完整; - /// full_reset 安全点无并发读者);重指必须先于释放完成,否则保留对象 - /// 的原型链读到已释放属性区。 - /// - 替换集由逐字段新旧指针比较(stubs 按指针集合)判定,与保留集天然 - /// 不相交:未替换字段新旧 world 沿用同一 Arc,其属性区归 session 收尾 - /// (`teardown_heap_data`)释放,此处不碰,不双放。 - /// - 保活钉住的 4 件本体不经任何路径释放(Arc 计数已被 `mem::forget` - /// 抬升);此处只释放其属性区,漏重指时读者降级为属性静默缺失 - /// (zombie 本体)而非 UAF。 - /// - 只重指 proto 槽;属性值链接由同家族同批替换与绑定层 sync_* 覆盖, - /// 不在此重指。 - /// - /// # 副作用 - /// 保留对象 proto 槽重指(generation 递增);被替换旧 P 对象属性区四区 - /// 释放并置空(幂等,重入为 no-op)。 - pub fn retire_replaced(&self, old: &BuiltinWorld) { - // 替换映射:逐字段新旧指针比较(stubs 按指针集合),记录被换出的 - // 旧指针 → 新指针;stub 无继任者记空指针,只进释放集。 - let old_fields = old.all_p_fields(); - let new_fields = self.all_p_fields(); - let mut remap: std::collections::HashMap<*mut JsObject, *mut JsObject> = std::collections::HashMap::new(); - for (o, n) in old_fields.iter().zip(new_fields.iter()) { - let (op, np) = (o.as_ptr() as *mut JsObject, n.as_ptr() as *mut JsObject); - if !std::ptr::eq(op, np) { - remap.insert(op, np); - } - } - for p in &old.stub_objects { - let op = p.as_ptr() as *mut JsObject; - if !self.stub_objects.iter().any(|q| std::ptr::eq(q.as_ptr() as *mut JsObject, op)) { - remap.insert(op, std::ptr::null_mut()); - } - } - if remap.is_empty() { - return; - } - // 重指:保留对象(新旧 world 同一指针)proto 槽仍指被替换旧指针的, - // 换新指针——与 `wire_builtin_world_links` 的 set_proto_if_changed 同模式。 - let repoint = |obj: &mut JsObject| { - let cur = obj.proto(); - if !cur.is_object() { - return; - } - let cur_ptr = cur.as_js_object_ptr(); - let Some(np) = remap.get(&cur_ptr).copied() else { - return; - }; - if np.is_null() { - return; - } - // SAFETY: np 是本 world 的 P 对象;full_reset 安全点无并发读者, - // 成环检查由 set_proto 内部完成。 - obj.set_proto(JsValue::from_js_object(np)).ok(); - }; - for (o, n) in old_fields.iter().zip(new_fields.iter()) { - let op = o.as_ptr() as *mut JsObject; - let np = n.as_ptr() as *mut JsObject; - if std::ptr::eq(op, np) { - // SAFETY: op/np 指向同一保留 P 对象,重指安全点无并发读者。 - unsafe { - repoint(&mut *np); - } - } - } - for slot in self.leaked_objects.borrow().iter() { - let ptr = slot.ptr; - // SAFETY: 登记表指针 session 存活期内有效,重指安全点无并发读者。 - unsafe { - repoint(&mut *ptr); - } - } - // 守约:重指后保留对象 proto 槽不得残留任何被替换旧指针。 - for (o, n) in old_fields.iter().zip(new_fields.iter()) { - let op = o.as_ptr() as *mut JsObject; - let np = n.as_ptr() as *mut JsObject; - if std::ptr::eq(op, np) { - let cur = unsafe { (*np).proto() }; - debug_assert!( - !cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()), - "保留字段 proto 槽不得残留被替换旧指针" - ); - } - } - for slot in self.leaked_objects.borrow().iter() { - let cur = unsafe { (*slot.ptr).proto() }; - debug_assert!( - !cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()), - "登记表 wrapper proto 槽不得残留被替换旧指针" - ); - } - // 释放:被替换旧 P 对象属性区逐一恰好释放一次(本体不释放;保活钉住 - // 的 4 件保留本体钉、属性区同样释放)。 - for &op in remap.keys() { - // SAFETY: op 是旧 world 被换出的 P 对象,属性区仅此一处释放并置空 - // (幂等);full_reset 安全点无并发读者。 - unsafe { - (&mut *op).release_raw_heap(); - } - } - } - /// 枚举本 world 全部固定 P 对象字段(按结构体字段序,含迭代器原型族、 /// stub 之外的全部命名空间对象与 console)。 /// @@ -1233,448 +1127,6 @@ impl BuiltinWorld { kernel_info!("BuiltinWorld initialized"); world } - - /// 按脏标记选择性重建 builtin world:仅重建被污染的对象家族,未污染的保留原指针。 - /// - /// # 注意事项 - /// - Function/Object 家族脏时,旧 fn_proto/object_proto 对须先保活再重建: - /// 释放表统一持有的方法 wrapper 永久泄漏(`Box::into_raw`),其 proto 裸指针 - /// 指向绑定时的 function_proto——执行期原型链查找(如 `push.call` 沿 wrapper - /// 原型链取 `call`)仍走这些对象,reset 清空执行状态不阻断该路径,旧对 - /// Arc 归零即悬空。`retire_replaced` 重指完成后旧对无读者;本体钉保留 - /// (每次 dirty rebuild 至多 4 个对象本体永久泄漏)作重指遗漏兜底——漏 - /// 重指时读者降级为属性静默缺失(zombie 本体)而非 UAF,属性区于重指 - /// 完成时由 `retire_replaced` 释放。 - pub fn rebuild_with_dirty( - current: &BuiltinWorld, string_forge: &PermInterner, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet, - ) -> BuiltinWorld { - let labels = builtin_labels(string_forge); - - // 保活:钉住旧 Function/Object 对的 Arc 计数使其永不归零——保留对象的 - // proto 槽重指在 `retire_replaced`(本函数返回后、旧 world 换出前) - // 完成,钉住的本体是重指遗漏的兜底,不经任何路径释放。 - if dirty.function || dirty.object { - std::mem::forget(current.function_proto.clone()); - std::mem::forget(current.function_constructor.clone()); - std::mem::forget(current.object_proto.clone()); - std::mem::forget(current.object_constructor.clone()); - } - let (object_proto, object_constructor) = if dirty.object { - make_named_pair(string_forge, shape_forge, labels, "Object") - } else { - (current.object_proto.clone(), current.object_constructor.clone()) - }; - let (array_proto, array_constructor) = if dirty.array { - make_named_pair(string_forge, shape_forge, labels, "Array") - } else { - (current.array_proto.clone(), current.array_constructor.clone()) - }; - let (function_proto, function_constructor) = if dirty.function { - make_named_pair(string_forge, shape_forge, labels, "Function") - } else { - (current.function_proto.clone(), current.function_constructor.clone()) - }; - let (string_proto, string_constructor) = if dirty.string { - make_named_pair(string_forge, shape_forge, labels, "String") - } else { - (current.string_proto.clone(), current.string_constructor.clone()) - }; - let (number_proto, number_constructor) = if dirty.number { - make_named_pair(string_forge, shape_forge, labels, "Number") - } else { - (current.number_proto.clone(), current.number_constructor.clone()) - }; - let (boolean_proto, boolean_constructor) = if dirty.boolean { - make_named_pair(string_forge, shape_forge, labels, "Boolean") - } else { - (current.boolean_proto.clone(), current.boolean_constructor.clone()) - }; - let (error_proto, error_constructor, error_subtypes) = if dirty.error_family { - let (error_proto, error_constructor) = make_named_pair(string_forge, shape_forge, labels, "Error"); - let error_subtypes = make_error_subtypes(&error_proto); - (error_proto, error_constructor, error_subtypes) - } else { - ( - current.error_proto.clone(), - current.error_constructor.clone(), - ErrorSubtypeProtos { - type_error_proto: current.type_error_proto.clone(), - reference_error_proto: current.reference_error_proto.clone(), - range_error_proto: current.range_error_proto.clone(), - syntax_error_proto: current.syntax_error_proto.clone(), - uri_error_proto: current.uri_error_proto.clone(), - eval_error_proto: current.eval_error_proto.clone(), - suppressed_error_proto: current.suppressed_error_proto.clone(), - }, - ) - }; - let ( - symbol_proto, - symbol_constructor, - sym_match, - sym_replace, - sym_search, - sym_split, - sym_iterator, - sym_to_primitive, - sym_has_instance, - sym_match_all, - sym_async_iterator, - sym_to_string_tag, - sym_species, - sym_async_dispose, - sym_dispose, - ) = if dirty.symbol_family { - let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol"); - ( - symbol_proto, - symbol_constructor, - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - ) - } else { - ( - current.symbol_proto.clone(), - current.symbol_constructor.clone(), - current.sym_match.clone(), - current.sym_replace.clone(), - current.sym_search.clone(), - current.sym_split.clone(), - current.sym_iterator.clone(), - current.sym_to_primitive.clone(), - current.sym_has_instance.clone(), - current.sym_match_all.clone(), - current.sym_async_iterator.clone(), - current.sym_to_string_tag.clone(), - current.sym_species.clone(), - current.sym_async_dispose.clone(), - current.sym_dispose.clone(), - ) - }; - - let math_object = if dirty.math { - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())) - } else { - current.math_object.clone() - }; - let json_object = if dirty.json { - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())) - } else { - current.json_object.clone() - }; - let (date_proto, date_constructor) = if dirty.date { - make_named_pair(string_forge, shape_forge, labels, "Date") - } else { - (current.date_proto.clone(), current.date_constructor.clone()) - }; - let (set_proto, set_constructor) = if dirty.set { - make_named_pair(string_forge, shape_forge, labels, "Set") - } else { - (current.set_proto.clone(), current.set_constructor.clone()) - }; - let (map_proto, map_constructor) = if dirty.map { - make_named_pair(string_forge, shape_forge, labels, "Map") - } else { - (current.map_proto.clone(), current.map_constructor.clone()) - }; - let (regexp_proto, regexp_constructor) = if dirty.regexp { - make_named_pair(string_forge, shape_forge, labels, "RegExp") - } else { - (current.regexp_proto.clone(), current.regexp_constructor.clone()) - }; - let (array_buffer_proto, array_buffer_constructor) = if dirty.array_buffer { - make_named_pair(string_forge, shape_forge, labels, "ArrayBuffer") - } else { - (current.array_buffer_proto.clone(), current.array_buffer_constructor.clone()) - }; - let (data_view_proto, data_view_constructor) = if dirty.data_view { - make_named_pair(string_forge, shape_forge, labels, "DataView") - } else { - (current.data_view_proto.clone(), current.data_view_constructor.clone()) - }; - let typed_arrays = if dirty.typed_array_family { - make_typed_array_family(string_forge, shape_forge, labels, &object_proto) - } else { - TypedArrayFamily { - typed_array_proto: current.typed_array_proto.clone(), - typed_array_constructor: current.typed_array_constructor.clone(), - int8array_constructor: current.int8array_constructor.clone(), - int8array_proto: current.int8array_proto.clone(), - uint8array_constructor: current.uint8array_constructor.clone(), - uint8array_proto: current.uint8array_proto.clone(), - uint8clampedarray_constructor: current.uint8clampedarray_constructor.clone(), - uint8clampedarray_proto: current.uint8clampedarray_proto.clone(), - int16array_constructor: current.int16array_constructor.clone(), - int16array_proto: current.int16array_proto.clone(), - uint16array_constructor: current.uint16array_constructor.clone(), - uint16array_proto: current.uint16array_proto.clone(), - int32array_constructor: current.int32array_constructor.clone(), - int32array_proto: current.int32array_proto.clone(), - uint32array_constructor: current.uint32array_constructor.clone(), - uint32array_proto: current.uint32array_proto.clone(), - float32array_constructor: current.float32array_constructor.clone(), - float32array_proto: current.float32array_proto.clone(), - float64array_constructor: current.float64array_constructor.clone(), - float64array_proto: current.float64array_proto.clone(), - bigint64array_constructor: current.bigint64array_constructor.clone(), - bigint64array_proto: current.bigint64array_proto.clone(), - biguint64array_constructor: current.biguint64array_constructor.clone(), - biguint64array_proto: current.biguint64array_proto.clone(), - } - }; - let ( - temporal_object, - temporal_now_object, - instant_proto, - instant_constructor, - plain_date_proto, - plain_date_constructor, - plain_time_proto, - plain_time_constructor, - duration_proto, - duration_constructor, - zoned_date_time_proto, - zoned_date_time_constructor, - plain_date_time_proto, - plain_date_time_constructor, - plain_month_day_proto, - plain_month_day_constructor, - plain_year_month_proto, - plain_year_month_constructor, - ) = if dirty.temporal { - let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant"); - let (plain_date_proto, plain_date_constructor) = - make_named_pair(string_forge, shape_forge, labels, "PlainDate"); - let (plain_time_proto, plain_time_constructor) = - make_named_pair(string_forge, shape_forge, labels, "PlainTime"); - let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration"); - let (zoned_date_time_proto, zoned_date_time_constructor) = - make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime"); - let (plain_date_time_proto, plain_date_time_constructor) = - make_named_pair(string_forge, shape_forge, labels, "PlainDateTime"); - let (plain_month_day_proto, plain_month_day_constructor) = - make_named_pair(string_forge, shape_forge, labels, "PlainMonthDay"); - let (plain_year_month_proto, plain_year_month_constructor) = - make_named_pair(string_forge, shape_forge, labels, "PlainYearMonth"); - ( - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - instant_proto, - instant_constructor, - plain_date_proto, - plain_date_constructor, - plain_time_proto, - plain_time_constructor, - duration_proto, - duration_constructor, - zoned_date_time_proto, - zoned_date_time_constructor, - plain_date_time_proto, - plain_date_time_constructor, - plain_month_day_proto, - plain_month_day_constructor, - plain_year_month_proto, - plain_year_month_constructor, - ) - } else { - ( - current.temporal_object.clone(), - current.temporal_now_object.clone(), - current.instant_proto.clone(), - current.instant_constructor.clone(), - current.plain_date_proto.clone(), - current.plain_date_constructor.clone(), - current.plain_time_proto.clone(), - current.plain_time_constructor.clone(), - current.duration_proto.clone(), - current.duration_constructor.clone(), - current.zoned_date_time_proto.clone(), - current.zoned_date_time_constructor.clone(), - current.plain_date_time_proto.clone(), - current.plain_date_time_constructor.clone(), - current.plain_month_day_proto.clone(), - current.plain_month_day_constructor.clone(), - current.plain_year_month_proto.clone(), - current.plain_year_month_constructor.clone(), - ) - }; - let (bigint_proto, bigint_constructor) = if dirty.stubs { - make_named_pair(string_forge, shape_forge, labels, "BigInt") - } else { - (current.bigint_proto.clone(), current.bigint_constructor.clone()) - }; - let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() }; - let console_object = if dirty.console { - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())) - } else { - current.console_object.clone() - }; - - // 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时 - // 一并重建,否则旧原型链指向已释放的 object_proto。 - let ( - iterator_proto, - array_iterator_proto, - map_iterator_proto, - set_iterator_proto, - string_iterator_proto, - regexp_string_iterator_proto, - iterator_helper_proto, - disposable_stack_proto, - async_disposable_stack_proto, - ) = if dirty.object { - ( - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), - ) - } else { - ( - current.iterator_proto.clone(), - current.array_iterator_proto.clone(), - current.map_iterator_proto.clone(), - current.set_iterator_proto.clone(), - current.string_iterator_proto.clone(), - current.regexp_string_iterator_proto.clone(), - current.iterator_helper_proto.clone(), - current.disposable_stack_proto.clone(), - current.async_disposable_stack_proto.clone(), - ) - }; - - let world = BuiltinWorld { - object_proto, - string_default_iterator: std::cell::Cell::new(if dirty.string { - std::ptr::null() - } else { - current.string_default_iterator.get() - }), - array_proto, - function_proto, - string_proto, - number_proto, - boolean_proto, - error_proto, - symbol_proto, - object_constructor, - array_constructor, - function_constructor, - string_constructor, - number_constructor, - boolean_constructor, - error_constructor, - symbol_constructor, - type_error_proto: error_subtypes.type_error_proto, - reference_error_proto: error_subtypes.reference_error_proto, - range_error_proto: error_subtypes.range_error_proto, - syntax_error_proto: error_subtypes.syntax_error_proto, - uri_error_proto: error_subtypes.uri_error_proto, - eval_error_proto: error_subtypes.eval_error_proto, - suppressed_error_proto: error_subtypes.suppressed_error_proto, - math_object, - json_object, - date_constructor, - date_proto, - set_constructor, - set_proto, - map_constructor, - map_proto, - regexp_constructor, - regexp_proto, - array_buffer_constructor, - array_buffer_proto, - data_view_constructor, - data_view_proto, - typed_array_proto: typed_arrays.typed_array_proto, - typed_array_constructor: typed_arrays.typed_array_constructor, - int8array_constructor: typed_arrays.int8array_constructor, - int8array_proto: typed_arrays.int8array_proto, - uint8array_constructor: typed_arrays.uint8array_constructor, - uint8array_proto: typed_arrays.uint8array_proto, - uint8clampedarray_constructor: typed_arrays.uint8clampedarray_constructor, - uint8clampedarray_proto: typed_arrays.uint8clampedarray_proto, - int16array_constructor: typed_arrays.int16array_constructor, - int16array_proto: typed_arrays.int16array_proto, - uint16array_constructor: typed_arrays.uint16array_constructor, - uint16array_proto: typed_arrays.uint16array_proto, - int32array_constructor: typed_arrays.int32array_constructor, - int32array_proto: typed_arrays.int32array_proto, - uint32array_constructor: typed_arrays.uint32array_constructor, - uint32array_proto: typed_arrays.uint32array_proto, - float32array_constructor: typed_arrays.float32array_constructor, - float32array_proto: typed_arrays.float32array_proto, - float64array_constructor: typed_arrays.float64array_constructor, - float64array_proto: typed_arrays.float64array_proto, - bigint64array_constructor: typed_arrays.bigint64array_constructor, - bigint64array_proto: typed_arrays.bigint64array_proto, - biguint64array_constructor: typed_arrays.biguint64array_constructor, - biguint64array_proto: typed_arrays.biguint64array_proto, - sym_match, - sym_replace, - sym_search, - sym_split, - sym_iterator, - sym_to_primitive, - sym_has_instance, - sym_match_all, - sym_async_iterator, - sym_to_string_tag, - sym_species, - sym_async_dispose, - sym_dispose, - temporal_object, - temporal_now_object, - instant_constructor, - instant_proto, - plain_date_constructor, - plain_date_proto, - plain_time_constructor, - plain_time_proto, - duration_constructor, - duration_proto, - zoned_date_time_constructor, - zoned_date_time_proto, - plain_date_time_constructor, - plain_date_time_proto, - plain_month_day_constructor, - plain_month_day_proto, - plain_year_month_constructor, - plain_year_month_proto, - bigint_constructor, - bigint_proto, - iterator_proto, - array_iterator_proto, - map_iterator_proto, - set_iterator_proto, - string_iterator_proto, - regexp_string_iterator_proto, - iterator_helper_proto, - disposable_stack_proto, - async_disposable_stack_proto, - stub_objects, - console_object, - leaked_objects: std::cell::RefCell::new(Vec::new()), - }; - wire_builtin_world_links(&world); - world - } } #[cfg(test)] diff --git a/crates/oxide_kernel/src/builtin/rebuild.rs b/crates/oxide_kernel/src/builtin/rebuild.rs new file mode 100644 index 00000000..0cbd532e --- /dev/null +++ b/crates/oxide_kernel/src/builtin/rebuild.rs @@ -0,0 +1,566 @@ +//! 脏重建职责:按脏标记选择性重建 builtin world(干净家族 Arc::clone 复用、 +//! Function/Object 四件套保活钉),收尾重指保留对象 proto 槽并恰好释放一次被替换 +//! 旧 P 对象的属性区。 + +use oxide_types::mem::P; +use oxide_types::object::JsObject; +use oxide_types::value::JsValue; + +use super::{ + builtin_labels, make_error_subtypes, make_named_pair, make_typed_array_family, wire_builtin_world_links, + BuiltinWorld, ErrorSubtypeProtos, TypedArrayFamily, +}; +use crate::kernel::BuiltinDirtySet; +use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; +use crate::string_forge::PermInterner; + +impl BuiltinWorld { + /// 选择性重建收尾:把保留对象(登记表 wrapper + 新旧 world 共用的保留 P + /// 字段)的 proto 槽从被替换旧指针重指到新指针,随后逐一释放被替换旧 P + /// 对象的属性区(含保活钉住的 Function/Object 四件;本体钉保留,见 + /// `rebuild_with_dirty` 的保活注记)。 + /// + /// # 边界与前提 + /// - 须在 `inherit_leaked_objects` 之后、旧 world 换出前调用(登记表完整; + /// full_reset 安全点无并发读者);重指必须先于释放完成,否则保留对象 + /// 的原型链读到已释放属性区。 + /// - 替换集由逐字段新旧指针比较(stubs 按指针集合)判定,与保留集天然 + /// 不相交:未替换字段新旧 world 沿用同一 Arc,其属性区归 session 收尾 + /// (`teardown_heap_data`)释放,此处不碰,不双放。 + /// - 保活钉住的 4 件本体不经任何路径释放(Arc 计数已被 `mem::forget` + /// 抬升);此处只释放其属性区,漏重指时读者降级为属性静默缺失 + /// (zombie 本体)而非 UAF。 + /// - 只重指 proto 槽;属性值链接由同家族同批替换与绑定层 sync_* 覆盖, + /// 不在此重指。 + /// + /// # 副作用 + /// 保留对象 proto 槽重指(generation 递增);被替换旧 P 对象属性区四区 + /// 释放并置空(幂等,重入为 no-op)。 + pub fn retire_replaced(&self, old: &BuiltinWorld) { + // 替换映射:逐字段新旧指针比较(stubs 按指针集合),记录被换出的 + // 旧指针 → 新指针;stub 无继任者记空指针,只进释放集。 + let old_fields = old.all_p_fields(); + let new_fields = self.all_p_fields(); + let mut remap: std::collections::HashMap<*mut JsObject, *mut JsObject> = std::collections::HashMap::new(); + for (o, n) in old_fields.iter().zip(new_fields.iter()) { + let (op, np) = (o.as_ptr() as *mut JsObject, n.as_ptr() as *mut JsObject); + if !std::ptr::eq(op, np) { + remap.insert(op, np); + } + } + for p in &old.stub_objects { + let op = p.as_ptr() as *mut JsObject; + if !self.stub_objects.iter().any(|q| std::ptr::eq(q.as_ptr() as *mut JsObject, op)) { + remap.insert(op, std::ptr::null_mut()); + } + } + if remap.is_empty() { + return; + } + // 重指:保留对象(新旧 world 同一指针)proto 槽仍指被替换旧指针的, + // 换新指针——与 `wire_builtin_world_links` 的 set_proto_if_changed 同模式。 + let repoint = |obj: &mut JsObject| { + let cur = obj.proto(); + if !cur.is_object() { + return; + } + let cur_ptr = cur.as_js_object_ptr(); + let Some(np) = remap.get(&cur_ptr).copied() else { + return; + }; + if np.is_null() { + return; + } + // SAFETY: np 是本 world 的 P 对象;full_reset 安全点无并发读者, + // 成环检查由 set_proto 内部完成。 + obj.set_proto(JsValue::from_js_object(np)).ok(); + }; + for (o, n) in old_fields.iter().zip(new_fields.iter()) { + let op = o.as_ptr() as *mut JsObject; + let np = n.as_ptr() as *mut JsObject; + if std::ptr::eq(op, np) { + // SAFETY: op/np 指向同一保留 P 对象,重指安全点无并发读者。 + unsafe { + repoint(&mut *np); + } + } + } + for slot in self.leaked_objects.borrow().iter() { + let ptr = slot.ptr; + // SAFETY: 登记表指针 session 存活期内有效,重指安全点无并发读者。 + unsafe { + repoint(&mut *ptr); + } + } + // 守约:重指后保留对象 proto 槽不得残留任何被替换旧指针。 + for (o, n) in old_fields.iter().zip(new_fields.iter()) { + let op = o.as_ptr() as *mut JsObject; + let np = n.as_ptr() as *mut JsObject; + if std::ptr::eq(op, np) { + let cur = unsafe { (*np).proto() }; + debug_assert!( + !cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()), + "保留字段 proto 槽不得残留被替换旧指针" + ); + } + } + for slot in self.leaked_objects.borrow().iter() { + let cur = unsafe { (*slot.ptr).proto() }; + debug_assert!( + !cur.is_object() || !remap.contains_key(&cur.as_js_object_ptr()), + "登记表 wrapper proto 槽不得残留被替换旧指针" + ); + } + // 释放:被替换旧 P 对象属性区逐一恰好释放一次(本体不释放;保活钉住 + // 的 4 件保留本体钉、属性区同样释放)。 + for &op in remap.keys() { + // SAFETY: op 是旧 world 被换出的 P 对象,属性区仅此一处释放并置空 + // (幂等);full_reset 安全点无并发读者。 + unsafe { + (&mut *op).release_raw_heap(); + } + } + } + + /// 按脏标记选择性重建 builtin world:仅重建被污染的对象家族,未污染的保留原指针。 + /// + /// # 注意事项 + /// - Function/Object 家族脏时,旧 fn_proto/object_proto 对须先保活再重建: + /// 释放表统一持有的方法 wrapper 永久泄漏(`Box::into_raw`),其 proto 裸指针 + /// 指向绑定时的 function_proto——执行期原型链查找(如 `push.call` 沿 wrapper + /// 原型链取 `call`)仍走这些对象,reset 清空执行状态不阻断该路径,旧对 + /// Arc 归零即悬空。`retire_replaced` 重指完成后旧对无读者;本体钉保留 + /// (每次 dirty rebuild 至多 4 个对象本体永久泄漏)作重指遗漏兜底——漏 + /// 重指时读者降级为属性静默缺失(zombie 本体)而非 UAF,属性区于重指 + /// 完成时由 `retire_replaced` 释放。 + pub fn rebuild_with_dirty( + current: &BuiltinWorld, string_forge: &PermInterner, shape_forge: &ShapeForge, dirty: &BuiltinDirtySet, + ) -> BuiltinWorld { + let labels = builtin_labels(string_forge); + + // 保活:钉住旧 Function/Object 对的 Arc 计数使其永不归零——保留对象的 + // proto 槽重指在 `retire_replaced`(本函数返回后、旧 world 换出前) + // 完成,钉住的本体是重指遗漏的兜底,不经任何路径释放。 + if dirty.function || dirty.object { + std::mem::forget(current.function_proto.clone()); + std::mem::forget(current.function_constructor.clone()); + std::mem::forget(current.object_proto.clone()); + std::mem::forget(current.object_constructor.clone()); + } + let (object_proto, object_constructor) = if dirty.object { + make_named_pair(string_forge, shape_forge, labels, "Object") + } else { + (current.object_proto.clone(), current.object_constructor.clone()) + }; + let (array_proto, array_constructor) = if dirty.array { + make_named_pair(string_forge, shape_forge, labels, "Array") + } else { + (current.array_proto.clone(), current.array_constructor.clone()) + }; + let (function_proto, function_constructor) = if dirty.function { + make_named_pair(string_forge, shape_forge, labels, "Function") + } else { + (current.function_proto.clone(), current.function_constructor.clone()) + }; + let (string_proto, string_constructor) = if dirty.string { + make_named_pair(string_forge, shape_forge, labels, "String") + } else { + (current.string_proto.clone(), current.string_constructor.clone()) + }; + let (number_proto, number_constructor) = if dirty.number { + make_named_pair(string_forge, shape_forge, labels, "Number") + } else { + (current.number_proto.clone(), current.number_constructor.clone()) + }; + let (boolean_proto, boolean_constructor) = if dirty.boolean { + make_named_pair(string_forge, shape_forge, labels, "Boolean") + } else { + (current.boolean_proto.clone(), current.boolean_constructor.clone()) + }; + let (error_proto, error_constructor, error_subtypes) = if dirty.error_family { + let (error_proto, error_constructor) = make_named_pair(string_forge, shape_forge, labels, "Error"); + let error_subtypes = make_error_subtypes(&error_proto); + (error_proto, error_constructor, error_subtypes) + } else { + ( + current.error_proto.clone(), + current.error_constructor.clone(), + ErrorSubtypeProtos { + type_error_proto: current.type_error_proto.clone(), + reference_error_proto: current.reference_error_proto.clone(), + range_error_proto: current.range_error_proto.clone(), + syntax_error_proto: current.syntax_error_proto.clone(), + uri_error_proto: current.uri_error_proto.clone(), + eval_error_proto: current.eval_error_proto.clone(), + suppressed_error_proto: current.suppressed_error_proto.clone(), + }, + ) + }; + let ( + symbol_proto, + symbol_constructor, + sym_match, + sym_replace, + sym_search, + sym_split, + sym_iterator, + sym_to_primitive, + sym_has_instance, + sym_match_all, + sym_async_iterator, + sym_to_string_tag, + sym_species, + sym_async_dispose, + sym_dispose, + ) = if dirty.symbol_family { + let (symbol_proto, symbol_constructor) = make_named_pair(string_forge, shape_forge, labels, "Symbol"); + ( + symbol_proto, + symbol_constructor, + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + ) + } else { + ( + current.symbol_proto.clone(), + current.symbol_constructor.clone(), + current.sym_match.clone(), + current.sym_replace.clone(), + current.sym_search.clone(), + current.sym_split.clone(), + current.sym_iterator.clone(), + current.sym_to_primitive.clone(), + current.sym_has_instance.clone(), + current.sym_match_all.clone(), + current.sym_async_iterator.clone(), + current.sym_to_string_tag.clone(), + current.sym_species.clone(), + current.sym_async_dispose.clone(), + current.sym_dispose.clone(), + ) + }; + + let math_object = if dirty.math { + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())) + } else { + current.math_object.clone() + }; + let json_object = if dirty.json { + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())) + } else { + current.json_object.clone() + }; + let (date_proto, date_constructor) = if dirty.date { + make_named_pair(string_forge, shape_forge, labels, "Date") + } else { + (current.date_proto.clone(), current.date_constructor.clone()) + }; + let (set_proto, set_constructor) = if dirty.set { + make_named_pair(string_forge, shape_forge, labels, "Set") + } else { + (current.set_proto.clone(), current.set_constructor.clone()) + }; + let (map_proto, map_constructor) = if dirty.map { + make_named_pair(string_forge, shape_forge, labels, "Map") + } else { + (current.map_proto.clone(), current.map_constructor.clone()) + }; + let (regexp_proto, regexp_constructor) = if dirty.regexp { + make_named_pair(string_forge, shape_forge, labels, "RegExp") + } else { + (current.regexp_proto.clone(), current.regexp_constructor.clone()) + }; + let (array_buffer_proto, array_buffer_constructor) = if dirty.array_buffer { + make_named_pair(string_forge, shape_forge, labels, "ArrayBuffer") + } else { + (current.array_buffer_proto.clone(), current.array_buffer_constructor.clone()) + }; + let (data_view_proto, data_view_constructor) = if dirty.data_view { + make_named_pair(string_forge, shape_forge, labels, "DataView") + } else { + (current.data_view_proto.clone(), current.data_view_constructor.clone()) + }; + let typed_arrays = if dirty.typed_array_family { + make_typed_array_family(string_forge, shape_forge, labels, &object_proto) + } else { + TypedArrayFamily { + typed_array_proto: current.typed_array_proto.clone(), + typed_array_constructor: current.typed_array_constructor.clone(), + int8array_constructor: current.int8array_constructor.clone(), + int8array_proto: current.int8array_proto.clone(), + uint8array_constructor: current.uint8array_constructor.clone(), + uint8array_proto: current.uint8array_proto.clone(), + uint8clampedarray_constructor: current.uint8clampedarray_constructor.clone(), + uint8clampedarray_proto: current.uint8clampedarray_proto.clone(), + int16array_constructor: current.int16array_constructor.clone(), + int16array_proto: current.int16array_proto.clone(), + uint16array_constructor: current.uint16array_constructor.clone(), + uint16array_proto: current.uint16array_proto.clone(), + int32array_constructor: current.int32array_constructor.clone(), + int32array_proto: current.int32array_proto.clone(), + uint32array_constructor: current.uint32array_constructor.clone(), + uint32array_proto: current.uint32array_proto.clone(), + float32array_constructor: current.float32array_constructor.clone(), + float32array_proto: current.float32array_proto.clone(), + float64array_constructor: current.float64array_constructor.clone(), + float64array_proto: current.float64array_proto.clone(), + bigint64array_constructor: current.bigint64array_constructor.clone(), + bigint64array_proto: current.bigint64array_proto.clone(), + biguint64array_constructor: current.biguint64array_constructor.clone(), + biguint64array_proto: current.biguint64array_proto.clone(), + } + }; + let ( + temporal_object, + temporal_now_object, + instant_proto, + instant_constructor, + plain_date_proto, + plain_date_constructor, + plain_time_proto, + plain_time_constructor, + duration_proto, + duration_constructor, + zoned_date_time_proto, + zoned_date_time_constructor, + plain_date_time_proto, + plain_date_time_constructor, + plain_month_day_proto, + plain_month_day_constructor, + plain_year_month_proto, + plain_year_month_constructor, + ) = if dirty.temporal { + let (instant_proto, instant_constructor) = make_named_pair(string_forge, shape_forge, labels, "Instant"); + let (plain_date_proto, plain_date_constructor) = + make_named_pair(string_forge, shape_forge, labels, "PlainDate"); + let (plain_time_proto, plain_time_constructor) = + make_named_pair(string_forge, shape_forge, labels, "PlainTime"); + let (duration_proto, duration_constructor) = make_named_pair(string_forge, shape_forge, labels, "Duration"); + let (zoned_date_time_proto, zoned_date_time_constructor) = + make_named_pair(string_forge, shape_forge, labels, "ZonedDateTime"); + let (plain_date_time_proto, plain_date_time_constructor) = + make_named_pair(string_forge, shape_forge, labels, "PlainDateTime"); + let (plain_month_day_proto, plain_month_day_constructor) = + make_named_pair(string_forge, shape_forge, labels, "PlainMonthDay"); + let (plain_year_month_proto, plain_year_month_constructor) = + make_named_pair(string_forge, shape_forge, labels, "PlainYearMonth"); + ( + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + instant_proto, + instant_constructor, + plain_date_proto, + plain_date_constructor, + plain_time_proto, + plain_time_constructor, + duration_proto, + duration_constructor, + zoned_date_time_proto, + zoned_date_time_constructor, + plain_date_time_proto, + plain_date_time_constructor, + plain_month_day_proto, + plain_month_day_constructor, + plain_year_month_proto, + plain_year_month_constructor, + ) + } else { + ( + current.temporal_object.clone(), + current.temporal_now_object.clone(), + current.instant_proto.clone(), + current.instant_constructor.clone(), + current.plain_date_proto.clone(), + current.plain_date_constructor.clone(), + current.plain_time_proto.clone(), + current.plain_time_constructor.clone(), + current.duration_proto.clone(), + current.duration_constructor.clone(), + current.zoned_date_time_proto.clone(), + current.zoned_date_time_constructor.clone(), + current.plain_date_time_proto.clone(), + current.plain_date_time_constructor.clone(), + current.plain_month_day_proto.clone(), + current.plain_month_day_constructor.clone(), + current.plain_year_month_proto.clone(), + current.plain_year_month_constructor.clone(), + ) + }; + let (bigint_proto, bigint_constructor) = if dirty.stubs { + make_named_pair(string_forge, shape_forge, labels, "BigInt") + } else { + (current.bigint_proto.clone(), current.bigint_constructor.clone()) + }; + let stub_objects = if dirty.stubs { Vec::new() } else { current.stub_objects.clone() }; + let console_object = if dirty.console { + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())) + } else { + current.console_object.clone() + }; + + // 迭代器原型与资源栈原型依赖 Object.prototype(链到其上):object 家族重建时 + // 一并重建,否则旧原型链指向已释放的 object_proto。 + let ( + iterator_proto, + array_iterator_proto, + map_iterator_proto, + set_iterator_proto, + string_iterator_proto, + regexp_string_iterator_proto, + iterator_helper_proto, + disposable_stack_proto, + async_disposable_stack_proto, + ) = if dirty.object { + ( + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())), + ) + } else { + ( + current.iterator_proto.clone(), + current.array_iterator_proto.clone(), + current.map_iterator_proto.clone(), + current.set_iterator_proto.clone(), + current.string_iterator_proto.clone(), + current.regexp_string_iterator_proto.clone(), + current.iterator_helper_proto.clone(), + current.disposable_stack_proto.clone(), + current.async_disposable_stack_proto.clone(), + ) + }; + + let world = BuiltinWorld { + object_proto, + string_default_iterator: std::cell::Cell::new(if dirty.string { + std::ptr::null() + } else { + current.string_default_iterator.get() + }), + array_proto, + function_proto, + string_proto, + number_proto, + boolean_proto, + error_proto, + symbol_proto, + object_constructor, + array_constructor, + function_constructor, + string_constructor, + number_constructor, + boolean_constructor, + error_constructor, + symbol_constructor, + type_error_proto: error_subtypes.type_error_proto, + reference_error_proto: error_subtypes.reference_error_proto, + range_error_proto: error_subtypes.range_error_proto, + syntax_error_proto: error_subtypes.syntax_error_proto, + uri_error_proto: error_subtypes.uri_error_proto, + eval_error_proto: error_subtypes.eval_error_proto, + suppressed_error_proto: error_subtypes.suppressed_error_proto, + math_object, + json_object, + date_constructor, + date_proto, + set_constructor, + set_proto, + map_constructor, + map_proto, + regexp_constructor, + regexp_proto, + array_buffer_constructor, + array_buffer_proto, + data_view_constructor, + data_view_proto, + typed_array_proto: typed_arrays.typed_array_proto, + typed_array_constructor: typed_arrays.typed_array_constructor, + int8array_constructor: typed_arrays.int8array_constructor, + int8array_proto: typed_arrays.int8array_proto, + uint8array_constructor: typed_arrays.uint8array_constructor, + uint8array_proto: typed_arrays.uint8array_proto, + uint8clampedarray_constructor: typed_arrays.uint8clampedarray_constructor, + uint8clampedarray_proto: typed_arrays.uint8clampedarray_proto, + int16array_constructor: typed_arrays.int16array_constructor, + int16array_proto: typed_arrays.int16array_proto, + uint16array_constructor: typed_arrays.uint16array_constructor, + uint16array_proto: typed_arrays.uint16array_proto, + int32array_constructor: typed_arrays.int32array_constructor, + int32array_proto: typed_arrays.int32array_proto, + uint32array_constructor: typed_arrays.uint32array_constructor, + uint32array_proto: typed_arrays.uint32array_proto, + float32array_constructor: typed_arrays.float32array_constructor, + float32array_proto: typed_arrays.float32array_proto, + float64array_constructor: typed_arrays.float64array_constructor, + float64array_proto: typed_arrays.float64array_proto, + bigint64array_constructor: typed_arrays.bigint64array_constructor, + bigint64array_proto: typed_arrays.bigint64array_proto, + biguint64array_constructor: typed_arrays.biguint64array_constructor, + biguint64array_proto: typed_arrays.biguint64array_proto, + sym_match, + sym_replace, + sym_search, + sym_split, + sym_iterator, + sym_to_primitive, + sym_has_instance, + sym_match_all, + sym_async_iterator, + sym_to_string_tag, + sym_species, + sym_async_dispose, + sym_dispose, + temporal_object, + temporal_now_object, + instant_constructor, + instant_proto, + plain_date_constructor, + plain_date_proto, + plain_time_constructor, + plain_time_proto, + duration_constructor, + duration_proto, + zoned_date_time_constructor, + zoned_date_time_proto, + plain_date_time_constructor, + plain_date_time_proto, + plain_month_day_constructor, + plain_month_day_proto, + plain_year_month_constructor, + plain_year_month_proto, + bigint_constructor, + bigint_proto, + iterator_proto, + array_iterator_proto, + map_iterator_proto, + set_iterator_proto, + string_iterator_proto, + regexp_string_iterator_proto, + iterator_helper_proto, + disposable_stack_proto, + async_disposable_stack_proto, + stub_objects, + console_object, + leaked_objects: std::cell::RefCell::new(Vec::new()), + }; + wire_builtin_world_links(&world); + world + } +}