- caps record parent (mint/delegate/ipc), CapRef w/ pid+slot gens - Revoke = delete subtree, keep self; drop/death re-parents children - CreateRegion/MapRegion; mapping lives only as long as its cap - drop Permission::Revoke + object-wide revoke - mint any cap (badge only ep/ntfn), process caps delegable - kani: descendants == subtree; model-based fuzz vs spec
192 lines
5.5 KiB
Rust
192 lines
5.5 KiB
Rust
use super::*;
|
|
use types::generation::Generation;
|
|
use types::id::{EndpointId, RegionId};
|
|
use types::permission::{Permission, PermissionSet};
|
|
|
|
fn perms(permissions: &[Permission]) -> PermissionSet {
|
|
permissions
|
|
.iter()
|
|
.fold(PermissionSet::empty(), |set, &p| set.with(p))
|
|
}
|
|
|
|
fn endpoint_cap(id: u16, permissions: &[Permission]) -> Capability {
|
|
Capability::new(
|
|
Resource::Endpoint(EndpointId::from_raw(id)),
|
|
perms(permissions),
|
|
Generation::initial(),
|
|
)
|
|
}
|
|
|
|
fn memory_cap(id: u16, permissions: &[Permission]) -> Capability {
|
|
Capability::new(
|
|
Resource::Memory(RegionId::from_raw(id)),
|
|
perms(permissions),
|
|
Generation::initial(),
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn new_table_is_empty() {
|
|
let table = CapabilityTable::new();
|
|
let slot = CapSlot::new(0).unwrap();
|
|
assert!(table.get(slot).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn insert_and_get() {
|
|
let mut table = CapabilityTable::new();
|
|
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
|
let retrieved = table.get(slot).unwrap();
|
|
|
|
assert!(retrieved.permissions().allows(Permission::Read));
|
|
assert!(!retrieved.permissions().allows(Permission::Write));
|
|
}
|
|
|
|
#[test]
|
|
fn remove_then_get_fails() {
|
|
let mut table = CapabilityTable::new();
|
|
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
|
table.remove(slot).unwrap();
|
|
assert!(table.get(slot).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn remove_invalid_slot_fails() {
|
|
let mut table = CapabilityTable::new();
|
|
let slot = CapSlot::new(5).unwrap();
|
|
|
|
assert!(table.remove(slot).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn insert_reuses_removed_slot() {
|
|
let mut table = CapabilityTable::new();
|
|
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
|
table.remove(slot).unwrap();
|
|
let new_slot = table.insert(endpoint_cap(2, &[Permission::Write])).unwrap();
|
|
assert_eq!(slot, new_slot);
|
|
}
|
|
|
|
#[test]
|
|
fn full_table_returns_no_free_slot() {
|
|
let mut table = CapabilityTable::new();
|
|
for i in 0..MAX_CAPS_PER_PROCESS {
|
|
assert!(
|
|
table
|
|
.insert(endpoint_cap(i as u16, &[Permission::Read]))
|
|
.is_ok()
|
|
);
|
|
}
|
|
assert!(table.insert(endpoint_cap(999, &[])).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn generation_mismatch_detected() {
|
|
let gen_v1 = Generation::initial();
|
|
let gen_v2 = gen_v1.next().unwrap();
|
|
|
|
assert!(gen_v1.is_valid_against(&gen_v1));
|
|
assert!(!gen_v1.is_valid_against(&gen_v2));
|
|
}
|
|
|
|
#[test]
|
|
fn restrict_to_never_escalates() {
|
|
let full = perms(&[Permission::Read, Permission::Write, Permission::Execute]);
|
|
let read_only = perms(&[Permission::Read]);
|
|
|
|
let restricted = full.restrict_to(&read_only);
|
|
|
|
assert!(restricted.allows(Permission::Read));
|
|
assert!(!restricted.allows(Permission::Write));
|
|
assert!(!restricted.allows(Permission::Execute));
|
|
}
|
|
|
|
#[test]
|
|
fn restrict_to_cannot_add_permissions() {
|
|
let read_only = perms(&[Permission::Read]);
|
|
let wants_everything = perms(&[
|
|
Permission::Read,
|
|
Permission::Write,
|
|
Permission::Execute,
|
|
Permission::Delegate,
|
|
]);
|
|
|
|
let result = read_only.restrict_to(&wants_everything);
|
|
|
|
assert!(result.allows(Permission::Read));
|
|
assert!(!result.allows(Permission::Write));
|
|
assert!(!result.allows(Permission::Execute));
|
|
assert!(!result.allows(Permission::Delegate));
|
|
}
|
|
|
|
#[test]
|
|
fn is_subset_of() {
|
|
let rw = perms(&[Permission::Read, Permission::Write]);
|
|
let read_only = perms(&[Permission::Read]);
|
|
|
|
assert!(read_only.is_subset_of(&rw));
|
|
assert!(!rw.is_subset_of(&read_only));
|
|
}
|
|
|
|
#[test]
|
|
fn empty_permission_set_allows_nothing() {
|
|
let empty = PermissionSet::empty();
|
|
|
|
assert!(!empty.allows(Permission::Read));
|
|
assert!(!empty.allows(Permission::Write));
|
|
assert!(!empty.allows(Permission::Execute));
|
|
assert!(!empty.allows(Permission::Delegate));
|
|
}
|
|
|
|
#[test]
|
|
fn capability_preserves_resource_and_permissions() {
|
|
let permissions = [Permission::Read, Permission::Write];
|
|
let expected_permissions = perms(&permissions);
|
|
let cap = memory_cap(42, &permissions);
|
|
|
|
assert_eq!(cap.permissions(), &expected_permissions);
|
|
assert_eq!(cap.generation(), &Generation::initial());
|
|
}
|
|
|
|
#[test]
|
|
fn removing_a_cap_changes_its_slot_generation() {
|
|
let mut table = CapabilityTable::new();
|
|
let slot = table.insert(endpoint_cap(1, &[Permission::Read])).unwrap();
|
|
let before = table.slot_generation(slot).unwrap();
|
|
table.remove(slot).unwrap();
|
|
assert_eq!(
|
|
table.slot_generation(slot),
|
|
None,
|
|
"empty slots have no generation"
|
|
);
|
|
let again = table.insert(endpoint_cap(2, &[Permission::Read])).unwrap();
|
|
assert_eq!(again, slot);
|
|
assert!(!before.is_valid_against(&table.slot_generation(again).unwrap()));
|
|
}
|
|
|
|
#[test]
|
|
fn iter_lists_occupied_slots() {
|
|
let mut table = CapabilityTable::new();
|
|
let a = table.insert(endpoint_cap(1, &[])).unwrap();
|
|
let b = table.insert(endpoint_cap(2, &[])).unwrap();
|
|
table.remove(a).unwrap();
|
|
let slots: [Option<types::bounded::CapSlot>; 2] = {
|
|
let mut it = table.iter().map(|(s, _)| s);
|
|
[it.next(), it.next()]
|
|
};
|
|
assert_eq!(slots, [Some(b), None]);
|
|
}
|
|
|
|
#[test]
|
|
fn caps_record_their_parent() {
|
|
let parent = CapRef {
|
|
process: types::id::ProcessId::from_raw(3),
|
|
process_generation: Generation::initial(),
|
|
slot: types::bounded::CapSlot::new(4).unwrap(),
|
|
slot_generation: Generation::initial(),
|
|
};
|
|
let cap = endpoint_cap(1, &[]).with_parent(Some(parent));
|
|
assert_eq!(cap.parent(), Some(parent));
|
|
assert_eq!(endpoint_cap(1, &[]).parent(), None);
|
|
}
|