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.
|
/// Canonical registry of all syscalls and their metadata.
|
||||||
///
|
///
|
||||||
/// IMPORTANT: This table is the single authoritative source for the slot-based
|
/// 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).
|
// 3) Table and explicit list sizes must match (guard against omissions).
|
||||||
assert_eq!(SYSCALL_TABLE.len(), all_syscalls().len());
|
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
|
# PR-5.5 — Remove Legacy Syscall Entry Paths
|
||||||
|
|
||||||
### Briefing
|
### Briefing
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user