Seventy percent of CVEs are memory safety bugs. Buffer overflows, use-after-free, double frees. The kind of vulnerabilities that let an attacker pivot from a malformed JPEG to root access.
We have known how to stop most of these in hardware since 1975. The Cambridge CAP computer used capability-based addressing. Carnegie Mellon’s Hydra did too. The idea was simple: every memory access carries its own permission slip. Hardware enforces it. No permission slip, no access.
And then capability hardware spent the next four decades losing to flat memory models. It is only now, with ARM’s Morello board and the CHERI instruction set, that capability architectures are staging a credible comeback.
The question isn’t whether capabilities work. They do. The question is why it took forty years for the industry to care.
A capability pointer is an unforgeable ticket, not just an address
A normal pointer is an integer. An address. It has no length, no permissions, and no provenance. If you can guess an address, you can access it. That is not a feature. That is a design bug that C inherited from the PDP-11.
A capability pointer, by contrast, is a hardware-enforced tuple. It contains the virtual address, the base and length of the authorized region, read/write/execute permissions, and a 1-bit validity tag. The tag is stored outside normal memory, in dedicated hardware metadata, which makes capabilities unforgeable. You cannot construct a valid capability from an integer. You can only receive one from something that already had authority.
On CHERI, this looks like a 128-bit pointer on a 64-bit machine. The extra 64 bits carry bounds and permissions. The tag bit lives in a shadow table or spare ECC bits, depending on the implementation.
Here is what narrowing a capability looks like in practice on a CHERI system:
#include <cheriintrin.h>
#include <stdio.h>
int main(void) {
char buffer[64];
// In the purecap ABI, &buffer[0] is already a capability
// carrying 64-byte bounds and read/write permissions.
char *cap = buffer;
printf("Original length: %zu\n", cheri_length_get(cap));
// Narrow authority to the first 16 bytes.
char *narrow = cheri_bounds_set(cap, 16);
printf("Narrowed length: %zu\n", cheri_length_get(narrow));
// This access is safe and hardware-authorized.
narrow[15] = 'x';
// This would trap with a CHERI bounds violation:
// narrow[20] = 'y';
return 0;
}
If an attacker corrupts a capability pointer, the tag bit clears. The hardware raises an exception on the next dereference. The exploit chain dies at the first hop.
That is a radically better security model than ASLR and stack canaries, which are just speed bumps.
The iAPX 432 poisoned the well for a generation
So why did we spend forty years pretending pointers were just integers?
Intel shipped the iAPX 432 in 1981. It had hardware capabilities, object-oriented memory segments, and even hardware-assisted garbage collection. It was also roughly five to ten times slower than the 8086, enormously complex, and incompatible with every existing compiler.
IBM had more luck with the System/38, which used capabilities for its single-level store. It worked. It was stable. It was also a proprietary midrange system with no path into the emerging open ecosystem. When the UNIX revolution arrived, it brought flat address spaces, C pointers, and the assumption that a pointer fits in a register. That assumption became the ABI of the entire industry.
By the 1990s, capability hardware was a research curiosity. The commercial imperative was backwards compatibility and raw performance. Memory safety was a language-level concern, if it was a concern at all. C and C++ simply declared it the programmer’s problem.
That worked well enough until the internet made every buffer overflow remotely exploitable.
CHERI’s hybrid mode breaks the compatibility deadlock
Modern capability hardware, specifically CHERI, learned the lesson of the iAPX 432. It does not force an all-or-nothing migration.
CHERI supports a hybrid ABI where legacy integer pointers and capability pointers coexist in the same process. You can compile just your network parser or your sandboxed library with capabilities, while the rest of your application uses normal pointers. The operating system (CheriBSD, or the CHERI Linux port) manages the transition.
This matters because the real barrier was never silicon area. It was software inertia. There are billions of lines of C and C++ that assume sizeof(void *) == 8. A capability architecture that requires rewriting all of them is dead on arrival. One that can be adopted incrementally has a shot.
The hardware mechanism is elegant. Capability-aware loads and stores use dedicated instructions that check the tag and bounds in parallel with address translation. Integer pointers bypass the checks entirely. You pay for safety only where you use it.
The real cost is TLB pressure and pointer bloat, not cycle count
Capabilities are not free. The overhead falls into three buckets.
First, pointer size. In a purecap ABI, every pointer is 128 bits. That doubles cache pressure for pointer-heavy data structures. Linked lists, trees, and vtables all get fatter.
Second, bounds granularity. CHERI enforces bounds at byte granularity, which means the memory subsystem must check bounds on every dereference. The check itself is fast, but fine-grained capabilities can increase TLB and cache pressure if you create many small guarded regions.
Third, and most importantly, software changes. Compilers must generate capability-aware prologues. ABIs change. Memory allocators must return bounded capabilities rather than raw pointers. Debuggers need to understand 128-bit registers.
The measured runtime overhead on CHERI hardware is typically in the single-digit percent range for most workloads, and up to ten to fifteen percent for pointer-heavy benchmarks. That is less than the overhead of many modern mitigations like memory tagging or sandboxing, and it provides stronger guarantees.
The trade-off is not performance. The trade-off is ecosystem churn.
You can run this on a dev board today
If you want to try capability hardware, you do not need a time machine or a research grant.
ARM’s Morello board implements CHERI extensions on a Neoverse N1 core. CheriBSD runs on it out of the box. If you do not have the hardware, QEMU-CHERI emulates the full capability architecture.
The toolchain is standard LLVM. You compile with cheri-clang and the -march=morello+cheri flag. FreeBSD and Linux ports exist. The LLVM integrated assembler, linker, and debugger all understand capabilities.
Start with a single function. Wrap a parser or a deserialization routine in a narrowed capability. Run your fuzzer against it. When an out-of-bounds write would have corrupted the heap, you will get a clean hardware exception instead.
Capability hardware is not a theoretical fix for memory safety. It is a working, shipping technology that the industry ignored for forty years because the incentives were wrong. Flat memory was faster to build, easier to port, and good enough until remote exploitation became the default threat model.
The silicon works. The compilers work. The operating systems work. The only thing left is deciding which parts of your codebase are worth protecting first.