Cover image taken from the cheriot-ibex GitHub project..
Ever found yourself staring at a debugger, chasing a memory corruption bug that seems to defy logic? Or maybe you’ve sunk countless hours hardening C/C++ code against the ever-present threats of buffer overflows and use-after-free vulnerabilities? If you’re an embedded systems developer, you’ve likely been there. These memory safety issues aren’t just annoyances; they’re prime vectors for security exploits. But what if we could tackle these problems at their very root – right down in the hardware?
That’s where CHERI (Capability Hardware Enhanced RISC Instructions) enters the scene. It’s not just another software patch or a new programming language. CHERI is a set of architectural extensions poised to change the way we approach pointers and memory access. This post is a quick dive into CHERI. We’ll unpack its core ideas, see how it ticks under the hood, explore its implications for C/C++, its synergy with Rust, and discuss the practicalities of bringing this tech into the real world of embedded systems.
Disclaimer: I am not an expert in CHERI. This document captures notes of my current understanding of CHERI and its potential use cases. See the references section at the end of the document if you are looking for more in-depth material.
Pointers and Their Pitfalls
In C and C++, pointers are the workhorses giving us incredible power and direct memory control. But, as the saying goes, “with great power comes great responsibility.” And let’s be frank, that responsibility can be a heavy lift.
The classic C/C++ pointer is, at its heart, just a number – a memory address. This elegant simplicity is also its Achilles’ heel:
- Spatial Unsafety: It’s all too easy to write past an array or buffer’s end. This is your classic buffer overflow. The fallout? Corrupted adjacent data, trashed program state, or, in a security nightmare, overwritten return addresses enabling attackers to hijack control flow.
- Temporal Unsafety: Ever used a pointer to memory that’s already been free()’d? That’s a “dangling pointer.” The memory it once pointed to might now hold entirely different data. Using that old pointer can lead to bizarre crashes, subtle data corruption, or, you guessed it, exploitable security holes.
Memory safety issues are a significant concern, accounting for a large percentage of patched security vulnerabilities. While software-based solutions can be challenging, impact performance, or have limitations, CHERI offers a hardware-level solution.
CHERI Capabilities: Hardware-Backed Pointers
How does CHERI intend to solve these problems? Instead of raw memory addresses, CHERI introduces a richer concept: capabilities.
Think of a capability not just as a signpost, but as a special key fob. This fob doesn’t just show where the door (memory location) is; it also dictates:
- Which specific doors it can open (the bounds of the memory region).
- What you’re allowed to do once inside – just look (read), redecorate (write), or even host a party (execute) (the permissions).
- And crucially, this key fob is unforgeable. You can’t just whip one up or easily tamper with an existing one to gain more access.
In a CHERI system, every C/C++ pointer, including implicit ones like the stack or global pointer, becomes a hardware-enforced capability. It’s a shift from “here’s an address, good luck!” to “here’s an authorized way to access this specific memory for these specific purposes.” (read more: in-depth intro).
Anatomy of a CHERI Capability
A CHERI capability is more than an address; it’s a bundle of hardware-enforced information. While the exact bit-layout can vary (e.g., 64-bit CHERI vs. embedded-focused CHERIoT), the core concepts hold.
Since I am mostly interested in embedded security applications, such as secure enclaves or Root of Trust (RoT) technology, I will be focusing more on the CHERIoT implementation details. The document will use the term CHERI to refer to high level CHERI concerpts and CHERIoT or
cheriot-ibexto refer to the CHERIoT implementation based on the Ibex RISC-V 32IMC processor.
Here’s a conceptual look at a CHERIoT (CHERI for IoT) capability:
+--------+
| Tag |
+--------+
|
v
+------------------+
| |
| +--------+ |
| | Perm | |
| +--------+ |
| | |
| v |
| +--------+ |
| | Type | | Capability
| +--------+ | Metadata
| | |
| v |
| +--------+ |
| | Bounds | |
| +--------+ |
| |
+------------------+
|
v
+------------------+
| Address |
+------------------+
|
v
+------------------+
| Memory Range |
+------------------+
Figure 1: A conceptual illustration of CHERI capability components and their relation to a memory region.
Let’s dissect these parts:
- Address: The familiar part – the current memory address the capability points to.
- Bounds (Base and Top/Length): This is key for memory safety. Every capability knows the exact range it can access, defined by a ‘base’ and ’top’ address (or base and length). Try to step outside these bounds, even by a byte? You will get a hardware trap. No more silent corruption. This is your hardware-enforced spatial memory safety.
- Embedded Note: In space-constrained systems, bounds are often compressed. Instead of full base/top addresses, it might store the current address and use a floating-point-like encoding for distances to base/top. This saves bits but means larger regions might need specific alignment for precise representation.
- Permissions: This field dictates what the capability can do. Think of it as the fine print:
- Load (Read): Permission to read data.
- Store (Write): Permission to write data.
- Execute: Permission to fetch and run instructions (crucial for W^X).
- Load/Store Capability: A meta-permission: can this capability load/store other capabilities? Vital for preventing data buffers from being treated as holding powerful pointers.
- Global: Governs the capability itself. A “global” capability can be stored anywhere. A “non-global” one (like for a stack variable) might only be storable in restricted regions (like the stack, which would need “store-local” permission). This enforces pointer scoping.
- Seal/Unseal: For advanced security via “sealing.”
- Tag Bit: The magic that makes capabilities unforgeable. It’s an extra, invisible bit. Hardware sets it to ‘valid’ only for legitimate capabilities. Tamper with a capability in memory via ordinary data writes, or if an operation invalidates it? The hardware automatically clears the tag. Load a potential capability? The CPU checks the tag. No tag? Just data. Trying to use data as a capability will result in a hardware trap. You can’t just conjure a valid, tagged capability.
- Type (Object Type for Sealing): Usually zero for a normal, usable (unsealed) capability. Non-zero means it’s sealed.
- Sealing: Puts a capability in a tamper-proof envelope. Once sealed, you can’t use it to access memory or change its attributes without the hardware zapping its tag. It’s an opaque token. To use it, you need another special capability with permit-unseal and a matching “object type.” Powerful for secure handles and protecting things like return addresses.
Together, these make CHERI capabilities rich, hardware-enforced descriptors of memory access rights.
Additional details on Tag and Type fields: CHERI’s Hardware Root of Trust
Security experts will rightly ask: “How can we trust the tag bit and type field against software tampering?” The answer: their protection is architectural, enforced by hardware, not just a software convention.
The following sections use CHERIoT as a particular implementation example.
The Tag Bit
The tag bit is the cornerstone of unforgeability.
- Hardware Exclusive Generation: Tags are set by hardware only: during initial capability creation (e.g., by a system loader) or when deriving a new capability from a valid one via privileged CHERI instructions. Standard software instructions cannot arbitrarily set a tag.
- Tags in Memory: A Parallel, Protected World: CHERIoT capabilities (e.g., 65 bits: 64 for data + 1 tag) store the tag in dedicated tag memory (tag RAM) paralleling main data RAM. For every capability-sized chunk in main memory, there’s a corresponding tag bit in tag RAM. Standard data store instructions (SW in RISC-V) cannot directly write to tag memory. If a data write targets a location holding a valid capability, the memory controller hardware automatically clears the corresponding tag bit. Only CHERI-specific capability store instructions can write both the capability data and set its tag to valid. This hardware gatekeeping prevents software from forging capabilities by simply overwriting memory.
- Tags on the Move: Propagation and Invalidation: When loading a capability, both data and tag are fetched. No tag? The loaded value is invalid in the CPU register. Tags propagate with valid CHERI operations. Any operation invalidating a capability (invalid derivation, bounds violation, data overwrite) makes the hardware clear the tag.
- All or Nothing: Atomic Operations: Capability (data + tag) movements between registers and memory are architecturally atomic, preventing race conditions.
The Type Field
The type field is key to CHERI’s sealing mechanism, creating opaque, tamper-proof pointers.
- Purpose: A non-zero type makes a capability sealed. It can’t directly access memory or have its attributes changed without invalidating its tag. It’s an opaque token.
- Storage: The type field (e.g., 3 bits in CHERIoT) is part of the 64-bit capability metadata in main memory.
- Integrity via the Tag: The type field’s security, like all capability metadata, is guaranteed by the tag bit. When sealing, the object type is set. If software then tries to modify any part of this sealed capability in memory with a standard data write (SW), the hardware sees it as a data overwrite and clears the tag. The tampered capability becomes untagged and invalid. Unsealing requires another capability with permit-unseal and a matching type.
Security Claims
For a CHERIoT system:
- Hardware Enforcement: Tag unforgeability and integrity are enforced by the CHERIoT architecture, implemented in the cheriot-ibex load store unit.
- Segregated Tag Pathway: Tag management is out-of-band for normal software. Standard instructions clear tags on overwrite; CHERI-specific instructions manage them via privileged hardware paths.
- Deterministic Behavior: Misuse or forgery attempts lead to deterministic hardware traps, not exploitable undefined behavior.
While microarchitectural details (like tag RAM layout or sealing) are deeper in the design, the CHERIoT architectural guarantees upheld by cheriot-ibex provide a robust hardware root of trust for capability integrity.
How CHERI Actually Delivers on Memory Safety & Security
With capabilities boasting bounds, permissions, and unforgeable tags, how does this translate to a safer embedded world? It’s all about what the hardware prevents.
- Spatial Memory Safety: If capability p is for a 10-integer array, accessing p[10] (out of bounds) violates p’s hardware-enforced bounds. The processor traps immediately.
- Temporal Memory Safety: When memory is freed, CHERIoT systems can use a load filter and a revoker. The load filter checks a shadow bitmap on capability load; if the memory is stale (freed), the loaded capability’s tag is cleared. A background revoker scans memory, clearing tags of capabilities pointing to deallocated regions. So, using a capability to freed memory likely means using an untagged (invalid) one, causing a trap.
- Pointer Provenance: Capabilities are unforgeable and only derivable from other valid capabilities. You can’t just cast an integer to a pointer and roam free. All valid capabilities trace back to an initial, system-granted set.
- Principle of Least Privilege: Software gets only the permissions it needs. Pass a read-only capability to a function needing only to read, even if the original was read-write. Hardware enforces this.
- Intentional Use: Privileged operations require intentional use of the specific authorizing capability, thwarting “confused deputy” attacks.
Additional details on CHERIoT
While CHERI is a general architecture, CHERIoT is its lean, mean incarnation for resource-constrained embedded/IoT devices.
- Compressed Bounds: CHERIoT uses clever encodings for bounds, saving memory.
- Smart Permission Encoding: For 32-bit addresses (64-bit capabilities + tag), CHERIoT intelligently packs permissions, sometimes using primary/dependent distinctions. Some theoretical combinations might not be expressible, but practical security is maintained.
- No Half Measures (No Hybrid Mode): CHERIoT generally mandates all-capability pointers, assuming full recompilation for simplicity and stronger guarantees.
- Built for Compartmentalization: CHERI capabilities underpin CHERIoT’s software compartmentalization. Firmware can be divided into secure, isolated “compartments” that communicate via hardware-mediated entry points, with hardware sanitizing state on transitions.
Why Isn’t CHERI Everywhere?
With such compelling benefits, why isn’t CHERI in every chip? The path from research to widespread adoption is a marathon, especially for fundamental changes to CPU memory handling. It’s a classic “chicken and egg” scenario involving the entire hardware and software ecosystem (history lesson).
- The Silicon-Software Interlock:
- Silicon Vendors: Need strong market demand and a mature software ecosystem (compilers, OS, debuggers) before investing heavily in CHERI-enabled silicon.
- Software Ecosystem: Toolchain and OS developers are wary of committing major resources to an architecture without readily available commercial hardware for testing and validation.
- Ecosystem Inertia and Investment:
- Legacy Code: Vast C/C++ codebases need recompilation. While CHERI aims for high source compatibility, some porting effort is inevitable, especially for low-level code. This is particularly true for RoT software.
- Learning Curve: Developers need to learn new concepts and tools.
- Cost: Initial hardware redesign and software porting costs can be daunting without clear market pull. Most people will say that switching to Rust is cheaper. More on this later.
- Standardization and Maturation: Full industry standardization and a mature software ecosystem take time and collaborative effort. Initiatives like the CHERI Alliance are vital in fostering this.
A Path Forward: Enabling a Platform
TLDR; The most pragmatic approach is to enable a hardware platform that supports hybrid modes of operation. Once the hardware is available, it will be easier to upstream toolchain changes and enable software teams to eavaluate the technology. In the particular case for CHERIoT, hybrid doesn’t mean running software with legacy pointers and capabilities; rather, it means giving developers the option to enable CHERI as a runtime option. For example, the first mutable instruction fetched by the processor can be configure to execute in CHERI or regular RISC-V 32IMC mode of execution.
Hybrid CHERI architectures – CPUs supporting both legacy and capability modes – offer a pragmatic solution.
- Backward Compatibility: They run existing, unmodified software, lowering the entry barrier.
- Facilitating Toolchain Development: Hybrid cores prototyped in research projects provide crucial platforms for maturing the CHERI software ecosystem.
This incremental path de-risks the transition, building software support and demonstrating value, which encourages more hardware adoption. Research silicon like Microsoft’s CHERIoT-Ibex and other CHERI-enabled products like those from Codasip are key.
Practical Considerations: Area Overhead of CHERIoT on Ibex
So, CHERIoT is powerful, but what’s the silicon cost? The paper “Area Comparison of CHERIoT and PMP in Ibex” (Riedel et al., arXiv:2505.08541v1) offers insights by comparing CHERIoT and traditional Physical Memory Protection (PMP) on the Ibex RISC-V core.
Core-Level Impact
For a baseline Ibex core (~57 kGE - kilo Gate Equivalents):
- PMP (16 regions): Added ~24 kGE (a 42% core overhead), mainly from PMP checking logic and CSRs.
- CHERIoT: Added ~33 kGE (a 57.5% core overhead). Key contributors: CHERI-specific logic (~16 kGE), more CSRs, a doubled register file, and enhanced LSU/WB stages.
CHERIoT-Ibex was ~11% larger than PMP-Ibex, largely due to the register file.
The Bigger Picture: System-on-Chip (SoC)
A 40-60% core increase sounds like a lot, but in an SoC (like OpenTitan Earl Grey), the CPU is often a small part. Most area goes to memory, peripherals, and accelerators.
- The baseline Ibex might be just 1.4% of the total SoC area.
- Adding PMP to Ibex: estimated SoC area increase of ~0.6%.
- Adding CHERIoT: core extension (~0.8% SoC area) + memory system changes (tags, wider bus, ~0.2% SoC area) = total ~1% SoC area increase.
Area Conclusion
While CHERIoT has a larger core-level overhead than PMP (57.5% vs 42.1% in the study), its SoC impact is modest (~1%). Given CHERIoT’s vastly superior fine-grained spatial/temporal memory safety and compartmentalization, this is often a justifiable trade-off for security-critical systems.
Rust and CHERI: A “Belt and Suspenders” Security Win?
Rust’s ownership and borrowing system offers strong compile-time memory safety. So, if your stack is all Rust, is CHERI still useful? Maybe?
Why CHERI and Rust?
- Guarding unsafe Frontiers: Rust’s unsafe blocks bypass some compile-time checks for low-level code or FFI (as discussed in CHERI outreach and explored at FOSDEM). CHERI provides a runtime hardware backstop here.
- Fortifying the Foreign Function Interface (FFI) Wall: When Rust calls C/assembly, CHERI can enforce memory safety on the non-Rust side and ensure data passed via capabilities has hardware-enforced restricted permissions (explained well on the CHERIoT blog).
- Safety Net for Compiler/Runtime Bugs: Though rare, bugs here could compromise memory safety. CHERI offers an independent hardware defense (see Cambridge CHERI research).
- Hardware-Powered Compartments: CHERI enables hardware-enforced isolation between Rust modules or third-party crates, limiting blast radius.
- Enhanced unsafe Semantics: CHERI can potentially offload some runtime checks (like slice bounds) to hardware and inherently includes pointer provenance.
Use Case: Securing a C/Assembly Crypto Library
A C/assembly crypto library (like OpenTitan’s) is a prime candidate for CHERI protection within a Rust stack. Rewriting such intricate, hardware-tied code in Rust is a massive task and might negate security/performance tuning.
CHERI helps by:
- Memory Safety for Unsafe Code: Compiling the C/assembly library for CHERI means its internal memory errors become hardware traps.
- Asset Isolation – Protecting Keys:
- Crypto Compartment: Run the library in its own CHERI compartment, granted minimal capabilities to access crypto hardware and its private key storage.
- Key Capabilities: Keys are represented by highly restricted capabilities held exclusively by this compartment, perhaps only usable by specific crypto accelerator instructions, not for general read/write.
- No Direct Key Exposure: Rust code calls the crypto compartment, passing data. The compartment uses its internal, private key capabilities. Key material never leaves.
- Tamper-Proof Handles: For multiple key contexts, the library gives Rust opaque, sealed capabilities as handles. Only the crypto compartment can unseal them.
- Resilience: If other system parts are compromised, keys remain protected by hardware-enforced capability isolation.
Hybrid Approach: Rust for Logic, CHERI-C/Assembly for Sensitive Cores
Using Rust for general logic and CHERI-C/assembly for critical cores (like a crypto library) offers:
- Rust’s Strengths: Productivity, modern features, compile-time safety for most code.
- Secure Enclaves: C/assembly gets a hardware-enforced sandbox.
- Hardened FFI: Calls between Rust and C/assembly cross a CHERI protection boundary; capabilities are checked.
- Asset Protection: Sensitive data in C/assembly compartments is locked down.
This pragmatic model leverages Rust’s safety for new code while securely incorporating essential, hard-to-replace low-level components, underpinned by CHERI’s hardware security.
What This Means for C/C++ Developers
For C/C++ developers, CHERI mostly works behind the scenes via a CHERI-aware compiler. Key changes:
- Undefined Behavior Becomes Deterministic Faults: Many pointer errors (out-of-bounds, null dereference) become precise hardware traps. This is huge for debugging and thwarts exploits.
- ABI Adapts: The Application Binary Interface defines how capabilities are used for function calls, returns, and system calls. CHERIoT, for instance, uses a trusted “switcher” to manage inter-compartment calls, clearing registers and adjusting stack capabilities for isolation.
- CHERI Toolchain: You’ll use a CHERI-enabled compiler (LLVM/Clang, potentially GCC).
- Builtins (for the Curious): CHERI C provides intrinsics for direct capability manipulation (bounds, permissions, sealing/unsealing) for low-level or specialized code.
Essentially, CHERI offers a hardware safety net for C/C++, catching common memory errors.
Context for Security Experts
CHERI represents a fundamental shift in secure system design:
- Mitigates Common Exploits: Directly undermines memory corruption exploits like stack smashing, heap corruption, and many ROP/JOP attacks, thanks to capability integrity and sealed pointers (see CHERI overview papers).
- Stronger than MPU/MMU Alone: Offers fine-grained, intra-process memory safety and robust compartmentalization beyond traditional process-level isolation.
- Foundation for Secure IPC/FFI: Sealed capabilities enable secure inter-component communication with tamper-proof handles.
- Auditability by Design: CHERIoT’s build system can report capabilities granted to compartments, enabling automated security policy verification.
- Revocation Trade-offs: Direct capability storage simplifies common operations but makes broad revocation more complex than indirection-based systems. CHERIoT uses load filters and revocation sweeps for heap temporal safety.
CHERI moves many memory safety duties from fallible software to infallible hardware enforcement.
Closing Thoughts
CHERI and CHERIoT are serious, long-term efforts to build a more secure digital foundation. By baking memory safety and least privilege into hardware, CHERI offers a powerful alternative to the endless cycle of software patches.
For embedded developers, this means more secure, robust, and reliable systems, even with C/C++. As CHERI-enabled hardware (like cheriot-ibex) and toolchains mature, our approach to embedded security is set for a significant, welcome transformation – moving from memory safety as a constant battle to an inherent platform property.
There are still several challenges ahead in brining this technology to real products, most dealing with engineering cost and use case alignment. Use cases moving to Rust are currently not as interested in CHERI as they see it as a very expensive with not clear ROI. There is also skepticism about the technology being mature enough due to lack of upstream toolchain support and commercial grade silicon.
However, interest in some industries such as automotive and mobile is growing, as CHERI provides interesting isolation guarantees. There are pre-certified applications in these fields for which it is probably easier to migrate them to CHERI than it is to re-write them in memory safe programming languages.
Time will tell. The next couple of years will be very interesting to watch in this space.
Further Reading
If this dive into CHERI has piqued your interest, here are some excellent resources:
- The CHERI Website (University of Cambridge): The official home for CHERI research, packed with papers, presentations, and detailed specifications. Start with their CHERI FAQ and the Introduction to CHERI technical report.
- “Area Comparison of CHERIoT and PMP in Ibex” by Riedel et al. (arXiv:2505.08541v1): The research paper on area overhead.
- Microsoft’s CHERIoT and cheriot-ibex Resources: Public repositories/documentation from Microsoft Research (e.g., github.com/microsoft/cheriot-ibex).
- lowRISC and OpenTitan: Context on the Ibex core and secure silicon. (lowrisc.org, opentitan.org).
- CHERI Alliance: Industry initiative for CHERI adoption. (cheri-alliance.org).
- “Strengthening memory safety in Rust: exploring CHERI capabilities for a safe language” by Nicholas Wei Sheng Sim: Dissertation on Rust & CHERI. (nw0.github.io/cheri-rust.pdf).
- Relevant Blog Posts and Articles:
- “How to talk to your parents about hardware memory safety” (CHERIoT Platform Blog: cheriot.org/cheri/2024/08/06/how-to-talk-about-CHERI.html)
- “CHERI Myths: CHERI is incompatible with safety-critical systems” (CHERIoT Platform Blog: cheriot.org/cheri/myths/2024/11/25/cheri-myths-safety-critical.html)
- “CHERI: Hardware-Enabled C/C++ Memory Protection at Scale” (University of Cambridge: www.cl.cam.ac.uk/research/security/ctsrd/pdfs/20240419-ieeesp-cheri-memory-safety.pdf)
- “A Rusty CHERI - The path to hardware capabilities in Rust” (FOSDEM 2023: archive.fosdem.org/2023/schedule/event/rust_a_rusty_cheri_the_path_to_hardware_capabilities_in_rust/)
This list should give you plenty to explore :)
