|  | // Copyright Microsoft and CHERIoT Contributors. | 
|  | // SPDX-License-Identifier: MIT | 
|  |  | 
|  | #define TEST_NAME "Crash recovery (inner compartment)" | 
|  | #include "crash_recovery.h" | 
|  | #include <cheri.hh> | 
|  | #include <errno.h> | 
|  | #include <new> | 
|  |  | 
|  | using namespace CHERI; | 
|  |  | 
|  | volatile bool                   shouldDoubleFault             = false; | 
|  | volatile bool                   shouldSkipFaultingInstruction = false; | 
|  | volatile bool                   shouldCorruptCSP              = false; | 
|  | volatile ErrorRecoveryBehaviour recoveryBehaviour; | 
|  |  | 
|  | extern "C" ErrorRecoveryBehaviour | 
|  | compartment_error_handler(ErrorState *frame, size_t mcause, size_t mtval) | 
|  | { | 
|  | debug_log("Detected error in instruction {}", frame->pcc); | 
|  | debug_log("Error cause: {}", mcause); | 
|  | if (shouldSkipFaultingInstruction) | 
|  | { | 
|  | Capability pcc{__builtin_cheri_program_counter_get()}; | 
|  | pcc.address() = Capability{frame->pcc}.address(); | 
|  | uint32_t faultingInstruction; | 
|  | // pcc may be unaligned, so we need a memcpy to load from it. | 
|  | memcpy(&faultingInstruction, pcc, 4); | 
|  | debug_log("Faulting instruction: {}", faultingInstruction); | 
|  | // If the low bits are 11 then this is a 32-bit instruction, otherwise | 
|  | // it's a 16-bit one. | 
|  | ptrdiff_t skipSize = ((faultingInstruction & 3) == 3) ? 4 : 2; | 
|  | frame->pcc         = static_cast<char *>(frame->pcc) + skipSize; | 
|  | } | 
|  | if (shouldDoubleFault) | 
|  | { | 
|  | debug_log("Triggering double fault"); | 
|  | // Should trigger a permit-store violation | 
|  | auto readOnlyPointer = | 
|  | static_cast<char *>(__builtin_cheri_program_counter_get()); | 
|  | readOnlyPointer[0] = 1; | 
|  | } | 
|  | if (shouldCorruptCSP) | 
|  | { | 
|  | asm volatile("cmove csp, cnull\n" | 
|  | "cjr %0\n" | 
|  | : | 
|  | : "C"(__builtin_return_address(0))); | 
|  | } | 
|  |  | 
|  | return recoveryBehaviour; | 
|  | } | 
|  |  | 
|  | void *test_crash_recovery_inner(int option) | 
|  | { | 
|  | check_stack(); | 
|  | int  x[16]; | 
|  | int *ptr      = std::launder(x); | 
|  | auto capFault = [=]() { | 
|  | __c11_atomic_signal_fence(__ATOMIC_SEQ_CST); | 
|  | ptr[16] = 0; | 
|  | __c11_atomic_signal_fence(__ATOMIC_SEQ_CST); | 
|  | }; | 
|  | switch (option) | 
|  | { | 
|  | case 0: | 
|  | // simple crash | 
|  | shouldDoubleFault             = false; | 
|  | shouldSkipFaultingInstruction = false; | 
|  | shouldCorruptCSP              = false; | 
|  | debug_log("Trying to store out of bounds in {}, simple unwind", | 
|  | ptr); | 
|  | recoveryBehaviour = ErrorRecoveryBehaviour::ForceUnwind; | 
|  | capFault(); | 
|  | TEST(false, "Should be unreachable"); | 
|  | case 1: | 
|  | // Skip, return normally | 
|  | shouldDoubleFault             = false; | 
|  | shouldSkipFaultingInstruction = true; | 
|  | shouldCorruptCSP              = false; | 
|  | recoveryBehaviour = ErrorRecoveryBehaviour::InstallContext; | 
|  | capFault(); | 
|  | debug_log("Store silently ignored"); | 
|  | return nullptr; | 
|  | case 2: | 
|  | // Double fault | 
|  | shouldDoubleFault             = true; | 
|  | shouldSkipFaultingInstruction = true; | 
|  | shouldCorruptCSP              = false; | 
|  | recoveryBehaviour = ErrorRecoveryBehaviour::InstallContext; | 
|  | debug_log("Trying to fault and double fault in the error handler"); | 
|  | capFault(); | 
|  | TEST(false, "Double fault resumed"); | 
|  | case 3: | 
|  | // Corrupt CSP in the error handler | 
|  | shouldDoubleFault             = false; | 
|  | shouldSkipFaultingInstruction = true; | 
|  | shouldCorruptCSP              = true; | 
|  | recoveryBehaviour = ErrorRecoveryBehaviour::InstallContext; | 
|  | debug_log("Trying to fault and corrupt CSP in the error handler"); | 
|  | capFault(); | 
|  | TEST(false, "Resumed with exploded CSP"); | 
|  | } | 
|  | return nullptr; | 
|  | } |