Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CHERIoT support #156

Merged
merged 2 commits into from
Jan 30, 2025
Merged

Add CHERIoT support #156

merged 2 commits into from
Jan 30, 2025

Conversation

PhilDay-CT
Copy link
Contributor

This PR adds support for CHERIoT, an RTOS designed to provide hardware based memory safety on embedded systems using Capability Hardware Enhanced RISC Instructions

It was developed by Configured Things with funding from the Digital Security by Design program.

while (ebits < 256) {
uint32_t r = rand_32();

//delay(10);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this delay necessary, or is that leftover debugging code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good spot - not needed, I'll take it out

@@ -14,6 +14,10 @@ static int errno;
# include <string.h>
#endif

#if defined (__CHERIOT__)
static int errno;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come <errno.h> exists (since you didn't add checks around its inclusion and usage of constants such as EINTR) but doesn't define errno? Isn't it available as a global?

Copy link
Contributor Author

@PhilDay-CT PhilDay-CT Jan 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see why that would be confusing. CHERIoT is designed to have a very strong compartment model where each compartment is in effect a separate memory domain. When running in a compartment a thread can access only the globals and stack space of that compartment. When making a call from one compartment to another all data has to be explicitly passed in and returned via the stack, and the called compartment has no access to stack of the compartment that called it. If there is a failure inside a compartment, such as an invalid memory access, then the impacts of that are guaranteed to be fully contained to the compartment that failed, and everything in the calling compartment is safe. This lets us, for example, wrap libhydrogen in a compartment and be sure without having to inspect the code, that even if there was some path that could lead to buffer overrun it could never affect anything else in the system. We still have an errno.h because we use some of the enumeration values as return values from cross-compartment calls, but don't have a system wide errno var as that would break the memory mode. Declaring errno it here in effect creates a global just within each compartment that includes libhydrogen.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it. libhydrogen only sets errno in codecs, and applications usually just check the return value of the functions anyway. So, not a big deal.

@davidchisnall
Copy link

To add a bit more context:

CHERIoT splits a firmware image into compartments, which can invoke others with function calls but which define a hard security boundary (the caller and callee share state only when it’s explicitly passed as an argument or returned, the callee has access to a truncated stack, the callee can validate pointers, the caller can enforce bounds and permissions on passed pointers).

Compartments are stateful. They have code and both mutable and immutable global state. The expose zero or more functions for other compartments to call and import zero or more functions from others. All of this is visible in an audit log.

For code reuse we also have a notion of a shared library. Shared libraries are stateless: they have no mutable global state and so their invocations happen in the security context of the caller. Invoking a library function is equivalent, from a security perspective, to copying and pasting it into your compartment, but if two compartments do this the code is shared.

Almost all of libhydrogen fits the model of a shared library. The only exception is the random number generation.

For an ideal integration, we’d like to be able to either build the random parts as a compartment and the rest as a shared library, or provide the random number state as an explicit argument to things that need it and build the whole thing as a shared library and then wrap the random-number code in a compartment that maintained a protected state. Would a refactoring to enable either of these use cases be acceptable?

For a lot of use cases, we’d also want to wrap the encryption code in a compartment that validated arguments and handled keys as sealed types (so the caller has no access to them and so that they show up in the audit logs) but that doesn’t require any modification to libhydrogen itself.

@jedisct1
Copy link
Owner

Would a refactoring to enable either of these use cases be acceptable?

I'd rather avoid breaking API changes as much as possible, especially when they would make usage more complicated, while not providing benefits to other platforms.

@jedisct1 jedisct1 merged commit 59bcee2 into jedisct1:master Jan 30, 2025
6 checks passed
@davidchisnall
Copy link

I wouldn't expect usage for other platforms to be more complicated and I would think that other platforms would benefit from separating the stateful and stateless parts, but perhaps I'm just optimistic.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants