tests: catch up with compiler warnings
diff --git a/tests/allocator-test.cc b/tests/allocator-test.cc
index 98de832..8a5887d 100644
--- a/tests/allocator-test.cc
+++ b/tests/allocator-test.cc
@@ -101,8 +101,7 @@
"Checked that all allocations have been deallocated ({} of {})",
static_cast<int>(i),
static_cast<int>(TestIterations));
- Timeout t{1};
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to sleep");
}
allocations.clear();
}
@@ -122,8 +121,7 @@
freeStart.wait(0);
// One extra sleep to make sure that we're really in the blocking
// sleep.
- Timeout t{2};
- thread_sleep(&t);
+ TEST(sleep(2) >= 0, "Failed to sleep");
debug_log(
"Deallocation thread resuming, freeing pool of allocations");
// Free all of the allocations to make space.
@@ -131,7 +129,9 @@
{
if (allocation != nullptr)
{
- heap_free(MALLOC_CAPABILITY, allocation);
+ TEST_EQUAL(heap_free(MALLOC_CAPABILITY, allocation),
+ 0,
+ "Could not free allocation");
}
}
// Notify the parent thread that we're done.
@@ -426,6 +426,7 @@
void test_hazards()
{
+ int sleeps;
debug_log("Before allocating, quota left: {}",
heap_quota_remaining(SECOND_HEAP));
Timeout longTimeout{1000};
@@ -446,21 +447,22 @@
// Exiting this task will cause this closure to be freed, which
// will collect dangling hazard pointers. Wait for long enough for
// the heap check to work.
- t = 1;
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to sleep");
});
// Allow the async function to run and establish hazards
+ sleeps = 0;
while (state.load() != 1)
{
- Timeout t{1};
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to sleep");
+ TEST(sleeps++ < 100,
+ "Background thread failed to establish hazards");
}
debug_log("Before freeing, quota left: {}",
heap_quota_remaining(SECOND_HEAP));
- heap_free(SECOND_HEAP, ptr);
+ TEST_EQUAL(heap_free(SECOND_HEAP, ptr), 0, "First free failed");
debug_log("After free 1, quota left: {}",
heap_quota_remaining(SECOND_HEAP));
- heap_free(SECOND_HEAP, ptr2);
+ TEST_EQUAL(heap_free(SECOND_HEAP, ptr2), 0, "Second free failed");
debug_log("After free 2, quota left: {}",
heap_quota_remaining(SECOND_HEAP));
TEST(Capability{ptr}.is_valid(),
@@ -471,21 +473,21 @@
ptr2);
state = 2;
// Yield to allow the hazards to be dropped.
- Timeout t{1};
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to yield to drop hazards");
// Try a double free. This may logically succeed, but should not affect
// our quota.
- heap_free(SECOND_HEAP, ptr);
+ TEST_EQUAL(heap_free(SECOND_HEAP, ptr),
+ -EPERM,
+ "Attempt to free freed but hazarded pointer not EPERM");
// Sleep again to make sure that the lambda from our async is gone.
// The logs may make it take more than one quantum in debug builds.
// The next test requires all memory allocated from the malloc
// capability to be freed before it starts.
- int sleeps = 0;
+ sleeps = 0;
while (heap_quota_remaining(MALLOC_CAPABILITY) < MALLOC_QUOTA &&
heap_quota_remaining(MALLOC_CAPABILITY) > 0)
{
- Timeout t{1};
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to sleep");
TEST(sleeps++ < 100,
"Sleeping for too long waiting for async lambda to be freed");
}
@@ -712,7 +714,7 @@
TEST(ret == 0, "Freeing array failed: {}", ret);
test_blocking_allocator();
- heap_quarantine_empty();
+ TEST_EQUAL(heap_quarantine_empty(), 0, "Could not flush quarantine");
test_revoke();
test_fuzz();
allocations.clear();
diff --git a/tests/compartment_calls-test.cc b/tests/compartment_calls-test.cc
index a8ed1ee..d8c8368 100644
--- a/tests/compartment_calls-test.cc
+++ b/tests/compartment_calls-test.cc
@@ -68,7 +68,10 @@
test_number_of_arguments();
- test_incorrect_export_table(nullptr, &outTestFailed);
+ TEST_EQUAL(
+ test_incorrect_export_table(nullptr, &outTestFailed),
+ 0,
+ "Test incorrect entry point without error handler bad return value");
TEST(outTestFailed == false,
"Test incorrect entry point without error handler failed");
return 0;
diff --git a/tests/compartment_calls.h b/tests/compartment_calls.h
index b740f31..6e4103d 100644
--- a/tests/compartment_calls.h
+++ b/tests/compartment_calls.h
@@ -38,11 +38,10 @@
const int *x4,
int x5,
int x6);
-__cheri_compartment("compartment_calls_inner") void test_incorrect_export_table(
+__cheri_compartment("compartment_calls_inner") int test_incorrect_export_table(
__cheri_callback void (*fn)(),
bool *outTestFailed);
__cheri_compartment(
"compartment_calls_inner_with_"
"handler") int test_incorrect_export_table_with_handler(__cheri_callback int (*fn)());
-__cheri_compartment("compartment_calls_outer") void compartment_call_outer();
constexpr int ConstantValue = 0x41414141;
diff --git a/tests/compartment_calls_inner.cc b/tests/compartment_calls_inner.cc
index 43fd9d0..efd6138 100644
--- a/tests/compartment_calls_inner.cc
+++ b/tests/compartment_calls_inner.cc
@@ -97,8 +97,8 @@
return 0;
}
-void test_incorrect_export_table(__cheri_callback void (*fn)(),
- bool *outTestFailed)
+int test_incorrect_export_table(__cheri_callback void (*fn)(),
+ bool *outTestFailed)
{
/*
* Trigger a cross-compartment call with an invalid export entry.
@@ -111,4 +111,6 @@
fn();
*outTestFailed = false;
+
+ return 0;
}
diff --git a/tests/crash_recovery-test.cc b/tests/crash_recovery-test.cc
index cc67b24..1de2977 100644
--- a/tests/crash_recovery-test.cc
+++ b/tests/crash_recovery-test.cc
@@ -49,7 +49,7 @@
int test_crash_recovery()
{
debug_log("Calling crashy compartment indirectly");
- test_crash_recovery_outer(0);
+ TEST_EQUAL(test_crash_recovery_outer(0), 0, "Indirect crash failed");
check_stack();
TEST(crashes == 0, "Ran crash handler for outer compartment");
debug_log("Compartment with no error handler returned normally after "
diff --git a/tests/crash_recovery.h b/tests/crash_recovery.h
index 9c2c7bd..ebbdb56 100644
--- a/tests/crash_recovery.h
+++ b/tests/crash_recovery.h
@@ -6,7 +6,7 @@
__cheri_compartment("crash_recovery_inner") void *test_crash_recovery_inner(
int);
-__cheri_compartment("crash_recovery_outer") void test_crash_recovery_outer(int);
+__cheri_compartment("crash_recovery_outer") int test_crash_recovery_outer(int);
/**
* Checks that the stack is entirely full of zeroes below the current stack
diff --git a/tests/crash_recovery_outer.cc b/tests/crash_recovery_outer.cc
index 9d3513d..6b59b7e 100644
--- a/tests/crash_recovery_outer.cc
+++ b/tests/crash_recovery_outer.cc
@@ -6,7 +6,7 @@
#include <cheri.hh>
#include <errno.h>
-void test_crash_recovery_outer(int)
+int test_crash_recovery_outer(int)
{
debug_log(
"Calling crashy compartment from compartment with no error handler");
@@ -19,4 +19,5 @@
debug_log("Calling crashy compartment returned to compartment with no "
"error handler. Return value: {}",
ret);
+ return 0;
}
diff --git a/tests/futex-test.cc b/tests/futex-test.cc
index 4abfe6c..433f110 100644
--- a/tests/futex-test.cc
+++ b/tests/futex-test.cc
@@ -25,6 +25,7 @@
{
static uint32_t futex;
int ret;
+ int sleeps;
// Make sure that waking a futex with no sleepers doesn't crash!
ret = futex_wake(&futex, 1);
TEST(ret == 0, "Waking a futex with no sleepers should return 0");
@@ -32,7 +33,7 @@
// has been set to 1.
async([]() {
futex = 1;
- futex_wake(&futex, 1);
+ (void)futex_wake(&futex, 1);
});
debug_log("Calling blocking futex_wait");
ret = futex_wait(&futex, 0);
@@ -123,7 +124,7 @@
while (state != 1)
{
Timeout t{3};
- thread_sleep(&t);
+ (void)thread_sleep(&t);
}
debug_log("Consuming all CPU on medium-priority thread");
state = 2;
@@ -144,27 +145,31 @@
debug_log("Low-priority thread finished, unlocking");
state = 4;
futex = 0;
- futex_wake(&futex, 1);
+ (void)futex_wake(&futex, 1);
}
};
async(priorityBug);
async(priorityBug);
debug_log("Waiting for background threads to enter the right state");
- while (state != 2)
+ for (sleeps = 0; (sleeps < 100) && (state != 2); sleeps++)
{
- Timeout t{3};
- thread_sleep(&t);
+ TEST(sleep(3) >= 0, "Failed to sleep");
}
+ TEST(sleeps < 100, "Waited too long for background threads");
debug_log("High-priority thread attempting to acquire futex owned by "
"low-priority thread without priority propagation");
state = 3;
t.remaining = 1;
- futex_timed_wait(&t, &futex, futex, FutexNone);
+ TEST_EQUAL(futex_timed_wait(&t, &futex, futex, FutexNone),
+ -ETIMEDOUT,
+ "futex_timed_wait failed");
TEST(futex != 0, "Made progress surprisingly!");
debug_log("High-priority thread attempting to acquire futex owned by "
"low-priority thread with priority propagation");
t.remaining = 4;
- futex_timed_wait(&t, &futex, futex, FutexPriorityInheritance);
+ TEST_EQUAL(futex_timed_wait(&t, &futex, futex, FutexPriorityInheritance),
+ 0,
+ "futex_timed_wait failed");
TEST(futex == 0, "Failed to make progress!");
futex = 1234;
diff --git a/tests/list-test.cc b/tests/list-test.cc
index 5ea7700..bf6f66a 100644
--- a/tests/list-test.cc
+++ b/tests/list-test.cc
@@ -174,7 +174,10 @@
// we do not use here), not to the removed element.
LinkedObject::ObjectRing *removedCell = objects.first();
ds::linked_list::remove(objects.first());
- heap_free(MALLOC_CAPABILITY, LinkedObject::from_ring(removedCell));
+ TEST_EQUAL(
+ heap_free(MALLOC_CAPABILITY, LinkedObject::from_ring(removedCell)),
+ 0,
+ "Failed to free removed cell");
TEST(LinkedObject::from_ring(objects.first())->data == 1,
"First element of the list is incorrect after removing the first "
"element, expected {}, got {}",
@@ -191,7 +194,8 @@
{
struct LinkedObject *o = LinkedObject::from_ring(cell);
cell = cell->cell_next();
- heap_free(MALLOC_CAPABILITY, o);
+ TEST_EQUAL(
+ heap_free(MALLOC_CAPABILITY, o), 0, "Failed to free list object");
counter++;
}
@@ -213,7 +217,10 @@
// removed cell. This is great here because we will free the
// object anyways. We could also use `remove` here.
auto l = ds::linked_list::unsafe_remove(cell);
- heap_free(MALLOC_CAPABILITY, LinkedObject::from_ring(cell));
+ TEST_EQUAL(
+ heap_free(MALLOC_CAPABILITY, LinkedObject::from_ring(cell)),
+ 0,
+ "Failed to free searched object");
// `l` is the predecessor of `cell` in the residual ring, so
// this does exactly what we want when `::search` iterates.
cell = l;
@@ -221,7 +228,9 @@
return false;
});
// `::search` does not visit the element passed (`middle`)
- heap_free(MALLOC_CAPABILITY, LinkedObject::from_ring(middle));
+ TEST_EQUAL(heap_free(MALLOC_CAPABILITY, LinkedObject::from_ring(middle)),
+ 0,
+ "Failed to free middle object");
counter++;
TEST(counter == NumberOfListElements - 1,
diff --git a/tests/multiwaiter-test.cc b/tests/multiwaiter-test.cc
index b6b77dd..16cbc04 100644
--- a/tests/multiwaiter-test.cc
+++ b/tests/multiwaiter-test.cc
@@ -47,7 +47,7 @@
sleep(1);
debug_log("Waking futex from background thread");
*futexWord = value;
- futex_wake(futexWord, 1);
+ TEST(futex_wake(futexWord, 1) >= 0, "futex_wait failed");
});
};
diff --git a/tests/stack-test.cc b/tests/stack-test.cc
index b3b894f..205a398 100644
--- a/tests/stack-test.cc
+++ b/tests/stack-test.cc
@@ -27,16 +27,17 @@
return ErrorRecoveryBehaviour::ForceUnwind;
}
-__cheri_callback void test_trusted_stack_exhaustion()
+__cheri_callback int test_trusted_stack_exhaustion()
{
- exhaust_trusted_stack(&test_trusted_stack_exhaustion,
- &threadStackTestFailed);
+ return exhaust_trusted_stack(&test_trusted_stack_exhaustion,
+ &threadStackTestFailed);
}
-__cheri_callback void cross_compartment_call()
+__cheri_callback int cross_compartment_call()
{
TEST(false,
"Cross compartment call with invalid CSP shouldn't be reachable");
+ return -EINVAL;
}
namespace
@@ -70,7 +71,10 @@
void expect_handler(bool handlerExpected)
{
debug_log("Expected to invoke the handler? {}", handlerExpected);
- set_expected_behaviour(&threadStackTestFailed, handlerExpected);
+ TEST_EQUAL(
+ set_expected_behaviour(&threadStackTestFailed, handlerExpected),
+ 0,
+ "Failed to set expectations");
}
__attribute__((used)) extern "C" int test_small_stack()
@@ -114,7 +118,7 @@
// Defeat the compiler optimisation that may turn our first call to this into a
// call. If the compiler does this then we will fail on an even number of
// cross-compartment calls not an odd number.
-__cheri_callback void (*volatile crossCompartmentCall)();
+__cheri_callback int (*volatile crossCompartmentCall)();
/*
* The stack tests should cover the edge-cases scenarios for both
@@ -139,7 +143,7 @@
TEST(ret == -ENOTENOUGHSTACK,
"test_with_small_stack failed, returned {} with 128-byte stack",
ret);
- __cheri_callback void (*callback)() = cross_compartment_call;
+ __cheri_callback int (*callback)() = cross_compartment_call;
crossCompartmentCall = test_trusted_stack_exhaustion;
debug_log("exhaust trusted stack, do self recursion with a cheri_callback");
@@ -148,12 +152,15 @@
debug_log("exhausting the compartment stack");
expect_handler(false);
- exhaust_thread_stack();
+ TEST_EQUAL(
+ exhaust_thread_stack(), -ECOMPARTMENTFAIL, "exhaust_thread_stack failed");
debug_log("exhausting the compartment stack during a switcher call");
expect_handler(false);
threadStackTestFailed = true;
- exhaust_thread_stack_spill(callback);
+ TEST_EQUAL(exhaust_thread_stack_spill(callback),
+ 0,
+ "exhaust_thread_stack_spill failed");
TEST(threadStackTestFailed == false, "switcher did not return error");
debug_log("modifying stack permissions on fault");
@@ -164,7 +171,9 @@
compartmentStackPermissions.without(permissionToRemove);
debug_log("Permissions: {}", permissions);
expect_handler(stack_is_mostly_valid(permissions));
- set_csp_permissions_on_fault(permissions);
+ TEST_EQUAL(set_csp_permissions_on_fault(permissions),
+ -ECOMPARTMENTFAIL,
+ "Unexpected success with restricted permissions");
}
debug_log("modifying stack permissions on cross compartment call");
@@ -173,16 +182,22 @@
auto permissions =
compartmentStackPermissions.without(permissionToRemove);
debug_log("Permissions: {}", permissions);
- set_csp_permissions_on_call(permissions, callback);
+ TEST_EQUAL(set_csp_permissions_on_call(permissions, callback),
+ -ECOMPARTMENTFAIL,
+ "Unexpected success with restricted permissions");
}
debug_log("invalid stack on fault");
expect_handler(false);
- test_stack_invalid_on_fault();
+ TEST_EQUAL(test_stack_invalid_on_fault(),
+ -ECOMPARTMENTFAIL,
+ "stack_invalid_on_fault failed");
debug_log("invalid stack on cross compartment call");
expect_handler(false);
+ TEST_EQUAL(test_stack_invalid_on_call(callback),
+ -ECOMPARTMENTFAIL,
+ "stack_invalid_on_call failed");
- test_stack_invalid_on_call(callback);
return 0;
}
diff --git a/tests/stack_integrity_thread.cc b/tests/stack_integrity_thread.cc
index c897432..ee9f427 100644
--- a/tests/stack_integrity_thread.cc
+++ b/tests/stack_integrity_thread.cc
@@ -64,14 +64,16 @@
* Set up the handler expectations. Takes the caller's error flag and
* whether the handler is expected as arguments.
*/
-void set_expected_behaviour(bool *outTestFailed, bool handlerExpected)
+int set_expected_behaviour(bool *outTestFailed, bool handlerExpected)
{
expectedHandler = handlerExpected;
threadStackTestFailed = outTestFailed;
*outTestFailed = handlerExpected;
+
+ return 0;
}
-void exhaust_thread_stack()
+int exhaust_thread_stack()
{
/* Move the compartment's stack near its end, in order to
* trigger stack exhaustion while the switcher handles
@@ -92,6 +94,8 @@
*threadStackTestFailed = true;
TEST(false, "Should be unreachable");
+
+ return 0;
}
/**
@@ -99,7 +103,7 @@
* callee-saved state. The result should simply be an error return, rather than
* a forced-unwind.
*/
-void exhaust_thread_stack_spill(__cheri_callback void (*fn)())
+int exhaust_thread_stack_spill(__cheri_callback int (*fn)())
{
register auto rfn asm("ct1") = fn;
register uintptr_t res asm("ca0") = 0;
@@ -126,52 +130,59 @@
*threadStackTestFailed = false;
TEST(res == -ENOTENOUGHSTACK, "Bad return {}", res);
+
+ return 0;
}
-void set_csp_permissions_on_fault(PermissionSet newPermissions)
+int set_csp_permissions_on_fault(PermissionSet newPermissions)
{
__asm__ volatile(
"candperm csp, csp, %0\n"
"csh zero, 0(cnull)\n" ::"r"(newPermissions.as_raw()));
TEST(false, "Should be unreachable");
+ return -EINVAL;
}
-void set_csp_permissions_on_call(PermissionSet newPermissions,
- __cheri_callback void (*fn)())
+int set_csp_permissions_on_call(PermissionSet newPermissions,
+ __cheri_callback int (*fn)())
{
CALL_CHERI_CALLBACK(fn, "candperm csp, csp, %1\n", newPermissions.as_raw());
TEST(false, "Should be unreachable");
+ return -EINVAL;
}
-void test_stack_invalid_on_fault()
+int test_stack_invalid_on_fault()
{
__asm__ volatile("ccleartag csp, csp\n"
"csh zero, 0(cnull)\n");
*threadStackTestFailed = true;
TEST(false, "Should be unreachable");
+ return -EINVAL;
}
-void test_stack_invalid_on_call(__cheri_callback void (*fn)())
+int test_stack_invalid_on_call(__cheri_callback int (*fn)())
{
// the `move zero, %1` is a no-op, just to have an operand
CALL_CHERI_CALLBACK(fn, "move zero, %1\nccleartag csp, csp\n", 0);
*threadStackTestFailed = true;
TEST(false, "Should be unreachable");
+ return -EINVAL;
}
-void self_recursion(__cheri_callback void (*fn)())
+int self_recursion(__cheri_callback int (*fn)())
{
(*fn)();
+ return 0;
}
-void exhaust_trusted_stack(__cheri_callback void (*fn)(),
- bool *outLeakedSwitcherCapability)
+int exhaust_trusted_stack(__cheri_callback int (*fn)(),
+ bool *outLeakedSwitcherCapability)
{
- self_recursion(fn);
+ return self_recursion(fn);
}
int test_stack_requirement()
diff --git a/tests/stack_tests.h b/tests/stack_tests.h
index 93ad94b..90b49df 100644
--- a/tests/stack_tests.h
+++ b/tests/stack_tests.h
@@ -4,28 +4,27 @@
using namespace CHERI;
-__cheri_compartment("stack_integrity_thread") void exhaust_trusted_stack(
- __cheri_callback void (*fn)(),
+__cheri_compartment("stack_integrity_thread") int exhaust_trusted_stack(
+ __cheri_callback int (*fn)(),
bool *outLeakedSwitcherCapability);
-__cheri_compartment("stack_integrity_thread") void exhaust_thread_stack();
-__cheri_compartment("stack_integrity_thread") void exhaust_thread_stack_spill(
- __cheri_callback void (*fn)());
-__cheri_compartment("stack_integrity_thread") void set_csp_permissions_on_fault(
+__cheri_compartment("stack_integrity_thread") int exhaust_thread_stack();
+__cheri_compartment("stack_integrity_thread") int exhaust_thread_stack_spill(
+ __cheri_callback int (*fn)());
+__cheri_compartment("stack_integrity_thread") int set_csp_permissions_on_fault(
PermissionSet newPermissions);
-__cheri_compartment("stack_integrity_thread") void set_csp_permissions_on_call(
+__cheri_compartment("stack_integrity_thread") int set_csp_permissions_on_call(
PermissionSet newPermissions,
- __cheri_callback void (*fn)());
-__cheri_compartment(
- "stack_integrity_thread") void test_stack_invalid_on_fault();
-__cheri_compartment("stack_integrity_thread") void test_stack_invalid_on_call(
- __cheri_callback void (*fn)());
+ __cheri_callback int (*fn)());
+__cheri_compartment("stack_integrity_thread") int test_stack_invalid_on_fault();
+__cheri_compartment("stack_integrity_thread") int test_stack_invalid_on_call(
+ __cheri_callback int (*fn)());
/**
* Sets what we expect to happen for this test. Is a fault expected to invoke
* the handler? The fault handler will set or clear `*outTestFailed` when a
* fault is received, depending on whether it was expected.
*/
-__cheri_compartment("stack_integrity_thread") void set_expected_behaviour(
+__cheri_compartment("stack_integrity_thread") int set_expected_behaviour(
bool *outTestFailed,
bool handlerExpected);
diff --git a/tests/test-runner.cc b/tests/test-runner.cc
index 37d2958..6d89591 100644
--- a/tests/test-runner.cc
+++ b/tests/test-runner.cc
@@ -79,7 +79,7 @@
/**
* Test suite entry point. Runs all of the tests that we have defined.
*/
-void __cheri_compartment("test_runner") run_tests()
+int __cheri_compartment("test_runner") run_tests()
{
// magic_enum is a pretty powerful stress-test of various bits of linkage.
// In generating `enum_values`, it generates constant strings and pointers
diff --git a/tests/tests.hh b/tests/tests.hh
index 14780ed..79779b7 100644
--- a/tests/tests.hh
+++ b/tests/tests.hh
@@ -56,6 +56,6 @@
inline Ticks sleep(Ticks ticks)
{
Timeout t{ticks};
- thread_sleep(&t);
+ TEST(thread_sleep(&t) >= 0, "Failed to sleep");
return t.elapsed;
};
diff --git a/tests/thread_pool-test.cc b/tests/thread_pool-test.cc
index 52a08e0..00377b3 100644
--- a/tests/thread_pool-test.cc
+++ b/tests/thread_pool-test.cc
@@ -75,8 +75,7 @@
int sleeps = 0;
while (counter < 2)
{
- Timeout t{1};
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to sleep");
TEST(sleeps < 100, "Gave up after too many sleeps");
}
debug_log("Yielded {} times for the thread pool to run our jobs", sleeps);
@@ -125,8 +124,7 @@
{
if (!asyncThread)
{
- Timeout t{1};
- thread_sleep(&t);
+ TEST(sleep(1) >= 0, "Failed to sleep");
}
}
TEST(asyncThread, "Worker thread did not provide thread pointer");
@@ -134,8 +132,7 @@
bool ret = switcher_interrupt_thread(asyncThread);
interruptStarted = true;
TEST(ret, "Interrupting worker thread failed: {}", ret);
- Timeout t{3};
- thread_sleep(&t);
+ TEST(sleep(3) >= 0, "Failed to sleep");
TEST(interrupted, "Worker thread was not interrupted");
return 0;
static cheriot::atomic<uint32_t> barrier{3};