feat(kernel): implement generation-based capability revocation with proofs and tests
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
use super::*;
|
||||
use crate::{SyscallOutcome, SyscallResult};
|
||||
use domain::resource::Resource;
|
||||
use domain::syscall::Syscall;
|
||||
use types::bounded::CapSlot;
|
||||
use types::permission::PermissionSet;
|
||||
use types::permission::{Permission, PermissionSet};
|
||||
use types::address::DataRef;
|
||||
|
||||
#[test]
|
||||
@@ -53,3 +54,92 @@ fn delegate_with_valid_cap_succeeds() {
|
||||
SyscallOutcome::Complete(SyscallResult::Ok)
|
||||
));
|
||||
}
|
||||
|
||||
// --- Generation-based revocation ---
|
||||
|
||||
#[test]
|
||||
fn revoke_resource_invalidates_all_caps() {
|
||||
let (mut k, pid) = kernel_with_process();
|
||||
let ep_id = k.create_endpoint_for_test();
|
||||
|
||||
let cap_slot = k.grant_cap_for_test(
|
||||
pid,
|
||||
Resource::Endpoint(ep_id),
|
||||
PermissionSet::empty().with(Permission::Read),
|
||||
);
|
||||
|
||||
// Revoke the endpoint
|
||||
k.revoke_resource(&Resource::Endpoint(ep_id));
|
||||
|
||||
// Cap is now invalid — check_cap should reject with RevokedCapability
|
||||
let outcome = k.handle_syscall(Syscall::Receive { endpoint: cap_slot });
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
SyscallOutcome::Complete(SyscallResult::Error(domain::error::KernelError::RevokedCapability(_)))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_cap_after_revoke_uses_current_generation() {
|
||||
let (mut k, pid) = kernel_with_process();
|
||||
let ep_id = k.create_endpoint_for_test();
|
||||
|
||||
// Grant, revoke, grant again
|
||||
let old_slot = k.grant_cap_for_test(
|
||||
pid,
|
||||
Resource::Endpoint(ep_id),
|
||||
PermissionSet::empty().with(Permission::Read),
|
||||
);
|
||||
|
||||
k.revoke_resource(&Resource::Endpoint(ep_id));
|
||||
|
||||
// Old cap is dead
|
||||
let outcome = k.handle_syscall(Syscall::Receive { endpoint: old_slot });
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
SyscallOutcome::Complete(SyscallResult::Error(domain::error::KernelError::RevokedCapability(_)))
|
||||
));
|
||||
|
||||
// New cap with current generation works
|
||||
let new_slot = k.grant_cap_for_test(
|
||||
pid,
|
||||
Resource::Endpoint(ep_id),
|
||||
PermissionSet::empty().with(Permission::Read),
|
||||
);
|
||||
|
||||
let outcome = k.handle_syscall(Syscall::Receive { endpoint: new_slot });
|
||||
// Should block (no sender), not error — proving the cap is valid
|
||||
assert!(matches!(outcome, SyscallOutcome::BlockCaller(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn revoke_does_not_affect_other_resources() {
|
||||
let (mut k, pid) = kernel_with_process();
|
||||
let ep1 = k.create_endpoint_for_test();
|
||||
let ep2 = k.create_endpoint_for_test();
|
||||
|
||||
let slot1 = k.grant_cap_for_test(
|
||||
pid,
|
||||
Resource::Endpoint(ep1),
|
||||
PermissionSet::empty().with(Permission::Read),
|
||||
);
|
||||
let slot2 = k.grant_cap_for_test(
|
||||
pid,
|
||||
Resource::Endpoint(ep2),
|
||||
PermissionSet::empty().with(Permission::Read),
|
||||
);
|
||||
|
||||
// Revoke only ep1
|
||||
k.revoke_resource(&Resource::Endpoint(ep1));
|
||||
|
||||
// ep1 cap is revoked
|
||||
let outcome = k.handle_syscall(Syscall::Receive { endpoint: slot1 });
|
||||
assert!(matches!(
|
||||
outcome,
|
||||
SyscallOutcome::Complete(SyscallResult::Error(domain::error::KernelError::RevokedCapability(_)))
|
||||
));
|
||||
|
||||
// ep2 cap still works
|
||||
let outcome = k.handle_syscall(Syscall::Receive { endpoint: slot2 });
|
||||
assert!(matches!(outcome, SyscallOutcome::BlockCaller(_)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user