CHERIoT RTOS
A compartmentalised RTOS for CHERIoT hardware
Loading...
Searching...
No Matches
token.h
Go to the documentation of this file.
1// Copyright Microsoft and CHERIoT Contributors.
2// SPDX-License-Identifier: MIT
3
4#pragma once
5
6#include <cdefs.h>
7#include <stddef.h>
8#include <stdlib.h>
9#include <timeout.h>
10
11/**
12 * \file
13 *
14 * APIs for dealing with type-safe opaque handles (sealed objects).
15 */
16
17/// Type used for `TokenKey`, do not use this directly.
18struct TokenKeyType;
19
20/**
21 * Sealing keys in CHERI are represented as capabilities. They have the same
22 * representation as pointers but have permissions that allow their address to
23 * refer to an abstract space of types, rather than a space of addresses on the
24 * memory bus. CHERIoT uses a pointer to a specific opaque type to expose this
25 * in the C/C++ type systems. Sealing keys should be created with the
26 * `STATIC_SEALING_TYPE` macro or the `token_key_new` function. Although they
27 * appear to be pointers, there is no way to dereference them that will not
28 * trap.
29 */
30typedef struct TokenKeyType *TokenKey;
31
32/// The old name for TokenKey, do not use in new code.
33typedef TokenKey SKey __attribute__((deprecated("SKey renamed to TokenKey")));
34
35__BEGIN_DECLS
36
37/**
38 * Create a new sealing key.
39 *
40 * This function is guaranteed to complete unless the allocator has exhausted
41 * the total number of sealing keys possible (2^32 - 2^24). After this point,
42 * it will never succeed. A compartment that is granted access to this entry
43 * point is trusted not to exhaust this resource. If you wish to allow a
44 * compartment to seal objects, but do not wish to allow it to allocate new
45 * sealing keys, then you should insert a proxy compartment that guarantees
46 * that it will call this API once and return a single key to the caller.
47 *
48 * The return value from this is a capability with the permit-seal and
49 * permit-unseal permissions. Callers may remove one or both of these
50 * permissions and delegate the resulting capability to allow other
51 * compartments to either seal or unseal the capabilities with this key.
52 *
53 * If the sealing keys have been exhausted then this will return
54 * null. This API is guaranteed never to block.
55 */
56TokenKey __cheri_compartment("allocator") token_key_new(void);
57
58/**
59 * Allocate a new object with size `sz`.
60 *
61 * An unsealed pointer to the newly allocated object is returned in
62 * `*unsealed`, the sealed pointer is returned as the return value.
63 * An invalid `unsealed` pointer does not constitute an error; the caller will
64 * still be given the sealed return value, assuming allocation was otherwise
65 * successful.
66 *
67 * The `key` parameter must have both the permit-seal and permit-unseal
68 * permissions.
69 *
70 * On error, this returns null.
71 */
72CHERI_SEALED(void *)
73__cheri_compartment("allocator")
74 token_sealed_unsealed_alloc(TimeoutArgument timeout,
75 AllocatorCapability heapCapability,
76 TokenKey key,
77 size_t sz,
78 void **unsealed);
79
80/**
81 * Same as token_sealed_unsealed_alloc() without getting the unsealed
82 * capability.
83 *
84 * The key must have the permit-seal permission.
85 */
86CHERI_SEALED(void *)
87__cheri_compartment("allocator")
88 token_sealed_alloc(TimeoutArgument timeout,
89 AllocatorCapability heapCapability,
91 size_t);
92
93/**
94 * Unseal the object given the key.
95 *
96 * The key may be either a static or dynamic key (i.e. one created with the
97 * `STATIC_SEALING_TYPE` macro or with `token_key_new`) and the object may be
98 * either allocated dynamically (via the token APIs) or statically (via the
99 * `DEFINE_STATIC_SEALED_VALUE` macro).
100 *
101 * Returns the unsealed object if the key and object are valid and of the
102 * correct type, null otherwise.
103 *
104 * This function is equivalent to calling both `token_obj_unseal_static` and
105 * `token_obj_unseal_dynamic` and returning the result of the first one that
106 * succeeds, or null if both fail.
107 */
108[[cheriot::interrupt_state(disabled)]] void *
109 __cheri_libcall token_obj_unseal(TokenKey, CHERI_SEALED(void *));
110
111/**
112 * Unseal the object given the key.
113 *
114 * The key must be a static sealing key (i.e. one created with the
115 * `STATIC_SEALING_TYPE` macro) and the object must be a statically sealed
116 * object (i.e. one created with the `DEFINE_STATIC_SEALED_VALUE` macro).
117 *
118 * Returns the unsealed object if the key and object are valid and of the
119 * correct type, null otherwise.
120 */
121[[cheriot::interrupt_state(disabled)]] void *
122 __cheri_libcall token_obj_unseal_static(TokenKey, CHERI_SEALED(void *));
123
124/**
125 * Unseal the object given the key.
126 *
127 * The key may be either a static or dynamic key (i.e. one created with the
128 * `STATIC_SEALING_TYPE` macro or with `token_key_new`) and the object must be
129 * allocated dynamically with `token_sealed_alloc` or
130 * `token_sealed_unsealed_alloc`.
131 *
132 * Returns the unsealed object if the key and object are valid and of the
133 * correct type, null otherwise.
134 */
135[[cheriot::interrupt_state(disabled)]] void *
136 __cheri_libcall token_obj_unseal_dynamic(TokenKey, CHERI_SEALED(void *));
137
138/**
139 * Mask the user permissions of a sealed object. The first argument is the
140 * sealed capability, the second is a permission mask to apply.
141 */
142#ifndef __cplusplus
143CHERI_SEALED(void *)
144__cheri_libcall token_permissions_and(CHERI_SEALED(void *), int);
145#else
146extern "C++" template<typename T>
147CHERI_SEALED(T *)
148token_permissions_and(CHERI_SEALED(T *) object, int permissions)
149{
150 CHERI_SEALED(void *)
151 __cheri_libcall token_permissions_and(CHERI_SEALED(void *), int);
152 return static_cast<CHERI_SEALED(T *)>(
153 token_permissions_and(object, permissions));
154}
155#endif
156
157/**
158 * Returns the user permissions bitmap for the sealed object.
159 */
160static inline int token_permissions_get(CHERI_SEALED(void *) object)
161{
162 return __builtin_cheri_address_get(object) & 0x7;
163}
164
165/**
166 * Destroy the object given its key, freeing memory.
167 *
168 * The key must have the permit-unseal permission.
169 *
170 * Returns 0 on success. `-EINVAL` if `key` or `obj` are not valid, or they
171 * don't match, or if `obj` has already been destroyed.
172 */
173int __cheri_compartment("allocator")
174 token_obj_destroy(AllocatorCapability heapCapability,
175 TokenKey,
176 CHERI_SEALED(void *));
177
178/**
179 * Check whether the pair of a sealing key and a heap capability can free a
180 * sealed object.
181 *
182 * Returns 0 on success, `-EINVAL` if the key or object is not valid, or one of
183 * the errors from `heap_can_free` if the free would fail for other reasons.
184 */
185int __cheri_compartment("allocator")
186 token_obj_can_destroy(AllocatorCapability heapCapability,
187 TokenKey key,
188 CHERI_SEALED(void *) object);
189
190__END_DECLS
191
192#ifdef __cplusplus
193# include <utility>
194
195/**
196 * Helper template for representing a sealed capability created by the
197 * allocator's token API.
198 */
199template<typename T>
200class Sealed
201{
202 /// The raw sealed pointer
203 CHERI_SEALED(T *) sealedPointer;
204
205 public:
206# if __has_extension(cheri_sealed_pointers) && \
207 !defined(CHERIOT_NO_SEALED_POINTERS)
208 /// Constructor from a raw sealed pointer.
209 Sealed(CHERI_SEALED(T *) sealedPointer) : sealedPointer(sealedPointer) {}
210# else
211 Sealed(void *sealedPointer)
212 : sealedPointer(reinterpret_cast<TokenObjectType *>(sealedPointer))
213 {
214 }
215# endif
216
217 /**
218 * Explicit constructor from a sealed T*. This is explicit because this is
219 * used only in APIs that want to expose their internal sealed type as a
220 * public type.
221 */
222 template<typename U>
224 : sealedPointer(reinterpret_cast<decltype(sealedPointer)>(sealedPointer))
225 {
226 }
227 /// Implicitly convert back to the wrapped value.
228 operator decltype(sealedPointer)()
229 {
230 return sealedPointer;
231 }
232
233 /**
234 * Explicitly convert to the real type. This is explicit because the
235 * resulting value cannot be used as a `T*` in the general case, it can be
236 * used only as an opaque `T*` that can be unsealed to give a usable `T*`.
237 */
238 CHERI_SEALED(T *) get()
239 {
240 return sealedPointer;
241 }
242 /**
243 * Return the tag of the underlying pointer
244 */
245 bool is_valid()
246 {
247 return __builtin_cheri_tag_get(get());
248 }
249};
250
251/**
252 * Type-safe helper to allocate a sealed `T*`. Returns the sealed and unsealed
253 * pointers.
254 *
255 * Callers should check the sealed capability's tag to determine success.
256 */
257template<typename T>
258__always_inline std::pair<T *, Sealed<T>>
259token_allocate(TimeoutArgument timeout,
260 AllocatorCapability heapCapability,
261 TokenKey key)
262{
263 /*
264 * Explicitly initialize unsealed, since callers like to check it, and not
265 * the sealed result, for validity.
266 */
267 void *unsealed = nullptr;
268 CHERI_SEALED(void *)
270 timeout, heapCapability, key, sizeof(T), &unsealed);
271 return {static_cast<T *>(unsealed),
272# if __has_extension(cheri_sealed_pointers) && \
273 !defined(CHERIOT_NO_SEALED_POINTERS)
274 static_cast<CHERI_SEALED(T *)>(sealed)
275# else
276 Sealed<T>{sealed}
277# endif
278 };
279}
280
281/**
282 * Type-safe helper that unseal a `Sealed<T>` and returns a `T*`.
283 * If `key` matches the type used for sealing `sealed`, this returns the
284 * unsealed value, otherwise it returns `NULL` / `nullptr`.
285 */
286template<typename T>
287__always_inline T *token_unseal(TokenKey key, Sealed<T> sealed)
288{
289 return static_cast<T *>(token_obj_unseal(key, sealed));
290}
291
292#endif // __cplusplus
293
294/**
295 * Type-safe helper that unseal a `T *__sealed_capability` and returns a `T*`.
296 * If `key` matches the type used for sealing `sealed`, this returns the
297 * unsealed value, otherwise it returns `NULL` / `nullptr`.
298 */
299#if __has_extension(cheri_sealed_pointers) && \
300 !defined(CHERIOT_NO_SEALED_POINTERS)
301# ifdef __cplusplus
302template<typename T>
303__always_inline T *token_unseal(TokenKey key, T *__sealed_capability sealed)
304{
305 return static_cast<T *>(token_obj_unseal(key, sealed));
306}
307# else
308# define token_unseal(key, sealed) /*NOLINT*/ \
309 ((__typeof__(*(sealed)) *)token_obj_unseal(key, sealed))
310# endif
311#endif
Helper template for representing a sealed capability created by the allocator's token API.
Definition token.h:201
CHERI_SEALED(T *) get()
Explicitly convert to the real type.
Definition token.h:238
bool is_valid()
Return the tag of the underlying pointer.
Definition token.h:245
Sealed(U *sealedPointer)
Explicit constructor from a sealed T*.
Definition token.h:223
operator decltype sealedPointer()
Implicitly convert back to the wrapped value.
Definition token.h:228
This file contains the types used for timeouts across scheduler APIs.
token_sealed_alloc(TimeoutArgument timeout, AllocatorCapability heapCapability, TokenKey, size_t)
Same as token_sealed_unsealed_alloc() without getting the unsealed capability.
T * token_unseal(TokenKey key, Sealed< T > sealed)
Type-safe helper that unseal a Sealed<T> and returns a T*.
Definition token.h:287
std::pair< T *, Sealed< T > > token_allocate(TimeoutArgument timeout, AllocatorCapability heapCapability, TokenKey key)
Type-safe helper to allocate a sealed T*.
Definition token.h:259
TokenKey SKey
The old name for TokenKey, do not use in new code.
Definition token.h:33
token_sealed_unsealed_alloc(TimeoutArgument timeout, AllocatorCapability heapCapability, TokenKey key, size_t sz, void **unsealed)
Allocate a new object with size sz.
static int token_permissions_get(CHERI_SEALED(void *) object)
Returns the user permissions bitmap for the sealed object.
Definition token.h:160
void *__cheri_libcall token_obj_unseal_dynamic(TokenKey, CHERI_SEALED(void *))
Unseal the object given the key.
int token_obj_can_destroy(AllocatorCapability heapCapability, TokenKey key, CHERI_SEALED(void *) object)
Check whether the pair of a sealing key and a heap capability can free a sealed object.
void *__cheri_libcall token_obj_unseal_static(TokenKey, CHERI_SEALED(void *))
Unseal the object given the key.
TokenKey token_key_new(void)
Create a new sealing key.
int token_obj_destroy(AllocatorCapability heapCapability, TokenKey, CHERI_SEALED(void *))
Destroy the object given its key, freeing memory.
token_permissions_and(CHERI_SEALED(T *) object, int permissions)
Mask the user permissions of a sealed object.
Definition token.h:148
void *__cheri_libcall token_obj_unseal(TokenKey, CHERI_SEALED(void *))
Unseal the object given the key.
struct TokenKeyType * TokenKey
Sealing keys in CHERI are represented as capabilities.
Definition token.h:30