Userlevel heap temporal memory safety (experimental)
Since CheriBSD 23.11 there has been support for userlevel heap temporal memory safety based on a load-barrier extension to Cornucopia, which is inspired by garbage-collection techniques. A forthcoming paper at ASPLOS 2024 on the Cornucopia Reloaded technique will describe these differences in detail.
This feature involves a collaboration between the kernel (which provides
asynchronous capability revocation with VM acceleration) and the userlevel
heap allocator (which quarantines freed memory until revocation of any
pointers to it) to ensure that memory cannot be reallocated until there are
no outstanding valid capabilities lasting from its previous allocation.
The userlevel memory allocator and the kernel revoker share an epoch counter
that counts the number of completed atomic revocation sweeps.
Memory added to quarantine in one epoch cannot be removed from quarantine
until at least one complete epoch has passed -- i.e., the epoch counter has
been increased by 2.
More information on temporal memory safety support can be found in the
mrs(3) man page:
man mrs
Checking whether temporal safety is globally enabled
Use the sysctl(8)
command to inspect the value of the
security.cheri.runtime_revocation_default
system MIB entry:
sysctl security.cheri.runtime_revocation_default
This sysctl sets the default policy for revocation used by processes on
startup.
We recommend setting this in /boot/loader.conf
, which is processed by the
boot loader before any user processes start.
Controlling revocation by binary or process
You can forcefully enable or disable revocations for a specific binary or process with elfctl(1) or proccontrol(1) and ignore the default policy:
elfctl -e <+cherirevoke or +nocherirevoke> <binary>
proccontrol -m cherirevoke -s <enable or disable> <program with arguments>
You can read more on these commands in the mrs(3) man page.
Synchronous revocation
Revocation normally occurs asynchronously, with a memory quarantine preventing memory reuse until revocation of any pointers to that memory. You can insert a specific call in your program to await synchronous revocation:
malloc_revoke();
Synchronous revocation on every call to free()
can be configured using the
_RUNTIME_REVOCATION_EVERY_FREE_ENABLE
environmental variable.
Note that synchronous revocation can incur extremely high expense.
Monitoring revocation in processes
Use the procstat cheri -v
command to inspect the CHERI memory-safety state
of a target process.
For example:
# procstat cheri -v 923 1012
PID COMM C QUAR RSTATE EPOCH
923 seatd P yes none 0
1012 Xorg P yes none 0xd2
Both processes in this example use a pure-capability process environment, have quarantining enabled, and are not currently revoking. seatd has never needed to perform a revocation pass, as it remains in epoch 0, whereas X.org has a non-zero epoch and has performed multiple passes.