Prevent instance attributes from shadowing typed capability methods

This commit is contained in:
2026-10-22 15:28:19 +08:00
parent c96dce118b
commit 0513b96dfb
7 changed files with 349 additions and 4 deletions
+1 -1
View File
@@ -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) | 合并变更批次、变更键与有界通知额度 |
+1
View File
@@ -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)?;
}
+7
View File
@@ -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
View File
@@ -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),
}
}