diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt index 83d8db1c6166..1e29c8c0f354 100644 --- a/libunwind/CMakeLists.txt +++ b/libunwind/CMakeLists.txt @@ -50,7 +50,6 @@ option(LIBUNWIND_IS_BAREMETAL "Build libunwind for baremetal targets." OFF) option(LIBUNWIND_USE_FRAME_HEADER_CACHE "Cache frame headers for unwinding. Requires locking dl_iterate_phdr." OFF) option(LIBUNWIND_REMEMBER_HEAP_ALLOC "Use heap instead of the stack for .cfi_remember_state." OFF) option(LIBUNWIND_INSTALL_HEADERS "Install the libunwind headers." OFF) -option(LIBUNWIND_CHERI_C18N_SUPPORT "Use a libunwind implementation that supports a CHERI c18n RTLD." OFF) set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING "Define suffix of library directory name (32/64)") @@ -294,14 +293,6 @@ if (NOT LIBUNWIND_ENABLE_THREADS) add_compile_flags(-D_LIBUNWIND_HAS_NO_THREADS) endif() -# Sandboxing and c18n support -if (LIBUNWIND_CHERI_C18N_SUPPORT) - if (NOT CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64" OR NOT CMAKE_SYSTEM_NAME MATCHES "FreeBSD") - message(FATAL_ERROR "LIBUNWIND_CHERI_C18N_SUPPORT is not supported for ${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") - endif() - add_compile_flags(-D_LIBUNWIND_CHERI_C18N_SUPPORT) -endif() - # ARM WMMX register support if (LIBUNWIND_ENABLE_ARM_WMMX) # __ARM_WMMX is a compiler pre-define (as per the ACLE 2.0). Clang does not diff --git a/libunwind/include/__libunwind_config.h b/libunwind/include/__libunwind_config.h index 5b3f0bff628c..cc14c084072c 100644 --- a/libunwind/include/__libunwind_config.h +++ b/libunwind/include/__libunwind_config.h @@ -235,9 +235,4 @@ # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 #endif // _LIBUNWIND_IS_NATIVE_ONLY -#if defined(_LIBUNWIND_CHERI_C18N_SUPPORT) && \ - !defined(_LIBUNWIND_TARGET_AARCH64) -# error "LIBUNWIND_CHERI_C18N_SUPPORT is only supported on Morello" -#endif - #endif // ____LIBUNWIND_CONFIG_H__ diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 17b05a224ebf..892c97f71873 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -321,12 +321,6 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace { return get(addr); } capability_t getCapability(pint_t addr) { return get(addr); } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - static pint_t getUnwindSealer(); - static bool isValidSealer(pint_t sealer) { - return __builtin_cheri_tag_get(sealer); - } -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT __attribute__((always_inline)) uintptr_t getP(pint_t addr); uint64_t getRegister(pint_t addr); @@ -415,24 +409,6 @@ inline uint64_t LocalAddressSpace::getRegister(pint_t addr) { #endif } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) -extern "C" { -/// Call into the RTLD to get a sealer capability. This sealer will be used to -/// seal information in the unwinding context. -uintptr_t _rtld_unw_getsealer(); -uintptr_t __rtld_unw_getsealer(); -_LIBUNWIND_HIDDEN uintptr_t __rtld_unw_getsealer() { - return (uintptr_t)0; -} -_LIBUNWIND_WEAK_ALIAS(__rtld_unw_getsealer, _rtld_unw_getsealer) -} - -/// C++ wrapper for calling into RTLD. -inline LocalAddressSpace::pint_t LocalAddressSpace::getUnwindSealer() { - return _rtld_unw_getsealer(); -} -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT - /// Read a ULEB128 into a 64-bit word. inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) { const uint8_t *p = (uint8_t *)addr; diff --git a/libunwind/src/CompartmentInfo.hpp b/libunwind/src/CompartmentInfo.hpp index 08ff723c7220..a33f2f15dcb3 100644 --- a/libunwind/src/CompartmentInfo.hpp +++ b/libunwind/src/CompartmentInfo.hpp @@ -13,21 +13,75 @@ #ifndef __COMPARTMENT_INFO_HPP__ #define __COMPARTMENT_INFO_HPP__ +#ifdef __CHERI_PURE_CAPABILITY__ + +extern "C" { + +// Must mirror the layout in rtld_c18n_machdep.h +#ifdef _LIBUNWIND_TARGET_AARCH64 +struct dl_c18n_compart_state { + void *fp; + void *pc; + void *regs[10]; // c19 to c28 + void *sp; +}; +#endif + +#pragma weak dl_c18n_is_tramp +int dl_c18n_is_tramp(ptraddr_t pc, void *tf) { + return 0; +}; + +#pragma weak dl_c18n_pop_trusted_stk +void *dl_c18n_pop_trusted_stk(struct dl_c18n_compart_state *, void *); +} + +#endif // __CHERI_PURE_CAPABILITY__ + namespace libunwind { -class _LIBUNWIND_HIDDEN CompartmentInfo { -public: -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - static CompartmentInfo sThisCompartmentInfo; - // Per-architecture trusted stack frame layout. -#if defined(_LIBUNWIND_TARGET_AARCH64) - static const uint32_t kNewSPOffset = 12 * sizeof(void *); - static const uint32_t kNextOffset = 14 * sizeof(void *); - static const uint32_t kCalleeSavedOffset = 2 * sizeof(void *); - static const uint32_t kCalleeSavedCount = 10; - static const uint32_t kReturnAddressOffset = 15 * sizeof(void *) + 8; - static const uint32_t kPCOffset = sizeof(void *); -#endif // _LIBUNWIND_TARGET_AARCH64 -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT + +// A wrapper for RTLD APIs related to library-based compartmentalisation (c18n). +template +struct CompartmentInfo { + typedef typename A::pint_t pint_t; + + static int unwindIfAtBoundary(R ®isters) { +#ifdef __CHERI_PURE_CAPABILITY__ +#ifdef _LIBUNWIND_TARGET_AARCH64 + struct dl_c18n_compart_state state; + pint_t pc = registers.getIP(); + pint_t tf = registers.getTrustedStack(); + + if (!dl_c18n_is_tramp(pc, (void *)tf)) + return UNW_STEP_SUCCESS; + + CHERI_DBG("COMPARTMENT BOUNDARY %#p\n", (void *)pc); + + tf = (pint_t)dl_c18n_pop_trusted_stk(&state, (void *)tf); + + registers.setTrustedStack(tf); + CHERI_DBG("C18N: SET TRUSTED STACK %#p\n", (void *)tf); + + registers.setFP((pint_t)state.fp); + CHERI_DBG("C18N: SET FP %#p\n", state.fp); + + registers.setSP((pint_t)state.sp); + CHERI_DBG("C18N: SET SP: %#p\n", state.sp); + + registers.setIP((pint_t)state.pc); + CHERI_DBG("C18N: SET IP: %#p\n", state.pc); + + for (size_t i = 0; i < sizeof(state.regs) / sizeof(*state.regs); ++i) { + registers.setCapabilityRegister(UNW_ARM64_C19 + i, (pint_t)state.regs[i]); + CHERI_DBG("C18N: SET REGISTER: %lu (%s): %#p\n", + UNW_ARM64_C19 + i, + registers.getRegisterName(UNW_ARM64_C19 + i), + state.regs[i]); + } +#endif +#endif + return UNW_STEP_SUCCESS; + } }; } // namespace libunwind #endif // __COMPARTMENT_INFO_HPP__ diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index dc55191e8c93..f47e3665a6f9 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -20,7 +20,6 @@ #include "Registers.hpp" #include "DwarfParser.hpp" #include "config.h" -#include "CompartmentInfo.hpp" namespace libunwind { @@ -55,14 +54,6 @@ class DwarfInstructions { typedef typename CFI_Parser::FDE_Info FDE_Info; typedef typename CFI_Parser::CIE_Info CIE_Info; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - static pint_t restoreRegistersFromSandbox(pint_t csp, A &addressSpace, - R &newRegisters, - CompartmentInfo &CI, pint_t sealer); - static bool isCompartmentTransitionTrampoline(pint_t ecsp, A &addressSpace, - CompartmentInfo &CI, - pint_t returnAddress); -#endif static pint_t evaluateExpression(pint_t expression, A &addressSpace, const R ®isters, pint_t initialStackValue); @@ -255,75 +246,6 @@ bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, } #endif -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) -#if defined(_LIBUNWIND_TARGET_AARCH64) -template -typename A::pint_t DwarfInstructions::restoreRegistersFromSandbox( - pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI, - pint_t sealer) { - // Get the unsealed executive CSP - assert(__builtin_cheri_tag_get((void *)csp) && - "Executive stack should be tagged!"); - // Derive the new executive CSP - pint_t nextCSP = addressSpace.getP(csp + CI.kNextOffset); - // Seal ECSP - nextCSP = __builtin_cheri_seal(nextCSP, sealer); - assert(__builtin_cheri_tag_get((void *)nextCSP) && - "Next executive stack should be tagged!"); - CHERI_DBG("SANDBOX: SETTING EXECUTIVE CSP %#p\n", (void *)nextCSP); - newRegisters.setTrustedStack(nextCSP); - // Restore the next RCSP - pint_t nextRCSP = addressSpace.getP(csp + CI.kNewSPOffset); - newRegisters.setSP(nextRCSP); - CHERI_DBG("SANDBOX: SETTING RESTRICTED CSP: %#p\n", - (void *)newRegisters.getSP()); - // Restore callee-saved registers - // Restore: c19-c28 - for (size_t i = 0, offset = CI.kCalleeSavedOffset; i < CI.kCalleeSavedCount; - ++i, offset += sizeof(void *)) { - pint_t regValue = addressSpace.getP(csp + offset); - newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, regValue); - CHERI_DBG("SETTING CALLEE SAVED CAPABILITY REGISTER: %lu (%s): %#p " - "(offset=%zu)\n", - UNW_ARM64_C19 + i, - newRegisters.getRegisterName(UNW_ARM64_C19 + i), (void *)regValue, - offset); - } - // Restore the frame pointer - pint_t newFP = addressSpace.getP(csp); - CHERI_DBG("SANDBOX: SETTING CFP %#p\n", (void *)newFP); - newRegisters.setFP(newFP); - // Get the new return address. - return addressSpace.getP(csp + CI.kPCOffset); -} - -template -bool DwarfInstructions::isCompartmentTransitionTrampoline( - pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) { - ptraddr_t expectedReturnAddress = - addressSpace.template get(ecsp + CI.kReturnAddressOffset); - CHERI_DBG( - "isCompartmentTransitionTrampoline(): expectedReturnAddress: 0x%lx\n", - expectedReturnAddress); - return expectedReturnAddress == returnAddress; -} -#else // _LIBUNWIND_TARGET_AARCH64 -template -typename A::pint_t DwarfInstructions::restoreRegistersFromSandbox( - pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI, - pint_t sealer) { - assert(0 && "not implemented on this architecture"); - return (pint_t)0; -} -template -bool DwarfInstructions::isCompartmentTransitionTrampoline( - pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) { - assert(0 && "not implemented on this architecture"); - return false; -} -#endif // _LIBUNWIND_TARGET_AARCH64 -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT - template int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, pint_t fdeStart, R ®isters, @@ -483,28 +405,6 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, } #endif -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) - // If the sealer is not valid (only the case when we're running without - // c18n), check if the return address has the executive mode bit set. - // If so, we should be calling into the c18n RTLD as this is a - // compartment boundary. We need to restore registers from the executive - // stack and ask rtld for it. - uintptr_t sealer = addressSpace.getUnwindSealer(); - if (addressSpace.isValidSealer(sealer)) { - pint_t csp = registers.getTrustedStack(); - if (__builtin_cheri_sealed_get(csp)) - csp = __builtin_cheri_unseal(csp, sealer); - CompartmentInfo &CI = CompartmentInfo::sThisCompartmentInfo; - if (csp != 0 && isCompartmentTransitionTrampoline(csp, addressSpace, CI, - returnAddress)) { - CHERI_DBG("%#p: detected a trampoline, unwinding from sandbox\n", - (void *)returnAddress); - returnAddress = restoreRegistersFromSandbox( - csp, addressSpace, newRegisters, CI, sealer); - } - } -#endif - // Return address is address after call site instruction, so setting IP to // that does simualates a return. newRegisters.setIP(returnAddress); diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index 7dda2e106fdc..2b054f518bf8 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -1874,10 +1874,10 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { void setIP(uintptr_t value) { _registers.__pc = value; } uintptr_t getFP() const { return _registers.__fp; } void setFP(uintptr_t value) { _registers.__fp = value; } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) +#ifdef __CHERI_PURE_CAPABILITY__ uintptr_t getTrustedStack() const { return _registers.__ecsp; } void setTrustedStack(uintptr_t value) { _registers.__ecsp = value; } -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT +#endif private: struct GPRs { diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 2b4e51e295ab..fb6e09ae3d11 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -86,6 +86,7 @@ extern "C" _Unwind_Reason_Code __libunwind_seh_personality( #include "Registers.hpp" #include "RWMutex.hpp" #include "Unwind-EHABI.h" +#include "CompartmentInfo.hpp" namespace libunwind { @@ -1294,6 +1295,10 @@ class UnwindCursor : public AbstractUnwindCursor{ } #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) + int stepThroughIfAtC18NBoundary(R ®isters) { + return CompartmentInfo::unwindIfAtBoundary(registers); + } + A &_addressSpace; R _registers; unw_proc_info_t _info; @@ -2867,6 +2872,11 @@ int UnwindCursor::step() { #endif } + // If we are at a compartment boundary, step through it by asking RTLD to + // restore registers from the trusted stack. + if (result == UNW_STEP_SUCCESS) + result = stepThroughIfAtC18NBoundary(_registers); + // update info based on new PC if (result == UNW_STEP_SUCCESS) { this->setInfoBasedOnIPRegister(true); diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index e20d2b125c95..47605157e144 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -703,23 +703,16 @@ Lnovec: #elif defined(__aarch64__) -// -// extern "C" void __rtld_unw_setcontext(void *c0, void *c1, -// void *rcsp, void **sealed_ecsp); -// -#if defined(__CHERI_PURE_CAPABILITY__) -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) - mov c16, c2 - ldp c2, c3, [c3, #(-0x210 + 0x20)] - mov csp, c16 +#ifdef __CHERI_PURE_CAPABILITY__ +DEFINE_LIBUNWIND_FUNCTION(_dl_c18n_unwind_trusted_stk) #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI and x30, x30, #~1 ret x30 #else ret #endif -END_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) -WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) +END_LIBUNWIND_FUNCTION(_dl_c18n_unwind_trusted_stk) +WEAK_ALIAS(_dl_c18n_unwind_trusted_stk, dl_c18n_unwind_trusted_stk) #endif // @@ -731,8 +724,16 @@ WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) .p2align 2 DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) #ifdef __CHERI_PURE_CAPABILITY__ + // Preserve the argument in a callee-saved register instead of pushing it onto + // the stack because stack unwinding will switch the stack. + mov c19, c0 + ldr c0, [c19, #0x1f0] // Pass the target untrusted stack pointer + ldr c1, [c19, #0x210] // Pass the target trusted stack pointer + bl dl_c18n_unwind_trusted_stk + mov c0, c19 + // skip restore of c0,c1 for now - // also skip restoring c2 and c3 because they will get clobbered later on + ldp c2, c3, [c0, #0x020] ldp c4, c5, [c0, #0x040] ldp c6, c7, [c0, #0x060] ldp c8, c9, [c0, #0x080] @@ -772,17 +773,14 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) // context struct, because it is allocated on the stack, and an exception // could clobber the de-allocated portion of the stack after csp has been // restored. - ldr c2, [c0, #0x1f0] - add c3, c0, #0x210 - ldp c0, c1, [c0, #0x000] - // XXX: variant PCS is not yet supported by rtld, work around it - // using a function pointer. - adrp c16, :got:_rtld_unw_setcontext - ldr c16, [c16, :got_lo12:_rtld_unw_setcontext] + ldr c16, [c0, #0x1f0] + ldp c0, c1, [c0, #0x000] // restore c0,c1 + mov csp,c16 // restore csp #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x16 + and x30, x30, #~1 + ret x30 // jump to pc #else - br c16 + ret // jump to pcc #endif #else // skip restore of x0,x1 for now diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index 8edcd4cb51d0..7e3afaafd113 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -837,13 +837,16 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) #elif defined(__aarch64__) -#if defined(__CHERI_PURE_CAPABILITY__) -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_getcontext) - mov c2, csp - str c2, [c1] - ret c30 -END_LIBUNWIND_FUNCTION(__rtld_unw_getcontext) -WEAK_ALIAS(__rtld_unw_getcontext, _rtld_unw_getcontext) +#ifdef __CHERI_PURE_CAPABILITY__ +DEFINE_LIBUNWIND_FUNCTION(_dl_c18n_get_trusted_stk) +#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI + and x30, x30, #~1 + ret x30 +#else + ret +#endif +END_LIBUNWIND_FUNCTION(_dl_c18n_get_trusted_stk) +WEAK_ALIAS(_dl_c18n_get_trusted_stk, dl_c18n_get_trusted_stk) #endif // @@ -875,8 +878,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str c1, [c0, #0x1f0] str c30, [c0, #0x200] // store return address as pcc - // Prepare c1 to get our executive stack. - add c1, c0, #0x210 + // Store the trusted stack pointer + stp c0, c30, [csp, #-0x20]! + add c0, c0, #0x210 + bl dl_c18n_get_trusted_stk + ldp c0, c30, [csp], #0x20 // skip cpsr add c0, c0, #0x230 @@ -898,7 +904,12 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str d30, [c0, #0x0f0] str d31, [c0, #0x0f8] mov x0, #0 // return UNW_ESUCCESS - b _rtld_unw_getcontext +#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI + and x30, x30, #~1 + ret x30 // jump to pc +#else + ret // jump to pcc +#endif #else stp x0, x1, [x0, #0x000] stp x2, x3, [x0, #0x010] diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index 4b5748c3bab7..83648894a740 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -28,7 +28,6 @@ #if !defined(__USING_SJLJ_EXCEPTIONS__) #include "AddressSpace.hpp" -#include "CompartmentInfo.hpp" #include "UnwindCursor.hpp" @@ -43,11 +42,6 @@ using namespace libunwind; /// internal object to represent this processes address space LocalAddressSpace LocalAddressSpace::sThisAddressSpace; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) -/// internal object to represent this processes compartment information -CompartmentInfo CompartmentInfo::sThisCompartmentInfo; -#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT - _LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space = (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace;