| // 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; |
| } |