| #include <cheri.hh> |
| #include <compartment.h> |
| #include <cstdlib> |
| #include <errno.h> |
| #include <locks.hh> |
| #include <queue.h> |
| #include <token.h> |
| |
| using namespace CHERI; |
| |
| using Debug = ConditionalDebug<false, "MessageQueue compartment">; |
| |
| namespace |
| { |
| __always_inline SKey handle_key() |
| { |
| return STATIC_SEALING_TYPE(MessageQueueHandle); |
| } |
| __always_inline SKey receive_key() |
| { |
| return STATIC_SEALING_TYPE(ReceiveHandle); |
| } |
| __always_inline SKey send_key() |
| { |
| return STATIC_SEALING_TYPE(SendHandle); |
| } |
| |
| /** |
| * Wrapper used for restricted endpoints. This is used to provide |
| * capabilities that allow only sending or receiving. Instances of this |
| * will be sealed with either `send_key()` or `receive_key()` to define |
| * their types. |
| */ |
| struct RestrictedEndpoint |
| { |
| MessageQueue *handle; |
| }; |
| |
| /** |
| * Unseal something that is either a queue handle or a restricted endpoint |
| * with the specified key. |
| */ |
| MessageQueue *unseal(SKey key, SObj handle) |
| { |
| MessageQueue *queue = nullptr; |
| if (auto *unsealed = |
| token_unseal(key, Sealed<RestrictedEndpoint>{handle})) |
| { |
| queue = unsealed->handle; |
| } |
| else if (auto *unsealed = |
| token_unseal(handle_key(), Sealed<MessageQueue>{handle})) |
| { |
| queue = unsealed; |
| } |
| return queue; |
| } |
| |
| } // namespace |
| |
| int queue_create_sealed(Timeout *timeout, |
| struct SObjStruct *heapCapability, |
| struct SObjStruct **outQueue, |
| size_t elementSize, |
| size_t elementCount) |
| { |
| ssize_t allocSize = queue_allocation_size(elementSize, elementCount); |
| if (allocSize < 0) |
| { |
| return -EINVAL; |
| } |
| |
| void *unsealed = nullptr; |
| // Allocate the space for the queue. |
| auto sealed = token_sealed_unsealed_alloc( |
| timeout, heapCapability, handle_key(), allocSize, &unsealed); |
| if (!unsealed) |
| { |
| return -ENOMEM; |
| } |
| |
| new (unsealed) MessageQueue(elementSize, elementCount); |
| *outQueue = sealed; |
| return 0; |
| } |
| |
| int queue_destroy_sealed(Timeout *timeout, |
| struct SObjStruct *heapCapability, |
| struct SObjStruct *queueHandle) |
| { |
| if (token_obj_unseal(handle_key(), queueHandle) != nullptr) |
| { |
| token_obj_destroy(heapCapability, handle_key(), queueHandle); |
| return 0; |
| } |
| if (token_obj_unseal(send_key(), queueHandle) != nullptr) |
| { |
| token_obj_destroy(heapCapability, send_key(), queueHandle); |
| return 0; |
| } |
| if (token_obj_unseal(receive_key(), queueHandle) != nullptr) |
| { |
| token_obj_destroy(heapCapability, receive_key(), queueHandle); |
| return 0; |
| } |
| return -EINVAL; |
| } |
| |
| int queue_send_sealed(Timeout *timeout, |
| struct SObjStruct *handle, |
| const void *src) |
| { |
| MessageQueue *queue = unseal(send_key(), handle); |
| if (!queue || !check_timeout_pointer(timeout)) |
| { |
| return -EINVAL; |
| } |
| return queue_send(timeout, queue, src); |
| } |
| |
| int queue_receive_sealed(Timeout *timeout, struct SObjStruct *handle, void *dst) |
| { |
| MessageQueue *queue = unseal(receive_key(), handle); |
| if (!queue || !check_timeout_pointer(timeout)) |
| { |
| return -EINVAL; |
| } |
| return queue_receive(timeout, queue, dst); |
| } |
| |
| int multiwaiter_queue_receive_init_sealed(struct EventWaiterSource *source, |
| struct SObjStruct *handle) |
| { |
| MessageQueue *queue = unseal(receive_key(), handle); |
| if (!queue) |
| { |
| return -EINVAL; |
| } |
| multiwaiter_queue_receive_init(source, queue); |
| return 0; |
| } |
| |
| int multiwaiter_queue_send_init_sealed(struct EventWaiterSource *source, |
| struct SObjStruct *handle) |
| { |
| MessageQueue *queue = unseal(send_key(), handle); |
| if (!queue) |
| { |
| return -EINVAL; |
| } |
| multiwaiter_queue_send_init(source, queue); |
| return 0; |
| } |
| |
| int queue_items_remaining_sealed(struct SObjStruct *handle, size_t *items) |
| { |
| MessageQueue *queue = unseal(send_key(), handle); |
| // This function takes either endpoint, so we need to try unsealing with |
| // both keys. |
| if (!queue) |
| { |
| if (auto *unsealed = |
| token_unseal(receive_key(), Sealed<RestrictedEndpoint>{handle})) |
| { |
| queue = unsealed->handle; |
| } |
| } |
| if (!queue) |
| { |
| return -EINVAL; |
| } |
| queue_items_remaining(queue, items); |
| return 0; |
| } |
| |
| int queue_receive_handle_create_sealed(struct Timeout *timeout, |
| struct SObjStruct *heapCapability, |
| struct SObjStruct *handle, |
| struct SObjStruct **outHandle) |
| { |
| MessageQueue *queue = |
| token_unseal(handle_key(), Sealed<MessageQueue>(handle)); |
| if (!queue) |
| { |
| return -EINVAL; |
| } |
| auto [unsealed, sealed] = token_allocate<RestrictedEndpoint>( |
| timeout, heapCapability, receive_key()); |
| if (!unsealed) |
| { |
| return -ENOMEM; |
| } |
| unsealed->handle = queue; |
| *outHandle = sealed; |
| return 0; |
| } |
| |
| int queue_send_handle_create_sealed(struct Timeout *timeout, |
| struct SObjStruct *heapCapability, |
| struct SObjStruct *handle, |
| struct SObjStruct **outHandle) |
| { |
| MessageQueue *queue = |
| token_unseal(handle_key(), Sealed<MessageQueue>(handle)); |
| if (!queue) |
| { |
| return -EINVAL; |
| } |
| auto [unsealed, sealed] = |
| token_allocate<RestrictedEndpoint>(timeout, heapCapability, send_key()); |
| if (!unsealed) |
| { |
| return -ENOMEM; |
| } |
| unsealed->handle = queue; |
| *outHandle = sealed; |
| return 0; |
| } |