blob: e368562d9aba6241112ba415dd332609004d9765 [file] [log] [blame]
// Copyright CHERIoT Contributors.
// SPDX-License-Identifier: MIT
#define TEST_NAME "Test unwind cleanup"
#include "locks.hh"
#include "tests.hh"
#include "unwind.h"
extern "C" ErrorRecoveryBehaviour
compartment_error_handler(ErrorState *, size_t, size_t)
{
cleanup_unwind();
return ErrorRecoveryBehaviour::ForceUnwind;
}
namespace
{
void test_setjmp()
{
jmp_buf env;
volatile int x = 0;
if (int r = setjmp(env); r == 0)
{
TEST_EQUAL(x, 0, "setjmp should return 0 the first time");
x = 42;
longjmp(env, 1);
}
else
{
TEST_EQUAL(r, 1, "setjmp should return 1 the second time");
TEST_EQUAL(
x, 42, "On the second return, x should have been modified");
x = 53;
}
TEST_EQUAL(x, 53, "After longjmp, x should have been modified");
}
FlagLock flagLock;
void test_on_error()
{
LockGuard g(flagLock);
on_error([&]() { cleanup_unwind(); }, [&]() { g.unlock(); });
TEST(!g, "on_error should lock the lock");
}
void test_on_error_raii_inner()
{
LockGuard g(flagLock);
// No handler. g's destructor runs after on_error returns.
on_error([&]() { cleanup_unwind(); });
}
void test_on_error_raii()
{
test_on_error_raii_inner();
TEST(flagLock.try_lock(), "raii should have been dropped the lock");
flagLock.unlock();
}
void test_c_macros()
{
volatile int x = 0;
CHERIOT_DURING
{
x = 42;
cleanup_unwind();
}
CHERIOT_HANDLER
{
TEST_EQUAL(x, 42, "In the handler, x should have been modified");
x = 53;
}
CHERIOT_END_HANDLER
TEST_EQUAL(x, 53, "After longjmp, object should have been modified");
}
__noinline void overflow_stack(volatile int *x)
{
int hugeBuffer[4096];
debug_log("Overflowing stack: {}", hugeBuffer);
}
/**
* Make sure that we can unwind out of a trap. This will invoke the normal
* error handler.
*/
void test_from_trap()
{
volatile int x = 0;
CHERIOT_DURING
{
x = 42;
__builtin_trap();
}
CHERIOT_HANDLER
{
TEST_EQUAL(x, 42, "In the handler, x should have been modified");
x = 53;
}
CHERIOT_END_HANDLER
TEST_EQUAL(
x, 53, "After error handler, object should have been modified");
}
/**
* Make sure that we can unwind out of a stack overflow. This will invoke
* the stackless error handler.
*/
void test_from_stack_overflow()
{
volatile int x = 0;
CHERIOT_DURING
{
x = 42;
overflow_stack(&x);
}
CHERIOT_HANDLER
{
debug_log("Error handler");
TEST_EQUAL(x, 42, "In the handler, x should have been modified");
x = 53;
}
CHERIOT_END_HANDLER
TEST_EQUAL(x, 53, "After handler, object should have been modified");
}
} // namespace
int test_unwind_cleanup()
{
test_setjmp();
test_on_error();
test_c_macros();
// Try these in both orders to make sure that both error handlers correctly
// clean up.
test_from_trap();
test_from_stack_overflow();
test_from_stack_overflow();
test_from_trap();
debug_log("Test unwind_cleanup passed");
return 0;
}