CHERI and Rust
Rust prevents many memory errors in safe code, while CHERI can constrain unsafe code, native libraries, runtimes, and compartments beneath it.
If Rust already prevents many memory-safety bugs, where does CHERI help? Real systems contain more than safe Rust. They also include unsafe blocks, C libraries, operating systems, device drivers, language runtimes, hardware interfaces, and components with different levels of trust.
Rust and CHERI address overlapping problems at different layers. They are strongest when used together, not treated as alternatives.
What safe Rust provides
Rust’s ownership and borrowing rules let the compiler check how references are created and used. In safe Rust, these rules prevent many problems including use-after-free, invalid aliasing, null dereferences, and data races.
The checks happen mainly at compile time, so safe Rust normally avoids the runtime cost associated with garbage collection or pervasive bounds metadata. Arrays and slices still use runtime bounds checks where needed.
This makes Rust a strong choice for new systems code when its platform and library ecosystem meet the project’s needs.
Where the language boundary ends
Rust permits unsafe code because some low-level operations cannot be expressed or verified through the safe subset. An unsafe block may dereference a raw pointer, access hardware registers, call a C interface, or implement a data structure that presents a safe application programming interface (API).
unsafe does not disable the compiler or mean that anything is allowed. It means the programmer is responsible for upholding additional rules that the compiler cannot check. A mistake can still cause memory corruption.
Safe Rust may also depend on:
- an operating-system kernel written largely in C
- a memory allocator containing unsafe code
- C or C++ libraries reached through a foreign-function interface
- firmware and drivers outside the Rust component
- a compiler and runtime that form part of the trusted computing base
The whole system is only as well protected as these boundaries allow.
What CHERI adds
CHERI checks memory authority in hardware when an access occurs. A raw pointer represented by a capability can still be constrained by bounds, permissions, and validity. This can turn some mistakes in unsafe Rust or native code into precise faults instead of unrestricted memory corruption.
CHERI also supports compartmentalisation. Two memory-safe components may each be correct in isolation but should not necessarily have access to each other’s secrets or services. Capabilities can express the minimum authority passed across that boundary.
This creates complementary layers:
- Safe Rust prevents many invalid programs through language rules.
- CHERI memory protection enforces bounds and permissions on capability-based access.
- CHERI compartmentalisation limits authority between components, including components that are working as designed.
- Testing and assurance find logic errors and check that the intended policy is implemented.
A boundary example
Imagine a Rust application that calls a C image decoder. The Rust code can pass a slice containing one image. On a conventional system, an error in the C decoder might let it read other memory in the process.
On a CHERI system, the native interface can pass a capability bounded to the image data. If the decoder reads beyond that range, the processor can stop the access. If the decoder runs in a compartment, it can also be denied authority to unrelated files, keys, and services.
The Rust type describes the safe interface at the language level. The capability preserves a related restriction after control crosses into lower-level code.
Important limits
CHERI does not automatically validate all Rust safety assumptions. Compiler support, application binary interface (ABI) design, standard-library support, and the handling of pointer metadata all matter. A capability with broad bounds can still authorise broad access, and a component given a powerful service capability can legitimately misuse it.
Likewise, Rust does not automatically provide least privilege between all components. A memory-safe program can still contain a logic flaw or expose more authority than intended.
Rust support varies across CHERI platforms and continues to evolve. Check the Rust software ecosystem page and the documentation for the chosen target before planning a production migration.
Key takeaways
- Prefer safe Rust for new code where practical.
- Keep
unsafecode small, documented, and tested. - Use CHERI to strengthen native boundaries and constrain low-level pointer use.
- Use compartments when components should not share the same authority.
- Assess the complete hardware and software stack rather than relying on a language label alone.
