nx_memcap.nx
buildroot/runtime/nx_memcap.nx
about
memcap.nx -- CHERI-style memory capabilities, pure software.
Pointer-with-bounds-and-permissions, implemented entirely in
software. When Nishi silicon ships with hardware capabilities
(CHERI-RISC-V extension, or our own), the compiler recognises
MemCap<T> and emits single-instruction capability ops; the
software runtime becomes a no-op or is replaced by the hardware
path. Same NishiLang source, silicon-accelerated backend.
This is the 40-year bet: build invariants in software now, keep
the HW/SW interface stable, upgrade silicon when we can fab it.
Distinct from runtime/cap.nx: that file covers ACCESS
capabilities (who can call fs_open), which are coarse-grained
and language-level. This file covers MEMORY capabilities (what
bytes a pointer can touch), which are fine-grained and per-
allocation. Both systems plug into the Nishi silicon story:
access caps become compartment IDs, memory caps become CHERI
pointer tags.
Reference papers / systems:
Watson et al 2015 "CHERI: A Hybrid Capability-System
Architecture for Scalable Software Compartmentalization"
Woodruff et al 2014 "The CHERI capability model"
Nienhuis et al 2020 "Rigorous engineering for hardware security"
Arm Morello -- production CHERI-Armv8 (2022)
RISC-V CHERI extension -- ratified draft (2024)
Layout (32 bytes in software):
+ 0 .. + 7 : address (i64, the actual pointer)
+ 8 .. + 15 : base (i64, lower bound inclusive)
+ 16 .. + 23 : length (i64, bytes from base)
+ 24 .. + 31 : perms + tag (packed i64)
bit 0 : READ (load permitted)
bit 1 : WRITE (store permitted)
bit 2 : EXEC (jump permitted -- future)
bit 3 : CAP_LOAD (can load a capability through)
bit 4 : CAP_STORE (can store a capability through)
bit 5 : SEAL (non-deref, unforgeable handle)
bit 8 : VALID_TAG (mandatory; cleared on raw memcpy)
dependencies 2 imports · 1 importers
imports: nx_syscalls.nxnx_assert.nx
imported by: memcap_violation_test.nx
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| 73 | struct MemCap |
consts
| 55 | const MCAP_MAGIC_10000: i64 = 10000 |
| 59 | const MCAP_READ: i64 = 0x001 |
| 60 | const MCAP_WRITE: i64 = 0x002 |
| 61 | const MCAP_EXEC: i64 = 0x004 |
| 62 | const MCAP_CAP_LOAD: i64 = 0x008 |
| 63 | const MCAP_CAP_STORE: i64 = 0x010 |
| 64 | const MCAP_SEAL: i64 = 0x020 |
| 65 | const MCAP_VALID_TAG: i64 = 0x100 |
| 68 | const MCAP_RW: i64 = 0x103 // READ | WRITE | VALID_TAG |
| 69 | const MCAP_RO: i64 = 0x101 // READ | VALID_TAG |
| 80 | const MEMCAP_BYTES: i64 = 32 |
functions
| 85 | func memcap_new(addr: i64, base: i64, length: i64, perms: i64) -> *MemCap |
| 97 | func memcap_alloc(size: i64) -> *MemCap |
| 105 | func memcap_valid(c: *MemCap) -> i64 |
| 112 | func memcap_can_read(c: *MemCap, offset: i64, n: i64) -> i64 |
| 122 | func memcap_can_write(c: *MemCap, offset: i64, n: i64) -> i64 |
| 134 | func memcap_load_i64(c: *MemCap, offset: i64) -> i64 |
| 142 | func memcap_store_i64(c: *MemCap, offset: i64, val: i64) -> i64 |
| 151 | func memcap_load_u8(c: *MemCap, offset: i64) -> i64 |
| 159 | func memcap_store_u8(c: *MemCap, offset: i64, val: i64) -> i64 |
| 174 | func memcap_subrange(parent: *MemCap, sub_offset: i64, sub_len: i64) -> *MemCap |
| 188 | func memcap_restrict(c: *MemCap, new_perms: i64) -> *MemCap |
| 198 | func memcap_seal(c: *MemCap) -> *MemCap |
| 206 | func main() -> i64 |