User space object manipulation is mainly implemented in lib/libcheri/libcheri_type.c
.
Total user space type range is [0 , $2^{23}$ - 1 ], and kernel space is [$2^{23}$, $2^{24}$ -1 ].
The user space is further splited into two ranges:
system type numbers: [$2^{22}$, $2^{23}$ - 1 ];
// file:
// lib/libcheri/libcheri_type.c
/* The number of bits in the type field of a capability. */
static const int libcheri_cap_type_bits = 24;
/* The next non-system type number to allocate. */
static _Atomic(uint64_t) libcheri_type_next = 1;
/* The maximum non-system type number to allocate (plus one). */
static const int libcheri_type_max = 1<<(libcheri_cap_type_bits-2);
/* The next system type number to allocate. */
static _Atomic(uint64_t) libcheri_system_type_next = libcheri_type_max;
/* The maximum system type number to allocate (plus one). */
static const int libcheri_system_type_max = 1<<(libcheri_cap_type_bits-1);
libcheri_sealing_root
is the root sealing capability of the types provenance tree. Defined as:
// file:
// ./lib/libcheri/libcheri_type.c
static void * __capability libcheri_sealing_root;
In initialization step, libcheri_type_init(void)
, the root sealing capability is requested from the kernel, by calling sysarch(..)
.
if (sysarch(CHERI_GET_SEALCAP, &libcheri_sealing_root) < 0)
libcheri_sealing_root = NULL;
assert((cheri_getperm(libcheri_sealing_root) & CHERI_PERM_SEAL) != 0);
assert(cheri_getlen(libcheri_sealing_root) != 0);
libcheri_type_init(void)
is called during type allocation for the first time, more details in libcheri_alloc_type_capability(..)
.
Object type is allocated by libcheri_type_alloc()
, a wrapper of libcheri_alloc_type_capability()
, which will return a capability with a type.
The sealing capability is required. Here is libcheri_sealing_root
.
// file:
// lib/libcheri/libcheri_type.c
/**
* A simple type allocator. The `source` argument specifies the pointer value
* that will be atomically incremented and whose current value should give the
* integer representation of the type to allocate. The `max` argument
* specifies value that the returned type must be less than.
*/
static inline void * __capability
libcheri_alloc_type_capability(_Atomic(uint64_t) *source, uint64_t max)
{
void * __capability new_type_cap;
uint64_t next;
/*
* We require that this counter be strictly monotonic, but we don't
* need to establish a happens-before relationship with any other
* thread.
*/
next = atomic_fetch_add_explicit(source, 1, memory_order_relaxed);
/*
* If we've run out of types, return NULL so that we get an obvious
* failure.
*/
if (next > max) {
return (NULL);
}
/*
* On first use, query the root object-type capability from the
* kernel.
*/
if ((cheri_getperm(libcheri_sealing_root) & CHERI_PERM_SEAL) == 0)
libcheri_type_init();
new_type_cap = cheri_maketype(libcheri_sealing_root, next);
return (new_type_cap);
}
/*
* A [very] simple CHERI type allocator.
*/
void * __capability
libcheri_type_alloc(void)
{
return (libcheri_alloc_type_capability(&libcheri_type_next,
libcheri_type_max));
}
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?