| // Copyright Microsoft and CHERIoT Contributors. |
| // SPDX-License-Identifier: MIT |
| |
| #include "compartment.h" |
| #include "token.h" |
| #include <cstdlib> |
| #define TEST_NAME "MessageQueue" |
| #include "tests.hh" |
| #include <FreeRTOS-Compat/queue.h> |
| #include <debug.hh> |
| #include <errno.h> |
| #include <queue.h> |
| #include <timeout.h> |
| |
| static constexpr size_t ItemSize = 8; |
| static constexpr size_t MaxItems = 2; |
| static constexpr char Message[MaxItems][ItemSize] = {"TstMsg0", "TstMsg1"}; |
| |
| extern "C" ErrorRecoveryBehaviour |
| compartment_error_handler(ErrorState *frame, size_t mcause, size_t mtval) |
| { |
| debug_log("Thread {} error handler invoked with mcause {}. PCC: {}", |
| thread_id_get(), |
| mcause, |
| frame->pcc); |
| return ErrorRecoveryBehaviour::ForceUnwind; |
| } |
| |
| void test_queue_unsealed() |
| { |
| char bytes[ItemSize]; |
| static MessageQueue *queue; |
| Timeout timeout{0, 0}; |
| debug_log("Testing queue send operations"); |
| auto checkSpace = [&](size_t expected, |
| SourceLocation loc = SourceLocation::current()) { |
| size_t items; |
| queue_items_remaining(queue, &items); |
| TEST(items == expected, |
| "MessageQueue test line {} reports {} items, should contain {}", |
| loc.line(), |
| items, |
| expected); |
| }; |
| int rv = |
| queue_create(&timeout, MALLOC_CAPABILITY, &queue, ItemSize, MaxItems); |
| TEST(queue->elementSize == ItemSize, |
| "MessageQueue element size is {}, expected {}", |
| queue->elementSize, |
| ItemSize); |
| TEST(queue->queueSize == MaxItems, |
| "MessageQueue size is {}, expected {}", |
| queue->queueSize, |
| MaxItems); |
| TEST(rv == 0, "MessageQueue creation failed with {}", rv); |
| rv = queue_send(&timeout, queue, Message[0]); |
| checkSpace(1); |
| TEST(rv == 0, "Sending the first message failed with {}", rv); |
| checkSpace(1); |
| rv = queue_send(&timeout, queue, Message[1]); |
| TEST(rv == 0, "Sending the second message failed with {}", rv); |
| checkSpace(2); |
| // MessageQueue is full, it should time out. |
| timeout.remaining = 5; |
| rv = queue_send(&timeout, queue, Message[1]); |
| TEST(rv == -ETIMEDOUT, |
| "Sending to a full queue didn't time out as expected, returned {}", |
| rv); |
| checkSpace(2); |
| debug_log("Testing queue receive operations"); |
| timeout.remaining = 10; |
| rv = queue_receive(&timeout, queue, bytes); |
| TEST(rv == 0, "Receiving the first message failed with {}", rv); |
| TEST(memcmp(Message[0], bytes, ItemSize) == 0, |
| "First message received but not as expected. Got {}", |
| bytes); |
| checkSpace(1); |
| rv = queue_receive(&timeout, queue, bytes); |
| TEST(rv == 0, "Receiving the second message failed with {}", rv); |
| TEST(memcmp(Message[1], bytes, ItemSize) == 0, |
| "Second message received but not as expected. Got {}", |
| bytes); |
| checkSpace(0); |
| timeout.remaining = 5; |
| rv = queue_receive(&timeout, queue, bytes); |
| TEST( |
| rv == -ETIMEDOUT, |
| "Receiving from an empty queue didn't time out as expected, returned {}", |
| rv); |
| // Check that the items remaining calculations are correct after overflow. |
| queue_send(&timeout, queue, Message[1]); |
| checkSpace(1); |
| queue_receive(&timeout, queue, bytes); |
| checkSpace(0); |
| queue_send(&timeout, queue, Message[1]); |
| checkSpace(1); |
| queue_receive(&timeout, queue, bytes); |
| checkSpace(0); |
| rv = queue_destroy(MALLOC_CAPABILITY, queue); |
| TEST(rv == 0, "MessageQueue deletion failed with {}", rv); |
| debug_log("All queue library tests successful"); |
| } |
| |
| void test_queue_sealed() |
| { |
| auto heapSpace = heap_quota_remaining(MALLOC_CAPABILITY); |
| Timeout t{1}; |
| SObj receiveHandle; |
| SObj sendHandle; |
| SObj queue; |
| char bytes[ItemSize]; |
| int ret = |
| queue_create_sealed(&t, MALLOC_CAPABILITY, &queue, ItemSize, MaxItems); |
| TEST(ret == 0, "MessageQueue creation failed with {}", ret); |
| ret = queue_receive_handle_create_sealed( |
| &t, MALLOC_CAPABILITY, queue, &receiveHandle); |
| TEST( |
| ret == 0, "MessageQueue receive endpoint creation failed with {}", ret); |
| ret = queue_send_handle_create_sealed( |
| &t, MALLOC_CAPABILITY, queue, &sendHandle); |
| TEST(ret == 0, "MessageQueue send endpoint creation failed with {}", ret); |
| |
| t = UnlimitedTimeout; |
| ret = queue_send_sealed(&t, receiveHandle, Message[1]); |
| TEST( |
| ret == -EINVAL, |
| "Sending with a receive handle should return -EINVAL ({}), returned {}", |
| EINVAL, |
| ret); |
| ret = queue_receive_sealed(&t, sendHandle, bytes); |
| TEST( |
| ret == -EINVAL, |
| "Sending with a receive handle should return -EINVAL ({}), returned {}", |
| EINVAL, |
| ret); |
| |
| ret = queue_send_sealed(&t, sendHandle, Message[1] + 1); |
| TEST(ret == -EPERM, |
| "Sending with short buffer should return -EPERM ({}), returned {}", |
| EPERM, |
| ret); |
| ret = queue_send_sealed(&t, sendHandle, Message[1]); |
| TEST( |
| ret == 0, "Sending with valid buffer should return 0, returned {}", ret); |
| ret = queue_receive_sealed(&t, receiveHandle, bytes + 1); |
| TEST(ret == -EPERM, |
| "Receiving with short buffer should return -EPERM ({}), returned {}", |
| EPERM, |
| ret); |
| size_t items; |
| ret = queue_items_remaining_sealed(receiveHandle, &items); |
| TEST(ret == 0, "Getting items remaining should return 0, returned {}", ret); |
| TEST(items == 1, "Items remaining should be 1, is {}", items); |
| ret = queue_items_remaining_sealed(sendHandle, &items); |
| TEST(ret == 0, "Getting items remaining should return 0, returned {}", ret); |
| TEST(items == 1, "Items remaining should be 1, is {}", items); |
| ret = queue_receive_sealed(&t, receiveHandle, bytes); |
| TEST(ret == 0, |
| "Receiving with valid buffer should return 0, returned {}", |
| ret); |
| |
| // Put something in the queue before we delete the send handle. |
| ret = queue_send_sealed(&t, sendHandle, Message[1]); |
| TEST( |
| ret == 0, "Sending with valid buffer should return 0, returned {}", ret); |
| |
| t = 1; |
| ret = queue_destroy_sealed(&t, MALLOC_CAPABILITY, sendHandle); |
| TEST(ret == 0, "MessageQueue send destruction failed with {}", ret); |
| |
| t = 1; |
| ret = queue_destroy_sealed(&t, MALLOC_CAPABILITY, receiveHandle); |
| TEST(ret == 0, "MessageQueue receive destruction failed with {}", ret); |
| |
| ret = queue_destroy_sealed(&t, MALLOC_CAPABILITY, queue); |
| TEST(ret == 0, "MessageQueue destruction failed with {}", ret); |
| |
| TEST(heap_quota_remaining(MALLOC_CAPABILITY) == heapSpace, |
| "Heap space leaked"); |
| debug_log("All queue compartment tests successful"); |
| } |
| |
| void test_queue_freertos() |
| { |
| debug_log("Testing FreeRTOS queues"); |
| auto quotaBegin = heap_quota_remaining(MALLOC_CAPABILITY); |
| auto freertosQueue = xQueueCreate(10, sizeof(int)); |
| vQueueDelete(freertosQueue); |
| auto quotaEnd = heap_quota_remaining(MALLOC_CAPABILITY); |
| TEST( |
| quotaBegin == quotaEnd, |
| "The FreeRTOS queue wrapper leaks memory: quota before is {}, after {}", |
| quotaBegin, |
| quotaEnd); |
| debug_log("All FreeRTOS queue tests successful"); |
| } |
| |
| int test_queue() |
| { |
| test_queue_unsealed(); |
| test_queue_sealed(); |
| test_queue_freertos(); |
| debug_log("All queue tests successful"); |
| return 0; |
| } |