feat(storage): scoped grants (subtree, ro/rw), narrow-only, release revokes all holders; IPC KEEP_DELEGATE opt-in; shell cd/pwd, programs get ro cwd

This commit is contained in:
2026-10-27 20:20:34 +01:00
parent c603a578c0
commit 1fc5a54f06
38 changed files with 1237 additions and 170 deletions

View File

@@ -28,9 +28,16 @@ fn permissions(r: usize) -> Option<PermissionSet> {
/// The message in `a1..a5`.
fn message(a: &[usize; 6]) -> Option<Message> {
let msg = Message::from_words([a[1], a[2], a[3], a[4]]);
Some(match reg_slot(a[5])? {
Some(match reg_give(a[5])? {
None => msg,
Some(cap) => msg.with_cap(cap),
Some(Give {
slot,
keep_delegate: false,
}) => msg.with_cap(slot),
Some(Give {
slot,
keep_delegate: true,
}) => msg.with_cap(slot).keeping_delegate(),
})
}

View File

@@ -39,12 +39,18 @@ fn send_roundtrips() {
} else {
None
};
let regs = message_regs(ep.value() as usize, words, cap);
let keep: bool = kani::any();
let give = cap.map(|slot| Give {
slot,
keep_delegate: keep,
});
let regs = message_regs(ep.value() as usize, words, give);
match decode(SYS_SEND, &regs) {
Some(Syscall::Send { endpoint, message }) => {
assert!(endpoint == ep);
assert!(*message.words() == words);
assert!(message.cap() == cap);
assert!(message.keeps_delegate() == (keep && cap.is_some()));
}
_ => panic!("valid send did not decode"),
}

View File

@@ -3,7 +3,7 @@ use domain::capability::{Capability, CapabilityTable};
use domain::resource::Resource;
use types::generation::Generation;
use types::id::EndpointId;
use types::permission::{Permission, PermissionSet};
use types::permission::PermissionSet;
#[kani::proof]
fn revoke_invalidates_cap_slot() {

View File

@@ -38,23 +38,23 @@ impl<P: Platform> Kernel<P> {
pub(super) fn transfer_cap(
&mut self,
from: ProcessId,
slot: Option<CapSlot>,
message: &Message,
grant: bool,
to: ProcessId,
) -> Option<CapSlot> {
let slot = slot?;
let slot = message.cap()?;
if !grant {
return None;
}
// The receiver gets the capability but not the right to pass it
// on; a sender that wants that uses Delegate with explicit rights.
// on, unless the sender says so; it can only give what it holds.
let source = self.check_cap(from, slot, Permission::Delegate).ok()?;
let copy = Capability::new(
*source.resource(),
source.permissions().without(Permission::Delegate),
*source.generation(),
)
.with_badge(source.badge());
let permissions = match message.keeps_delegate() {
true => *source.permissions(),
false => source.permissions().without(Permission::Delegate),
};
let copy = Capability::new(*source.resource(), permissions, *source.generation())
.with_badge(source.badge());
let parent = self.cap_ref(from, slot);
self.insert_derived(to, copy, parent).ok()
}
@@ -71,7 +71,7 @@ impl<P: Platform> Kernel<P> {
let from = self.thread_process(sender);
let to = self.thread_process(receiver);
let cap = match (from, to) {
(Some(from), Some(to)) => self.transfer_cap(from, out.message.cap(), out.grant, to),
(Some(from), Some(to)) => self.transfer_cap(from, &out.message, out.grant, to),
_ => None,
};
if kind == DeliveryKind::Call {
@@ -234,7 +234,7 @@ impl<P: Platform> Kernel<P> {
}
let to = self.thread_process(caller.tid);
// A reply may always carry a capability the server can delegate.
let cap = to.and_then(|to| self.transfer_cap(pid, message.cap(), true, to));
let cap = to.and_then(|to| self.transfer_cap(pid, &message, true, to));
let delivery = Delivery {
kind: DeliveryKind::Message,
badge: 0,

View File

@@ -917,6 +917,67 @@ fn a_received_cap_cannot_be_passed_on() {
));
}
/// What `b` holds in `slot`, by permission.
fn rights_of(t: &Ipc, pid: ProcessId, slot: CapSlot) -> PermissionSet {
*t.k.processes
.get(pid)
.unwrap()
.capabilities()
.get(slot)
.unwrap()
.permissions()
}
#[test]
fn a_sender_can_let_the_receiver_pass_a_cap_on() {
let mut t = Ipc::new();
let (a, b, ta, tb, a_send, b_recv) = (t.a, t.b, t.ta, t.tb, t.a_send, t.b_recv);
let other = t.k.create_endpoint(a).unwrap();
let gift =
t.k.grant_cap_for_test(a, Resource::Endpoint(other), perms(SEND_GRANT));
t.run(
ta,
Syscall::Send {
endpoint: a_send,
message: msg([0; 4]).with_cap(gift).keeping_delegate(),
},
);
let d = delivered(&t.run(tb, Syscall::Receive { endpoint: b_recv })).unwrap();
let got = d.cap.expect("the cap arrives");
assert_eq!(
rights_of(&t, b, got),
rights_of(&t, a, gift),
"with exactly the sender's rights"
);
let mint = t.run(
tb,
Syscall::Mint {
slot: got,
badge: Badge::new(1),
permissions: perms(SEND),
},
);
assert!(error(&mint).is_none(), "so it can derive from it");
}
#[test]
fn keeping_delegate_cannot_give_what_the_sender_lacks() {
let mut t = Ipc::new();
let (a, ta, tb, a_send, b_recv) = (t.a, t.ta, t.tb, t.a_send, t.b_recv);
let other = t.k.create_endpoint(a).unwrap();
let gift =
t.k.grant_cap_for_test(a, Resource::Endpoint(other), perms(SEND));
t.run(
ta,
Syscall::Send {
endpoint: a_send,
message: msg([0; 4]).with_cap(gift).keeping_delegate(),
},
);
let d = delivered(&t.run(tb, Syscall::Receive { endpoint: b_recv })).unwrap();
assert_eq!(d.cap, None, "a cap without Delegate never travels");
}
#[test]
fn an_irq_has_one_owner_until_it_dies() {
let mut t = Ipc::new();