Compare commits

...

1 Commits

Author SHA1 Message Date
Matthew Brecknell
c3d77ea0b3 MCS: Restrict reply grant rights
In MCS configurations, a server's ability to grant capabilities when
replying to a call were previously determined entirely by the grant
right on the reply capability the server supplied when calling
`receiveIPC`. If the server had sufficient untyped memory, it could
create arbitrary reply capabilities, giving itself the ability to grant
to its clients. Consequently, the only way to restrict a server's
ability to grant to its clients was to avoid giving it untyped memory.
This would break the expected authority confinement property.

Contrast this with non-MCS configurations, where the grant rights on a
server's receive endpoint control the server's ability to grant
capabilities when replying to calls. This makes it much easier to
restrict a server's ability to grant when replying.

This commit makes MCS configurations behave more like non-MCS
configurations: In order to grant capabilities when replying to a call,
a server must have both a grant right on the receive endpoint capability
when it calls `receiveIPC` and also a grant right on the reply
capability when it replies.

This came up in a conversation with @kent-mcleod. This is the first
alternative discussed in seL4 RFC-13:

https://sel4.atlassian.net/browse/RFC-13

Signed-off-by: Matthew Brecknell <matt@kry10.com>
2024-07-22 17:51:23 +10:00
6 changed files with 24 additions and 5 deletions

View File

@@ -27,6 +27,12 @@ description indicates whether it is SOURCE-COMPATIBLE, BINARY-COMPATIBLE, or BRE
* Added `zynqmp` and `rpi4` to the set of verified AArch64 configs.
## MCS
* To grant capabilities when replying to a call in MCS configurations, it is now necessary to have grant rights on both
the reply cap and the endpoint cap used to receive the call. Previously, the grant right on the endpoint cap was
ignored in MCS configurations.
### Upgrade Notes
---

View File

@@ -393,8 +393,13 @@ struct reply {
* context was passed along the call chain */
call_stack_t replyNext;
/* Unused, explicit padding to make struct size the correct power of 2. */
word_t padding;
/* If replyTCB is non-NULL, then canGrant indicates whether there was a grant right on the
* receiver's endpoint cap when the receiver called receiveIPC with a capability to this reply.
* This is the case whether replyTCB indicates the receiver waiting for a caller, or a caller
* waiting for a reply. The value of canGrant is undefined when replyTCB is NULL. In order to
* grant through a reply, the replier must have both a grant right on the reply cap, and a
* grant right on the endpoint cap used to receive the call. */
bool_t canGrant;
};
#endif

View File

@@ -260,8 +260,10 @@ directly addressed with any CPtr as it is not part of any CSpace.
A reply capability is invoked in the same way as a normal send on a
\obj{Endpoint}. A reply capability has implicitly the Write right, so the
message will always go through. Transferring caps in the reply can only happen
if the reply capability has the Grant right and is done in exactly the same way
as in a normal IPC transfer as described in \autoref{sec:cap-transfer}.
if the receive endpoint capability had the Grant right at the time
\apifunc{seL4\_Recv} was called, and if the Grant right on the reply capability
has not since been removed. The capability transfer is performed as in normal
IPC transfer as described in \autoref{sec:cap-transfer}.
The main difference with a normal endpoint transfer is that the kernel guarantees
that invoking a reply capability cannot block: If you own a reply capability,

View File

@@ -175,6 +175,7 @@ void receiveIPC(tcb_t *thread, cap_t cap, bool_t isBlocking)
thread_state_ptr_set_replyObject(&thread->tcbState, REPLY_REF(replyPtr));
if (replyPtr) {
replyPtr->replyTCB = thread;
replyPtr->canGrant = cap_endpoint_cap_get_capCanGrant(cap);
}
#else
thread_state_ptr_set_blockingIPCCanGrant(
@@ -248,6 +249,7 @@ void receiveIPC(tcb_t *thread, cap_t cap, bool_t isBlocking)
if ((canGrant || canGrantReply) && replyPtr != NULL) {
bool_t canDonate = sender->tcbSchedContext != NULL
&& seL4_Fault_get_seL4_FaultType(sender->tcbFault) != seL4_Fault_Timeout;
replyPtr->canGrant = cap_endpoint_cap_get_capCanGrant(cap);
reply_push(sender, thread, replyPtr, canDonate);
} else {
setThreadState(sender, ThreadState_Inactive);

View File

@@ -814,7 +814,7 @@ exception_t performInvocation_Notification(notification_t *ntfn, word_t badge)
#ifdef CONFIG_KERNEL_MCS
exception_t performInvocation_Reply(tcb_t *thread, reply_t *reply, bool_t canGrant)
{
doReplyTransfer(thread, reply, canGrant);
doReplyTransfer(thread, reply, canGrant && reply->canGrant);
return EXCEPTION_NONE;
}
#else

View File

@@ -27,6 +27,10 @@ void reply_push(tcb_t *tcb_caller, tcb_t *tcb_callee, reply_t *reply, bool_t can
/* link caller and reply */
reply->replyTCB = tcb_caller;
/* canGrant should have already been set according to the grant
right on the receiver's endpoint capability. */
thread_state_ptr_set_replyObject(&tcb_caller->tcbState, REPLY_REF(reply));
setThreadState(tcb_caller, ThreadState_BlockedOnReply);