Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
- [Capability permissions](apis/capability-permissions.md)
- [Bounds alignment due to compression](apis/bounds-alignment-due-to-compression.md)
- [Implications for memory-allocator design](apis/implications-for-memory-allocator-design.md)
- [Memory allocators and CHERI C/C++](allocators/README.md)
- [Guarantees to the allocator consumer](allocators/caller-guarantees.md)
- [Recommendation for allocator implementations](allocators/allocator-guarantees.md)
- [Printing capabilities from C](printf/README.md)
- [Generating string representations of capabilities](printf/strfcap.md)
- [Printing capabilities with the printf(3) API family](printf/printf.md)
Expand Down
27 changes: 27 additions & 0 deletions src/allocators/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Memory allocators and CHERI C/C++

This chapter considers two closely related topics:

* Guarantees that may be relied on by memory-allocator consumers programmed
in CHERI C/C++
* Guidance for memory-allocator developers targeting CHERI C/C++ execution
environments

While the focus of this section is on class C-language APIs such as
`malloc()`, `calloc()`, `free()`, and `realloc()`, aspects of these guidelines
will also apply to many other allocators including, to varying extents,
bespoke allocators in OS kernels, language runtimes, and scalable
applications.

The most fundamental behaviors of current allocators are not changed with
CHERI: Allocators are responsible for returning pointers to memory storage
that is, under its invariants, stable and unique for the lifetime of the
allocation.
However, allocator implementations must be adapted to CHERI C/C++: To achieve
memory protection for its callers, it must set CHERI capability properties
(such as bounds), taking into account properties such as capability alignment
and bounds compression, as well as (if required) integrate support for
revocation.
CHERI will then ensure that memory accesses to allocations made via pointers
are safe with respect to memory-safety properties such as spatial safety,
temporal safety, and so on.
69 changes: 69 additions & 0 deletions src/allocators/allocator-guarantees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
## Recommendation for allocator implementations

### Allocating memory

In addition to implementing the conventational invariants ensuring the
mutually exclusive allocation of memory, CHERI-aware implementations of
`malloc()` and `calloc()` must return a capability that has the following
properties:

* Is valid (i.e., with its tag bit set)
* Is unsealed
* Has bounds that permit access to the full requested range of the allocation
* Has bounds that do not permit access to any other current allocation, nor
allocator metadata, implementing non-aliasing spatial safety
* Has permissions that allow data load, data store, capability load, and
capability store
* Be sufficiently aligned to allow capability loads and stores at relative
offset 0 from the returned pointer

The allocator should:

* Fill reachable memory within bounds with zeroes before returning a pointer
to it

The allocator may:

* Provide precise bounds -- i.e., in which the lower bound is the lowest
address of the returned allocation, and the upper bound is the highest
address of the returned allocation plus one

### Freeing memory

CHERI-aware allocators must ignore, or trap on, calls to `free()` if the
passed capability:

* Is invalid (i.e, with its tag bit unset); or
* Is sealed; or
* Has bounds that have been changed from those returned by the allocator
when the allocation was first made; or
* Has permissions that disallow any of data load, data store, capability
load, or capability store

The allocator may:

* Implement fail-stop semantics if the call fails for one or more of the
above reasons.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably safe to leave up to the allocator whether a nonzero offset between address and base is acceptable.

The CHERIoT allocator overloads free() (well, heap_free()) to permit dropping claims on an object, and in that case it accepts interior, subset, &| attenuated pointers. That is, claims may be (taken and) dropped on subobjects (or, really, arbitrary slices of objects) but deallocation requires the original pointer (a la C).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the former: Are there use cases for non-zero offsets in heap allocators we are aware of beyond, perhaps, "deterministically trap on overflow rather than on underflow when imprecise bounds require padding"?

### Reallocating memory

The allocator must:

* Fail a call to `realloc()` if the passed capability violates any of the
properties checked for in `free()`.
* Return a capability with the same properties as those defined for
`malloc()`.

The allocator must not:

* Return a new pointer from `realloc()` that has an identical address to the
passed argument but differs in its bounds or other metadata.

The allocator should:

* Zero any newly accessible memory before returning a pointer to it,
including any allocator metadata.

The allocator may:

* Implement fail-stop semantics if the call fails for the above reasons.
82 changes: 82 additions & 0 deletions src/allocators/caller-guarantees.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
## Guarantees to the allocator consumer

This section describes properties that consumers of memory allocators may
rely on from all CHERI-enabled allocators.

### Allocating memory

Calls to `malloc()` and `calloc() must return capabilities that:

* Are valid (i.e., with its tag bit set)
* Are unsealed
* Have bounds that permit access to the full requested memory range of the
allocation
* Have bounds that do not permit access to any other current allocation, nor
allocator metadata, implementing non-aliasing spatial safety
* Have permissions that allow data load, data store, capability load, and
capability store
* Are sufficiently aligned to allow capability loads and stores at relative
offset 0 from the returned pointer

The allocator may:

* Fill reachable memory within bounds with zeroes before returning a pointer
to it
* Provide precise bounds, with the lower bound being the bottom address of
the allocation, and the upper bound being one byte above the top address of
the allocation

### Freeing memory

The caller must not pass as an argument to `free()` a capability that:

* Is invalid (i.e., without its tag bit set)
* Is unsealed
* Has bounds other than those on the original capability returned by
`malloc()`, `calloc()`, or `realloc()`
* Has permissions that differ from those on the original capability returned
by `malloc()`, `calloc()`, or `realloc()`

The allocator must not:

* Reuse storage associated with the allocation until there are no outstanding
valid capabilities that authorize access to the memory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit surprised to see this separately from the revocation point below?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought was that I wanted to avoid an assumption of revocation in the 'must' / 'must not' text, and simply state the invariants, leaving open the possibility of other techniques than revocation -- perhaps GC-related ones. I guess this is in the free() section, which is hardly a GC-centric API. But does the general argument for putting the invariant rather than the mechanism work from your perspective -- and, if so, is it the right invariant?


The allocator may:

* Fill reachable memory within the bounds of the allocation with zeroes after
it has been freed
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, if revocation is not prompt (CHERIoT, MTE), these zeros are advisory (and the allocator should re-zero memory before returning it from malloc).

* On virtual-memory-enabled systems, unmap reachable memory within the bounds
of the allocation after it has been freed.
* Revoke capabilities to the storage immediately upon free

If utilizing revocation, the allocator must:

* Ensure that any outstanding capabilities to the allocation become
non-dereferenceable

On revocation, the allocator may:

* Clear the tag of revoked capabilities

### Reallocating memory

The caller must not:

* Pass a capability to `realloc()` that violates any of the requirements for
a call to `free()`.

The allocator must:

* Conform to the guarantees associated with calls to `malloc()` and
`calloc()` when allocating memory in `realloc()`.

The allocator must not:

* Return a new pointer from `realloc()` that has an identical address to the
passed argument but differs in its bounds or other metadata.

The allocator may:

* Zero any newly accessible memory before returning a pointer to it.
* Always reallocate, returning a new pointer, on every call to `realloc()`.
2 changes: 2 additions & 0 deletions src/introduction/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ the following changes:
approach including with respect to integer-pointer type safety vs full
pointer type safety, compile-time uncertainty on types, bounds imprecision,
unions, stack temporal safety, and compiler optimizations.
* Document expectations for CHERI-enabled allocators, including guidance for
both consumers and implementers of allocation APIs.
* Numerous minor editorial and formatting improvements.
Loading