RAM Residency and Zeroization
July 16, 2026 · View on GitHub
The defining property of NØNOS is that it runs from memory and leaves nothing behind. There is no disk in the trust path, memory is scrubbed as it is reclaimed during normal operation, and a single whole-system routine erases everything on a ZeroState event. This page states that guarantee as the code actually implements it: what is zeroed, when, and by which pass, and just as importantly where the guarantee is single-pass zeroing versus a multi-pass secure erase, so the claim is precise rather than a slogan.
RAM-resident by architecture
The system boots, verifies, and runs entirely from memory. Capsules are not read off a filesystem; they are signed artifacts compiled into the kernel image and verified before they run, as the verified-spawn gate describes, so there is no on-disk image in the trust path to leave behind. What a user chooses to keep is the exception, and it is explicit: it goes through the encrypted store with recorded consent, and that consent is one of the things the wipe below revokes. Everything else is transient by construction, and the zeroization paths below make that concrete.
Continuous zeroization
In normal operation, memory is scrubbed as it is freed, so a later allocation never sees an earlier one's data. Four paths do this, each verified in its own module.
Every physical frame is zeroed when it is returned to the frame allocator. The
higher-level deallocate_frame calls zero_frame before the frame is reusable
(src/memory/frame_alloc/manager/alloc.rs:41), and zero_frame
(src/memory/frame_alloc/manager/zero.rs:21) bounds-checks the frame against the
direct map and writes 4 KiB of zeros through it. So a frame that held a capsule's
data is zero by the time any other allocation can receive it.
The heap zeroes on both allocation and free: HEAP_ZERO_ON_ALLOC and
HEAP_ZERO_ON_FREE both default to on, so allocations start zeroed and freed blocks
are wiped as they are returned.
The fault handler zeroes a demand-backed page before mapping it, so a lazily populated page never exposes the previous contents of the frame it drew.
Secure memory regions are zeroed on deallocation, scaled by their security level.
deallocate_region (src/memory/secure_memory/manager/dealloc.rs:29) calls
secure_zero_memory(va, size, security_level) before it releases the virtual range,
so a region allocated at a higher security level gets a correspondingly stronger
erase when it is freed.
How a capsule's memory is reclaimed
A capsule leaves no residue not because of a dedicated scrub in the exit path, but
because of the frame-free zeroing above. When a capsule exits, its address space is
torn down and its physical frames are returned to the allocator, and each of those
frames is zeroed on return by zero_frame. The guarantee that the next tenant of
that memory sees only zeros is therefore the composition of teardown returning the
frames and the allocator zeroing them, which is exactly the property the Lean
Zeroization module proves at the specification level: a wiped region reveals
nothing of what it held, and a reused region leaks nothing across lifetimes. The
page states this as it is: the mechanism is frame-free zeroing, not a separate exit
wipe, and that mechanism is sufficient because no freed frame is reusable until it
is zeroed.
The ZeroState wipe
The explicit whole-system erase is secure_wipe_all_memory
(src/security/wipe.rs:23). It is a complete and correct erase routine, and it does
considerably more than zero the heap:
secure_wipe_all_memory():
revoke_all_consent() drop every persistence consent
wipe_heap_region() DoD 5220 multi-pass erase of the heap
wipe_process_memory() zero every process's code region and VMAs
wipe_crypto_keys() delete every key in the crypto vault
wipe_ipc_buffers() reinitialise IPC, clearing its buffers
wipe_vfs_caches() clear the filesystem caches
compiler_fence(SeqCst)
log "ZeroState secure memory wipe complete"
It first revokes all persistence consent so nothing can be written out during or after the wipe, then erases the heap with the multi-pass routine below, then walks every process and volatile-zeros its code region and every virtual memory area, then deletes all crypto vault keys, reinitialises IPC to clear its buffers, and clears the VFS caches. The per-process and per-region sizes are bounds-checked before the wipe so a corrupt PCB cannot direct the wipe outside a sane range.
One thing has to be stated plainly, because the code is unambiguous about it: as
the tree stands, secure_wipe_all_memory has no caller. A search of the whole
repository finds only its definition; nothing in the kernel invokes it, not the
shutdown path, not the panic handler, not a tamper response. The routine is
therefore a capability, not an automatic guarantee. What enforces ephemerality in
operation is the continuous zeroing above, which does run, on every free. The
one-shot whole-system erase is built and correct but not wired, and making it fire
on its own would mean calling it from the shutdown, panic, and tamper paths. This
page records that gap rather than describing a wipe that does not currently
trigger.
The multi-pass erase
The heap wipe uses dod_5220_wipe (security/wipe.rs:44), a multi-pass overwrite
modelled on the DoD 5220.22-M pattern, with verification and a cache flush:
dod_5220_wipe(data):
pass 1: write 0x00 to every byte (volatile)
pass 2: write 0xFF to every byte (volatile)
pass 3: write secure-random bytes
pass 4: write 0x00 to every byte (volatile)
verify_wipe(data) read back, warn and re-wipe if nonzero
flush_cache_lines(data) clflush every line, then mfence (x86_64)
Each pass is a volatile write so the compiler cannot elide it, separated by
sequential-consistency fences. The random pass draws from the secure RNG. After the
final zero pass it reads the region back to verify the wipe took, and on x86_64 it
flushes every cache line with clflush and an mfence, so the zeros reach memory
rather than sitting in cache. This is the strong erase; the continuous zeroing above
is a single zero pass, which is what steady-state reclaim needs, and the DoD pattern
is reserved for the whole-system ZeroState wipe.
What this does and does not claim
Stated precisely: freed memory is single-pass zeroed as it is reclaimed, so no
allocation sees a previous one's data, and this runs on every free; a capsule's
frames are zeroed as its address space is torn down on exit, so it leaves no
readable residue; secure regions get a stronger erase on free; and the whole-system
secure_wipe_all_memory is a multi-pass, verified, cache-flushed erase, but it is
implemented and not wired to any trigger, so it is a capability that does not fire on
its own today. The operational ephemerality guarantee rests on the continuous
zeroing, which does run; the one-shot whole-system wipe is a gap to close by
invoking it from the shutdown, panic, and tamper paths. What the software wipe addresses is data remanence in memory
the kernel can address, by overwriting it and flushing it out of cache. It does not
claim to defeat physical attacks below that level, such as cold-boot DRAM remanence
against removed memory, which is a hardware property outside the wipe's reach. The
guarantee is that nothing the kernel can address is left readable, and that no freed
memory is reused before it is zeroed.
Security analysis
The whole page is a security argument, so this states it as named properties and, crucially, where the guarantee is a running mechanism versus an unwired capability.
No cross-lifetime leakage in steady state. Every physical frame is zeroed on return to the
allocator by zero_frame (frame_alloc/manager/zero.rs:21), which bounds-checks the frame against the
direct map before writing 4 KiB of zeros, so a frame that held a capsule's data is zero by the time any
other allocation can receive it. The heap zeroes on both alloc and free, the
fault handler zeroes a demand-backed page before mapping it, and secure regions get an
erase scaled by security level on free (secure_memory/manager/dealloc.rs:29). This is the property the
Lean Zeroization module proves at the spec level, and it is a running mechanism: it fires on every
free, so a capsule's frames are scrubbed as its address space is torn down on exit, with no dedicated
exit wipe needed.
The whole-system erase is strong but not wired. secure_wipe_all_memory (security/wipe.rs:23)
revokes persistence consent, multi-pass erases the heap, zeros every process's code and VMAs, deletes
crypto keys, and clears IPC and VFS caches, with per-region sizes bounds-checked so a corrupt PCB cannot
direct the wipe out of range. The multi-pass dod_5220_wipe (wipe.rs:44) writes 0x00, 0xFF, random,
then 0x00, all volatile so the compiler cannot elide them, verifies the read-back, and on x86_64
clflushes every line then mfences so the zeros reach memory rather than sitting in cache. The honest
statement the code forces: secure_wipe_all_memory has no caller anywhere in the tree, so it is a
capability, not an automatic guarantee. Operational ephemerality rests on the continuous zeroing above,
which does run; wiring the one-shot wipe means calling it from the shutdown, panic, and tamper paths.
The claim is bounded to memory the kernel can address. The software wipe defeats data remanence in addressable memory by overwriting and flushing it. It does not claim to defeat physical attacks below that level, such as cold-boot DRAM remanence against removed memory, which is a hardware property outside its reach. The guarantee is precisely that nothing the kernel can address is left readable, and that no freed memory is reused before it is zeroed.
Debugging zeroization
Zeroization is mostly invisible when it works, so debugging is about knowing which pass ran and what its
one console signal means. The continuous zeroing is silent: zero_frame returns without writing if the
frame is outside the direct map (zero.rs), which is the one way a free could not scrub, so a stale
byte surviving a free points at a frame whose physical address fell outside DIRECTMAP_SIZE rather than
at a missing zero pass. The one narrated path is the whole-system wipe, which logs
"ZeroState secure memory wipe complete" (wipe.rs) after its compiler fence, so the absence of that
line is how you confirm the wipe never ran, which today is always, because nothing calls it. Inside the
multi-pass erase, verify_wipe reads the region back after the final zero pass and warns and re-wipes
if it finds a nonzero byte, so a persistent verify warning would mean memory that will not hold zeros
(failing hardware) rather than a logic bug in the pattern. When reasoning about a suspected leak, the
distinction that matters is which mechanism should have covered it: a reused frame is the allocator's
zero_frame, a reused heap block is HEAP_ZERO_ON_FREE, a demand page is the fault handler's zero,
and a secure region is secure_zero_memory. The whole-system DoD erase is only for a ZeroState event
and is not part of any of those steady-state paths.
Verification
The specification-level Zeroization proofs in the
verification stack prove that a wiped region
holds no secret and that a reused region leaks nothing across lifetimes, which is the
abstract statement of the frame-free zeroing and the ZeroState wipe documented here.
Source map
src/memory/frame_alloc/manager/alloc.rs deallocate_frame calls zero_frame
src/memory/frame_alloc/manager/zero.rs zero_frame, the direct-map zero pass
src/memory/heap/manager/globals.rs HEAP_ZERO_ON_ALLOC / HEAP_ZERO_ON_FREE
src/memory/secure_memory/manager/dealloc.rs secure_zero_memory on region free
src/security/wipe.rs secure_wipe_all_memory and dod_5220_wipe
Every reference above is verified against those trees. The demand-page zero is on the fault handler page, the heap zeroing is on the heap page, the frame-free zero is the free-path arm of the physical frame allocator, and the spec proofs are on the verification stack page.