| // Copyright Microsoft and CHERIoT Contributors. |
| // SPDX-License-Identifier: MIT |
| |
| #pragma once |
| |
| #include <cdefs.h> |
| #include <stddef.h> |
| #include <timeout.h> |
| |
| struct SKeyStruct; |
| struct SObjStruct; |
| typedef struct SKeyStruct *SKey; |
| typedef struct SObjStruct *SObj; |
| |
| #define INVALID_SKEY ((SKey)0) |
| #define INVALID_SOBJ ((SObj)0) |
| |
| __BEGIN_DECLS |
| |
| /** |
| * Create a new sealing key. |
| * |
| * This function is guaranteed to complete unless the allocator has exhausted |
| * the total number of sealing keys possible (2^32 - 2^24). After this point, |
| * it will never succeed. A compartment that is granted access to this entry |
| * point is trusted not to exhaust this resource. If you wish to allow a |
| * compartment to seal objects, but do not wish to allow it to allocate new |
| * sealing keys, then you should insert a proxy compartment that guarantees |
| * that it will call this API once and return a single key to the caller. |
| * |
| * The return value from this is a capability with the permit-seal and |
| * permit-unseal permissions. Callers may remove one or both of these |
| * permissions and delegate the resulting capability to allow other |
| * compartments to either seal or unseal the capabilities with this key. |
| * |
| * If the sealing keys have been exhausted then this will return |
| * `INVALID_SKEY`. This API is guaranteed never to block. |
| */ |
| SKey __cheri_compartment("alloc") token_key_new(void); |
| |
| /** |
| * Allocate a new object with size `sz`. |
| * |
| * An unsealed pointer to the newly allocated object is returned in |
| * `*unsealed`, the sealed pointer is returned as the return value. |
| * |
| * The `key` parameter must have both the permit-seal and permit-unseal |
| * permissions. |
| * |
| * On error, this returns `INVALID_SOBJ`. |
| */ |
| SObj __cheri_compartment("alloc") |
| token_sealed_unsealed_alloc(Timeout *timeout, |
| struct SObjStruct *heapCapability, |
| SKey key, |
| size_t sz, |
| void **unsealed); |
| |
| /** |
| * Same as token_sealed_unsealed_alloc() without getting the unsealed |
| * capability. |
| * |
| * The key must have the permit-seal permission. |
| */ |
| SObj __cheri_compartment("alloc") |
| token_sealed_alloc(Timeout *timeout, |
| struct SObjStruct *heapCapability, |
| SKey, |
| size_t); |
| |
| /** |
| * Unseal the obj given the key. |
| * |
| * The key must have the permit-unseal permission. |
| * |
| * @return unsealed obj if key and obj are valid and they match. nullptr |
| * otherwise |
| */ |
| [[cheri::interrupt_state(disabled)]] void * |
| __cheri_libcall token_obj_unseal(SKey, SObj); |
| |
| /** |
| * Destroy the obj given its key, freeing memory. |
| * |
| * The key must have the permit-unseal permission. |
| * |
| * @return 0 if no errors. -EINVAL if key or obj not valid, or they don't |
| * match, or double destroy. |
| */ |
| int __cheri_compartment("alloc") |
| token_obj_destroy(struct SObjStruct *heapCapability, SKey, SObj); |
| |
| /** |
| * Check whether the pair of a sealing key and a heap capability can unseal a |
| * sealed object. |
| * |
| * Returns 0 on success, `-EINVAL` if the key or object is not valid, or one of |
| * the errors from `heap_can_free` if the free would fail for other reasons. |
| */ |
| int __cheri_compartment("alloc") |
| token_obj_can_destroy(SObj heapCapability, SKey key, SObj object); |
| |
| __END_DECLS |
| |
| #ifdef __cplusplus |
| # include <utility> |
| |
| /** |
| * Helper template for representing a sealed capability created by the |
| * allocator's token API. |
| */ |
| template<typename T> |
| class Sealed |
| { |
| /// The raw sealed pointer |
| SObj sealedPointer; |
| |
| public: |
| /// Constructor from a raw sealed pointer. |
| Sealed(SObj sealedPointer) : sealedPointer(sealedPointer) {} |
| /** |
| * Explicit constructor from a sealed T*. This is explicit because this is |
| * used only in APIs that want to expose their internal sealed type as a |
| * public type. |
| */ |
| explicit Sealed(T *sealedPointer) |
| : sealedPointer(reinterpret_cast<SObj>(sealedPointer)) |
| { |
| } |
| /// Implicitly convert back to the wrapped value. |
| operator SObj() |
| { |
| return sealedPointer; |
| } |
| /** |
| * Explicitly convert to the real type. This is explicit because the |
| * resulting value cannot be used as a `T*` in the general case, it can be |
| * used only as an opaque `T*` that can be unsealed to give a usable `T*`. |
| */ |
| T *get() |
| { |
| return reinterpret_cast<T *>(sealedPointer); |
| } |
| }; |
| |
| /** |
| * Type-safe helper to allocate a sealed `T*`. Returns the sealed and unsealed |
| * pointers. |
| */ |
| template<typename T> |
| __always_inline std::pair<T *, Sealed<T>> |
| token_allocate(Timeout *timeout, struct SObjStruct *heapCapability, SKey key) |
| { |
| void *unsealed; |
| SObj sealed = token_sealed_unsealed_alloc( |
| timeout, heapCapability, key, sizeof(T), &unsealed); |
| return {static_cast<T *>(unsealed), Sealed<T>{sealed}}; |
| } |
| |
| template<typename T> |
| __always_inline T *token_unseal(SKey key, Sealed<T> sealed) |
| { |
| return static_cast<T *>(token_obj_unseal(key, sealed)); |
| } |
| #endif // __cplusplus |