Permissions

Capability permissions let software grant read, write, execute, and other authority separately instead of treating every pointer as equally powerful.

A pointer to a configuration object may need to read settings but should not change them. A code pointer may need to execute instructions but should not authorise data writes. A logging component may be allowed to submit an event without gaining access to the logger’s internal state.

CHERI permissions let capabilities express these differences in hardware-enforced authority.

Common permission categories

The precise permission set varies by CHERI architecture, but it commonly includes authority related to:

Separating data access from capability access is especially useful. A component may be allowed to copy ordinary bytes from a buffer while being prevented from loading and acquiring capabilities stored inside it.

Removing permissions

Software can derive a less powerful capability by clearing permissions. For example:

Original:  read + write + load-capability + store-capability
Derived:   read

The derived capability can be passed to a parser that needs to inspect input but must not modify it or acquire embedded authority.

Clearing permissions follows the same monotonic model as narrowing bounds. The holder cannot restore a removed permission unless it retained or receives another capability that still carries that authority.

Permission checks happen on use

The processor checks the permission required by each operation. A store through a capability without write authority faults even if the target address is inside the bounds and the virtual-memory page is writable.

CHERI checks complement page-table permissions. A page may be writable for one part of a process while a particular capability passed to a component is read-only.

This gives software a finer boundary than page-granularity protection.

Read-only is more than an interface promise

In conventional C, const helps the compiler and documents intent, but another alias may still write to the same object. It also does not normally cause the processor to reject every store through the underlying address.

A capability with write permission removed creates an architectural restriction for accesses through that capability. Other capabilities with write authority may still modify the object, so the system must control aliases if immutability is part of the security policy.

Execute authority

Executable capabilities authorise instruction fetch within defined bounds. Architectures and operating systems can combine this with sealing, sentries, and page permissions to create controlled entry points.

A caller may receive authority to enter a service at an approved function without receiving a writable capability for the service’s code or data. This is one building block for compartment calls.

Capability load and store permissions

Consider a buffer that contains ordinary data and a stored capability. A load-data permission may let a component read the bytes, while a separate load-capability permission controls whether loading the aligned capability also transfers its valid tag and authority.

Similarly, store-capability permission can control whether a component may place tagged capabilities into memory. Exact behaviour is architecture-specific, but the security goal is consistent: control not only information flow, but also the movement of authority.

Apply least privilege

When creating an interface, ask which individual operations are necessary:

Start narrow and add a permission only when a documented operation requires it.

Important limits

Permissions answer “what operations may this capability authorise?” Bounds answer “where?”, while Provenance and tags answer “how did this authority remain valid?”

Where next

An explanation of how capability authority is derived and why arbitrary integers are not equivalent to valid capabilities.

Provenance →