Prevent instance attributes from shadowing typed capability methods
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
| --- | --- |
|
||||
| [lib.rs](lib.rs) | 包边界、对象类型/ObjectStore 公开导出(含Trait/MethodRequirement/TypedMethodRequirement/SourceSignature/AnnotationType/CallArity/CallSignature/CallType/CallEffect/ReadAccess)与独立存储 rustdoc 示例 |
|
||||
| [object.rs](object.rs) | Value、CompareOp、ObjectPayload(含显式函数标记)、ObjectHeader、EndorObject、Record、可发布数量元数据的 NativeFunction |
|
||||
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式/类型化Trait定义、强类型签名边与声明约束、原生构造委托、统一源码/原生/绑定方法参数区间查询与能力验证、声明环境托管边、字段构造/写入检查与标记为校验读取的注解解析/匹配 |
|
||||
| [types.rs](types.rs) | 托管名义类型、显式 Object/Type 引导、继承查询、受管方法表/接收者绑定、类字段、显式/类型化Trait定义、强类型签名边与声明约束、实例方法遮蔽检查和宿主缓存依赖名、原生构造委托、统一源码/原生/绑定方法参数区间查询与能力验证、声明环境托管边、字段构造/写入检查与标记为校验读取的注解解析/匹配 |
|
||||
| [contracts.rs](contracts.rs) | 方法参数逆变/返回协变、名义/能力/联合包含关系、祖先方法更新的后代检查、动态注解调用前复查、类型化能力替换/返回约束与临时递归验证集合 |
|
||||
| [native.rs](native.rs) | NativeContext、CallArity/CallSignature 类型、Delegated/固定效果元数据、调用前检查、签名类型强边、上下文原生函数、作用域语言函数工厂、显式能力声明入口、函数标记、根与非递归委托调用 |
|
||||
| [changes.rs](changes.rs) | 合并变更批次、变更键与有界通知额度 |
|
||||
|
||||
@@ -391,6 +391,7 @@ impl ObjectStore {
|
||||
if *candidate != function {
|
||||
continue;
|
||||
}
|
||||
self.check_typed_instance_storage(receiver)?;
|
||||
if overrides {
|
||||
self.check_override(definition, name, function, None, false)?;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ pub struct ObjectStore {
|
||||
pub(crate) heap: Heap,
|
||||
function_type: Option<WeakObjectRef>,
|
||||
pub(crate) field_type_checks: bool,
|
||||
pub(crate) active_attribute_checks: std::sync::Mutex<
|
||||
std::collections::HashMap<std::thread::ThreadId, std::collections::BTreeSet<ObjectRef>>,
|
||||
>,
|
||||
pub(crate) active_trait_checks: std::sync::Mutex<
|
||||
std::collections::HashMap<
|
||||
std::thread::ThreadId,
|
||||
@@ -157,6 +160,7 @@ impl ObjectStore {
|
||||
heap: Heap::new()?,
|
||||
function_type: None,
|
||||
field_type_checks: false,
|
||||
active_attribute_checks: std::sync::Mutex::new(std::collections::HashMap::new()),
|
||||
active_trait_checks: std::sync::Mutex::new(std::collections::HashMap::new()),
|
||||
})
|
||||
}
|
||||
@@ -312,6 +316,9 @@ impl ObjectStore {
|
||||
if self.field_type_checks {
|
||||
self.check_field_write(reference, &name, &value)?;
|
||||
}
|
||||
for object in self.attribute_path(reference)? {
|
||||
self.check_typed_method_write(object, &name)?;
|
||||
}
|
||||
let storage = *self
|
||||
.attribute_path(reference)?
|
||||
.last()
|
||||
|
||||
+166
-2
@@ -8,6 +8,30 @@ use crate::{
|
||||
VmError,
|
||||
};
|
||||
|
||||
// A host type_target may read its receiver's raw attributes. Validate the
|
||||
// outer lookup after resolution without recursively re-entering its guard.
|
||||
struct AttributeCheckGuard<'a> {
|
||||
store: &'a ObjectStore,
|
||||
thread: std::thread::ThreadId,
|
||||
object: ObjectRef,
|
||||
}
|
||||
impl Drop for AttributeCheckGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
let mut checks = self
|
||||
.store
|
||||
.active_attribute_checks
|
||||
.lock()
|
||||
.expect("attribute checker poisoned");
|
||||
let active = checks
|
||||
.get_mut(&self.thread)
|
||||
.expect("active attribute check");
|
||||
active.remove(&self.object);
|
||||
if active.is_empty() {
|
||||
checks.remove(&self.thread);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum TypeConstructor {
|
||||
/// Allocate a plain, attribute-bearing instance of this type.
|
||||
@@ -227,6 +251,9 @@ impl ObjectStore {
|
||||
type_object: ObjectRef,
|
||||
) -> Result<(), VmError> {
|
||||
self.payload::<EndorTypeObject>(type_object, "Type")?;
|
||||
for capability in self.inherited_traits(type_object)? {
|
||||
self.check_capability_storage(object, capability)?;
|
||||
}
|
||||
self.heap.attach_type(object, type_object)
|
||||
}
|
||||
|
||||
@@ -541,6 +568,7 @@ impl ObjectStore {
|
||||
if name == "name"
|
||||
&& let Some(capability) = object.payload().as_any().downcast_ref::<EndorTraitObject>()
|
||||
{
|
||||
self.check_typed_method_write(receiver, name)?;
|
||||
return Ok(AttributeLookup::Value(Value::Text(capability.name.clone())));
|
||||
}
|
||||
if let Some(definition) = object.payload().as_any().downcast_ref::<EndorTypeObject>() {
|
||||
@@ -550,9 +578,11 @@ impl ObjectStore {
|
||||
_ => None,
|
||||
};
|
||||
if let Some(value) = metadata {
|
||||
self.check_typed_method_write(receiver, name)?;
|
||||
return Ok(AttributeLookup::Value(value));
|
||||
}
|
||||
if let Some(function) = self.lookup_method(receiver, name)? {
|
||||
self.check_typed_method_write(receiver, name)?;
|
||||
return Ok(AttributeLookup::Value(Value::Object(function)));
|
||||
}
|
||||
}
|
||||
@@ -561,7 +591,10 @@ impl ObjectStore {
|
||||
.last()
|
||||
.expect("nonempty attribute path");
|
||||
match self.heap.get_attr(storage, name) {
|
||||
Ok(value) => return Ok(AttributeLookup::Value(value)),
|
||||
Ok(value) => {
|
||||
self.check_typed_method_write(receiver, name)?;
|
||||
return Ok(AttributeLookup::Value(value));
|
||||
}
|
||||
Err(VmError::AttributeNotFound(_) | VmError::UnsupportedOperation("attributes")) => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
@@ -899,6 +932,21 @@ impl ObjectStore {
|
||||
for descendant in self.descendant_types(definition)? {
|
||||
self.validate_trait(descendant, capability, false)?;
|
||||
}
|
||||
// Existing objects must satisfy the same storage invariant as new writes.
|
||||
// Borrowed heap enumeration adds no owning instance registry.
|
||||
if !self
|
||||
.payload::<EndorTraitObject>(capability, "Trait")?
|
||||
.signatures
|
||||
.is_empty()
|
||||
{
|
||||
for object in self.heap.live_references() {
|
||||
if let Some(actual) = self.type_of(object)?
|
||||
&& self.is_subtype(actual, definition)?
|
||||
{
|
||||
self.check_capability_storage(object, capability)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut updated = self.payload::<EndorTypeObject>(definition, "Type")?.clone();
|
||||
if !updated.traits.contains(&capability) {
|
||||
updated.traits.push(capability);
|
||||
@@ -918,10 +966,126 @@ impl ObjectStore {
|
||||
}
|
||||
Ok(declared)
|
||||
}
|
||||
/// Typed capability methods belong to the method table. Instance fields
|
||||
/// cannot replace them independently of dispatch through that table.
|
||||
pub(crate) fn check_typed_method_write(
|
||||
&self,
|
||||
object: ObjectRef,
|
||||
name: &str,
|
||||
) -> Result<(), VmError> {
|
||||
let thread = std::thread::current().id();
|
||||
if !self
|
||||
.active_attribute_checks
|
||||
.lock()
|
||||
.expect("attribute checker poisoned")
|
||||
.entry(thread)
|
||||
.or_default()
|
||||
.insert(object)
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
let _guard = AttributeCheckGuard {
|
||||
store: self,
|
||||
thread,
|
||||
object,
|
||||
};
|
||||
if let Some(definition) = self.type_of(object)? {
|
||||
for capability in self.inherited_traits(definition)? {
|
||||
let contract = self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
if contract.signatures.contains_key(name) {
|
||||
return Err(VmError::TraitViolation {
|
||||
capability: contract.name.clone(),
|
||||
method: name.into(),
|
||||
reason: "instance attribute shadows a typed capability method",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check_capability_storage(
|
||||
&self,
|
||||
object: ObjectRef,
|
||||
capability: ObjectRef,
|
||||
) -> Result<(), VmError> {
|
||||
let contract = self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
if contract.signatures.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let storage = *self
|
||||
.attribute_path(object)?
|
||||
.last()
|
||||
.expect("nonempty attribute path");
|
||||
for name in contract.signatures.keys() {
|
||||
let payload = self.object(object)?.payload();
|
||||
let intrinsic = if payload.as_any().is::<EndorTypeObject>() {
|
||||
name == "name" || name == "base" || self.lookup_method(object, name)?.is_some()
|
||||
} else {
|
||||
payload.as_any().is::<EndorTraitObject>() && name == "name"
|
||||
};
|
||||
if intrinsic {
|
||||
return Err(VmError::TraitViolation {
|
||||
capability: contract.name.clone(),
|
||||
method: name.clone(),
|
||||
reason: "intrinsic attribute shadows a typed capability method",
|
||||
});
|
||||
}
|
||||
match self.heap.get_attr(storage, name) {
|
||||
Ok(_) => {
|
||||
return Err(VmError::TraitViolation {
|
||||
capability: contract.name.clone(),
|
||||
method: name.clone(),
|
||||
reason: "instance attribute shadows a typed capability method",
|
||||
});
|
||||
}
|
||||
Err(
|
||||
VmError::AttributeNotFound(_) | VmError::UnsupportedOperation("attributes"),
|
||||
) => {}
|
||||
Err(error) => return Err(error),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Method names whose instance storage participates in typed capability
|
||||
/// validity. Hosts can subscribe to these attribute keys when caching.
|
||||
pub fn typed_method_names(&self, object: ObjectRef) -> Result<Vec<String>, VmError> {
|
||||
let mut names = std::collections::BTreeSet::new();
|
||||
if let Some(definition) = self.type_of(object)? {
|
||||
for capability in self.inherited_traits(definition)? {
|
||||
names.extend(
|
||||
self.payload::<EndorTraitObject>(capability, "Trait")?
|
||||
.signatures
|
||||
.keys()
|
||||
.cloned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(names.into_iter().collect())
|
||||
}
|
||||
|
||||
/// Validate instance storage before host dispatch that bypasses attribute
|
||||
/// lookup. Raw storage access is trusted but cannot certify conformance.
|
||||
pub fn check_typed_instance_storage(&self, object: ObjectRef) -> Result<(), VmError> {
|
||||
if let Some(definition) = self.type_of(object)? {
|
||||
for capability in self.inherited_traits(definition)? {
|
||||
self.check_capability_storage(object, capability)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn supports(&self, object: ObjectRef, capability: ObjectRef) -> Result<bool, VmError> {
|
||||
self.payload::<EndorTraitObject>(capability, "Trait")?;
|
||||
match self.type_of(object)? {
|
||||
Some(definition) => self.implements_trait(definition, capability),
|
||||
Some(definition) => {
|
||||
let declared = self.implements_trait(definition, capability)?;
|
||||
if declared {
|
||||
self.check_capability_storage(object, capability)?;
|
||||
}
|
||||
Ok(declared)
|
||||
}
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user