pr5.3.2
This commit is contained in:
parent
a10055f61b
commit
b9ac2a98ee
@ -212,6 +212,77 @@ impl SyscallIdentity {
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolved syscall information provided to the loader/VM.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct SyscallResolved {
|
||||
/// Numeric syscall id used at runtime (VM executes `SYSCALL <id>` only).
|
||||
pub id: u32,
|
||||
/// Associated metadata for verification/runtime checks.
|
||||
pub meta: SyscallMeta,
|
||||
}
|
||||
|
||||
/// Load-time error for syscall resolution.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LoadError {
|
||||
/// The (module, name, version) triple is not known by the host.
|
||||
UnknownSyscall {
|
||||
module: &'static str,
|
||||
name: &'static str,
|
||||
version: u16,
|
||||
},
|
||||
/// The cartridge lacks required capabilities for the syscall.
|
||||
MissingCapability {
|
||||
required: CapFlags,
|
||||
provided: CapFlags,
|
||||
module: &'static str,
|
||||
name: &'static str,
|
||||
version: u16,
|
||||
},
|
||||
}
|
||||
|
||||
/// Resolve a canonical syscall identity to its numeric id and metadata.
|
||||
///
|
||||
/// Returns `None` if the identity is unknown.
|
||||
pub fn resolve_syscall(module: &'static str, name: &'static str, version: u16) -> Option<SyscallResolved> {
|
||||
for entry in SYSCALL_TABLE {
|
||||
if entry.meta.module == module && entry.meta.name == name && entry.meta.version == version {
|
||||
return Some(SyscallResolved { id: entry.meta.id, meta: entry.meta });
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Resolve all declared program syscalls and enforce capability gating.
|
||||
///
|
||||
/// - `declared`: list of canonical identities required by the program.
|
||||
/// - `caps`: capabilities granted to the cartridge/program.
|
||||
///
|
||||
/// Fails deterministically if any syscall is unknown or requires missing caps.
|
||||
pub fn resolve_program_syscalls(
|
||||
declared: &[SyscallIdentity],
|
||||
caps: CapFlags,
|
||||
) -> Result<Vec<SyscallResolved>, LoadError> {
|
||||
let mut out = Vec::with_capacity(declared.len());
|
||||
for ident in declared {
|
||||
let Some(res) = resolve_syscall(ident.module, ident.name, ident.version) else {
|
||||
return Err(LoadError::UnknownSyscall { module: ident.module, name: ident.name, version: ident.version });
|
||||
};
|
||||
// Capability gating: required must be subset of provided
|
||||
let missing = res.meta.caps & !caps;
|
||||
if missing != 0 {
|
||||
return Err(LoadError::MissingCapability {
|
||||
required: res.meta.caps,
|
||||
provided: caps,
|
||||
module: ident.module,
|
||||
name: ident.name,
|
||||
version: ident.version,
|
||||
});
|
||||
}
|
||||
out.push(res);
|
||||
}
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// Canonical registry of all syscalls and their metadata.
|
||||
///
|
||||
/// IMPORTANT: This table is the single authoritative source for the slot-based
|
||||
@ -528,4 +599,55 @@ mod tests {
|
||||
// 3) Table and explicit list sizes must match (guard against omissions).
|
||||
assert_eq!(SYSCALL_TABLE.len(), all_syscalls().len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolver_returns_expected_id_for_known_identity() {
|
||||
// Pick a stable entry from the table
|
||||
let id = resolve_syscall("gfx", "clear", 1).expect("known identity must resolve");
|
||||
assert_eq!(id.id, 0x1001);
|
||||
assert_eq!(id.meta.module, "gfx");
|
||||
assert_eq!(id.meta.name, "clear");
|
||||
assert_eq!(id.meta.version, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolver_rejects_unknown_identity() {
|
||||
let res = resolve_syscall("gfx", "nonexistent", 1);
|
||||
assert!(res.is_none());
|
||||
|
||||
// And via the program-level resolver
|
||||
let requested = [SyscallIdentity { module: "gfx", name: "nonexistent", version: 1 }];
|
||||
let err = resolve_program_syscalls(&requested, 0).unwrap_err();
|
||||
match err {
|
||||
LoadError::UnknownSyscall { module, name, version } => {
|
||||
assert_eq!(module, "gfx");
|
||||
assert_eq!(name, "nonexistent");
|
||||
assert_eq!(version, 1);
|
||||
}
|
||||
_ => panic!("expected UnknownSyscall error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolver_enforces_capabilities() {
|
||||
// Choose a syscall that requires GFX caps.
|
||||
let requested = [SyscallIdentity { module: "gfx", name: "clear", version: 1 }];
|
||||
// Provide no caps → should fail.
|
||||
let err = resolve_program_syscalls(&requested, 0).unwrap_err();
|
||||
match err {
|
||||
LoadError::MissingCapability { required, provided, module, name, version } => {
|
||||
assert_eq!(module, "gfx");
|
||||
assert_eq!(name, "clear");
|
||||
assert_eq!(version, 1);
|
||||
assert_ne!(required, 0);
|
||||
assert_eq!(provided, 0);
|
||||
}
|
||||
_ => panic!("expected MissingCapability error"),
|
||||
}
|
||||
|
||||
// Provide correct caps → should pass.
|
||||
let ok = resolve_program_syscalls(&requested, caps::GFX).expect("must resolve with caps");
|
||||
assert_eq!(ok.len(), 1);
|
||||
assert_eq!(ok[0].id, 0x1001);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,53 +1,3 @@
|
||||
# PR-5.4 — Verifier Integration for Syscall Slot Rules
|
||||
|
||||
### Briefing
|
||||
|
||||
The verifier must ensure that syscall calls respect argument and return slot counts before runtime.
|
||||
|
||||
### Target
|
||||
|
||||
* Extend verifier to validate syscall usage.
|
||||
|
||||
### Work items
|
||||
|
||||
* At syscall call sites:
|
||||
|
||||
* Look up `SyscallMeta`.
|
||||
* Ensure enough argument slots are available.
|
||||
* Ensure stack shape after call matches `ret_slots`.
|
||||
* Emit verifier errors for mismatches.
|
||||
|
||||
### Acceptance checklist
|
||||
|
||||
* [ ] Verifier rejects incorrect syscall slot usage.
|
||||
* [ ] Correct programs pass.
|
||||
* [ ] Runtime traps are not required for verifier-detectable cases.
|
||||
* [ ] `cargo test` passes.
|
||||
|
||||
### Tests
|
||||
|
||||
* Add tests:
|
||||
|
||||
* Too few args for syscall → verifier error.
|
||||
* Correct args/returns → passes.
|
||||
|
||||
### Junie instructions
|
||||
|
||||
**You MAY:**
|
||||
|
||||
* Extend verifier with syscall checks.
|
||||
|
||||
**You MUST NOT:**
|
||||
|
||||
* Change runtime trap logic.
|
||||
* Add new trap categories.
|
||||
|
||||
**If unclear:**
|
||||
|
||||
* Ask before enforcing slot rules.
|
||||
|
||||
---
|
||||
|
||||
# PR-5.5 — Remove Legacy Syscall Entry Paths
|
||||
|
||||
### Briefing
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user