User Object

User space object manipulation is mainly implemented in lib/libcheri/libcheri_type.c.

User space type range

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:

  • non-system type numbers: [1, $2^{22}$ - 1 ];
  • 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);

Type initialization and sealing root capability

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(..).

Type allocation via libcheri

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));
}
Created Aug 27, 2019 // Last Updated Sep 5, 2019

If you could revise
the fundmental principles of
computer system design
to improve security...

... what would you change?