From faf5ea2f0240738fd0c5970dec35897f20f5b575 Mon Sep 17 00:00:00 2001 From: Dapeng Gao Date: Fri, 21 Jun 2024 16:21:55 +0100 Subject: [PATCH] libunwind: Use APIs exposed by RTLD to unwind the trusted stack --- libunwind/include/__libunwind_config.h | 5 -- libunwind/src/AddressSpace.hpp | 24 ------ libunwind/src/CompartmentInfo.hpp | 79 +++++++++++++++---- libunwind/src/DwarfInstructions.hpp | 101 +++---------------------- libunwind/src/UnwindRegistersRestore.S | 2 +- libunwind/src/UnwindRegistersSave.S | 12 +-- libunwind/src/libunwind.cpp | 6 -- 7 files changed, 84 insertions(+), 145 deletions(-) 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..d7421acc3ce5 100644 --- a/libunwind/src/CompartmentInfo.hpp +++ b/libunwind/src/CompartmentInfo.hpp @@ -14,20 +14,73 @@ #define __COMPARTMENT_INFO_HPP__ 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. + +extern "C" { + +struct trusted_frame; + +// Must mirror the layout in rtld_c18n_machdep.h #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 +struct compart_state { + void *fp; + void *pc; + void *regs[10]; // c19 to c28 + void *sp; +}; +#else +# error "LIBUNWIND_CHERI_C18N_SUPPORT is not supported on this target" +#endif + +#pragma weak c18n_is_enabled +bool c18n_is_enabled(void) { + return false; +}; + +#pragma weak c18n_is_tramp +bool c18n_is_tramp(ptraddr_t, struct trusted_frame *); + +#pragma weak c18n_pop_trusted_stk +struct trusted_frame * +c18n_pop_trusted_stk(struct compart_state *, struct trusted_frame *); +} + +// A wrapper for RTLD APIs related to library-based compartmentalisation (c18n). +template +struct CompartmentInfo { + typedef typename A::pint_t pint_t; + + static bool isC18NEnabled() { + return c18n_is_enabled(); + } + + static bool isC18NTramp(pint_t pc, pint_t tf) { + return c18n_is_tramp(pc, (struct trusted_frame *)tf); + } + + static pint_t fillC18NState(R &newRegisters, pint_t tf) { + struct compart_state state; + tf = (pint_t)c18n_pop_trusted_stk(&state, (struct trusted_frame *)tf); + + newRegisters.setTrustedStack(tf); + CHERI_DBG("C18N: SET TRUSTED STACK %#p\n", (void *)tf); + + newRegisters.setFP((pint_t)state.fp); + CHERI_DBG("C18N: SET FP %#p\n", state.fp); + + newRegisters.setSP((pint_t)state.sp); + CHERI_DBG("C18N: SET SP: %#p\n", state.sp); + + for (size_t i = 0; i < sizeof(state.regs) / sizeof(*state.regs); ++i) { + newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, + (pint_t)state.regs[i]); + CHERI_DBG("C18N: SET REGISTER: %lu (%s): %#p\n", + UNW_ARM64_C19 + i, + newRegisters.getRegisterName(UNW_ARM64_C19 + i), + state.regs[i]); + } + + return (pint_t)state.pc; + } }; } // namespace libunwind #endif // __COMPARTMENT_INFO_HPP__ diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index dc55191e8c93..32a0dcc32aa1 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -20,7 +20,9 @@ #include "Registers.hpp" #include "DwarfParser.hpp" #include "config.h" +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) #include "CompartmentInfo.hpp" +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT namespace libunwind { @@ -55,14 +57,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); @@ -105,6 +99,9 @@ class DwarfInstructions { static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa, PrologInfo &prolog); #endif +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) + static constexpr CompartmentInfo CompartInfo{}; +#endif }; template @@ -255,75 +252,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, @@ -484,23 +412,14 @@ 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)) { + // If c18n is enabled, check if we are at a compartment boundary. If so, + // restore registers from the trusted stack by asking rtld for them. + if (CompartInfo.isC18NEnabled()) { 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)) { + if (CompartInfo.isC18NTramp(returnAddress, csp)) { CHERI_DBG("%#p: detected a trampoline, unwinding from sandbox\n", (void *)returnAddress); - returnAddress = restoreRegistersFromSandbox( - csp, addressSpace, newRegisters, CI, sealer); + returnAddress = CompartInfo.fillC18NState(newRegisters, csp); } } #endif diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index 351ef4e3bdbb..b93e7c7b44e1 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -714,7 +714,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) #ifdef __CHERI_PURE_CAPABILITY__ ldr c1, [c0, #0x1f0] // Pass the target untrusted stack pointer ldr c2, [c0, #0x210] // Pass the target trusted stack pointer - bl _rtld_unw_setcontext + bl c18n_unwind_trusted_stk // skip restore of c0,c1 for now ldp c2, c3, [c0, #0x020] diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index bb2dd7cb8bf3..74f07812d834 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -838,16 +838,18 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) #elif defined(__aarch64__) #ifdef __CHERI_PURE_CAPABILITY__ -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_noop) +DEFINE_LIBUNWIND_FUNCTION(_c18n_noop) #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI and x30, x30, #~1 ret x30 #else ret #endif -END_LIBUNWIND_FUNCTION(__rtld_unw_noop) -WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_getcontext) -WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_setcontext) +END_LIBUNWIND_FUNCTION(_c18n_noop) +// uintptr_t c18n_get_trusted_stk(uintptr_t, struct trusted_frame **); +WEAK_ALIAS(_c18n_noop, c18n_get_trusted_stk) +// uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *); +WEAK_ALIAS(_c18n_noop, c18n_unwind_trusted_stk) #endif // @@ -902,7 +904,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str d30, [c0, #0x0f0] str d31, [c0, #0x0f8] mov x0, #0 // return UNW_ESUCCESS - b _rtld_unw_getcontext + b c18n_get_trusted_stk #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;