Kernel object manipulation is mainly defined in sys/cheri/cheri_otype.c.
A global sealing capability kernel_sealcap is used throughout the kernel; it is initiliazed in _start().
kernel_sealcap is initialized to have PCC with offset CHERI_SEALCAP_KERNEL_BASE, bounds CHERI_SEALCAP_KERNEL_LENGTH, perms CHERI_SEALCAP_KERNEL_PERMS.
See more on Cheri kernel booting
A struct unrhdr *cheri_otypes is used to maintain a unit number space for type value allocations.
The unit number space is created as a set of all possible type values for allocation. The unit number space is named cheri_otypes, which is defined as:
static struct unrhdr *cheri_otypes;unit number space: It is a number space described by the unit number space header as below:
// file:
// sys/sys/_unrhdr.h
/* Header element for a unr number space. */
struct unrhdr {
TAILQ_HEAD(unrhd,unr) head;
u_int low; /* Lowest item */
u_int high; /* Highest item */
u_int busy; /* Count of allocated items */
u_int alloc; /* Count of memory allocations */
u_int first; /* items in allocated from start */
u_int last; /* items free at end */
struct mtx *mtx;
TAILQ_HEAD(unrfr,unr) ppfree; /* Items to be freed after mtx
lock dropped */
};Basically, it maintains a set of unit numbers from low to high, while keeping track of the number of allocated items, the items allocated, etc.
The unit numeber space is initialized by creating a struct unrhdr which represents a set of numbers in [CHERI_OTYPE_KERN_MIN, CHERI_OTYPE_KERN_MAX], with a lock cheri_otype_lock, which is used to lock the set internally evey time the unit number is allocated/freed from the set.
// file:
// sys/cheri/cheri_otype.c
static void cheri_otype_init(void *dummy __unused)
{
mtx_init(&cheri_otype_lock, "CHERI object type lock", NULL, MTX_DEF);
cheri_otypes = new_unrhdr(CHERI_OTYPE_KERN_MIN,
CHERI_OTYPE_KERN_MAX, &cheri_otype_lock);
}
SYSINIT(cheri_otype_init, SI_SUB_LOCK, SI_ORDER_FIRST, cheri_otype_init, NULL);This function is called during system startup via registeration of SYSINT macro above.
A type is allocated via cheri_otype_alloc(). It returns an otype_t, which is defined as a capability type:
// file
// sys/sys/types.h
typedef void * __capability otype_t;cheri_otype_alloc use a global sealing capability, kernel_sealcap, to create a capability with a new type. Type value is generated by alloc_unr() which allocates a unit number from unit number space cheri_otype.
kenrel_sealcap is initialized during boot.
// file:
// sys/cheri/cheri_otype.c
otype_t cheri_otype_alloc(void)
{
u_int type;
type = alloc_unr(cheri_otypes);
if (type == -1)
return (NULL);
return (cheri_maketype(kernel_sealcap,
type - CHERI_SEALCAP_KERNEL_BASE));
}cheri_otype_free is used to return a type to the pool. Ideally we would ensure that no capablities of this type remain in memory, but that would be VERY expensive.
In practice, most consumers will never free a type.
// file:
// sys/cheri/cheri_otype.c
void cheri_otype_free(otype_t cap)
{
u_int type;
type = cheri_getbase(cap);
free_unr(cheri_otypes, type);
}Until CheriBSD commit (e430b100ab1e8408ec92655ad26786bb5ec23ef1) on Aug 29, there is no call to ‘cheri_otype_free()’ from any place in the entire cheribsd source code (master branch).
Until CheriBSD commit (e430b100ab1e8408ec92655ad26786bb5ec23ef1) on Aug 29, there is no call to ‘cheri_otype_alloc()’ from any place in the entire cheribsd source code (master branch).
However, cheri_otype_init is called during system boot.
If you could revise
the fundmental principles of
computer system design
to improve security...
... what would you change?