From 6643eaf1622c32c58db40d8b39225da20ffc3bc1 Mon Sep 17 00:00:00 2001 From: Domagoj Stolfa Date: Fri, 22 Mar 2024 15:10:42 +0000 Subject: [PATCH] [libunwind] Support rtld-c18n as the runtime linker. This commit adds support for backtrace and exception handling in libunwind when the process is running with the compartmentalization runtime linker. The unwinding process remains the same until a trampoline is encountered as the return address. This means that we are crossing compartment boundaries and we need to gather the unwind information from the runtime linker. We do this by reading information from the executive stack that the runtime linker populates for us in unw_getcontext. It also adds a new class, CompartmentInfo, which is responsible for abstracting away the details of c18n compartments. Currently, it is only used to define the constants relating to the trusted frame layout. The otype allocated to libunwind is given to libunwind by the runtime linker via the _rtld_unw_getsealer function, and as such this code is guarded by a LIBUNWIND_SANDBOX_OTYPES define. The sealer is used to internally access the executive stack pointer in order to unwind through compartment boundaries. This design may change in the future. This functionality only works on Morello right now. --- libunwind/CMakeLists.txt | 9 ++ libunwind/include/__libunwind_config.h | 11 +- libunwind/include/libunwind.h | 3 +- libunwind/src/AddressSpace.hpp | 27 ++++- libunwind/src/CompartmentInfo.hpp | 36 ++++++ libunwind/src/DwarfInstructions.hpp | 152 ++++++++++++++++++++++--- libunwind/src/Registers.hpp | 30 ++++- libunwind/src/UnwindCursor.hpp | 2 +- libunwind/src/UnwindRegistersRestore.S | 33 ++++-- libunwind/src/UnwindRegistersSave.S | 20 +++- libunwind/src/libunwind.cpp | 6 + 11 files changed, 291 insertions(+), 38 deletions(-) create mode 100644 libunwind/src/CompartmentInfo.hpp diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt index 1e29c8c0f354..ee666919a1cd 100644 --- a/libunwind/CMakeLists.txt +++ b/libunwind/CMakeLists.txt @@ -50,6 +50,7 @@ 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)") @@ -293,6 +294,14 @@ 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 "^aarch64c" 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 fd5fec8c0b27..25bd1333c548 100644 --- a/libunwind/include/__libunwind_config.h +++ b/libunwind/include/__libunwind_config.h @@ -20,7 +20,7 @@ #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 32 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC 112 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 116 -#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MORELLO 229 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MORELLO 230 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 95 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM 287 #define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32 @@ -76,11 +76,11 @@ # elif defined(__aarch64__) # define _LIBUNWIND_TARGET_AARCH64 1 # if defined(__CHERI_PURE_CAPABILITY__) -# define _LIBUNWIND_CONTEXT_SIZE 100 +# define _LIBUNWIND_CONTEXT_SIZE 102 # if defined(__SEH__) # error "Pure-capability aarch64 SEH not supported" # else -# define _LIBUNWIND_CURSOR_SIZE 124 +# define _LIBUNWIND_CURSOR_SIZE 126 # endif # define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MORELLO # else @@ -235,4 +235,9 @@ # define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 #endif // _LIBUNWIND_IS_NATIVE_ONLY +#if defined(_LIBUNWIND_CHERI_C18N_SUPPORT) && \ + !(defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_ARM)) +# error "LIBUNWIND_CHERI_C18N_SUPPORT is only supported on Morello architectures." +#endif + #endif // ____LIBUNWIND_CONFIG_H__ diff --git a/libunwind/include/libunwind.h b/libunwind/include/libunwind.h index fb38d3fe53f7..5dba4f7a214d 100644 --- a/libunwind/include/libunwind.h +++ b/libunwind/include/libunwind.h @@ -678,7 +678,8 @@ enum { UNW_ARM64_C30 = 228, UNW_ARM64_CLR = 228, UNW_ARM64_C31 = 229, - UNW_ARM64_CSP = 229 + UNW_ARM64_CSP = 229, + UNW_ARM64_ECSP = 240, }; // 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1. diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp index 56e5de51e334..c6fad856f3ad 100644 --- a/libunwind/src/AddressSpace.hpp +++ b/libunwind/src/AddressSpace.hpp @@ -320,6 +320,12 @@ 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); @@ -408,6 +414,24 @@ 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(void); +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; @@ -932,7 +956,8 @@ inline bool LocalAddressSpace::findUnwindSections(pc_t targetAddr, return true; #elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) dl_iterate_cb_data cb_data = {this, &info, targetAddr}; - CHERI_DBG("Calling dl_iterate_phdr()\n"); + CHERI_DBG("Calling dl_iterate_phdr(0x%jx)\n", + (uintmax_t)targetAddr.address()); int found = dl_iterate_phdr(findUnwindSectionsByPhdr, &cb_data); return static_cast(found); #endif diff --git a/libunwind/src/CompartmentInfo.hpp b/libunwind/src/CompartmentInfo.hpp new file mode 100644 index 000000000000..773ea28a9268 --- /dev/null +++ b/libunwind/src/CompartmentInfo.hpp @@ -0,0 +1,36 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Abstracts unwind information when used with a compartmentalizing runtime +// linker. +// +//===----------------------------------------------------------------------===// + +#ifndef __COMPARTMENT_INFO_HPP__ +#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. +#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 kFPOffset = 0; + static const uint32_t kCalleeSavedOffset = 2 * sizeof(void *); + static const uint32_t kCalleeSavedCount = 10; + static const uint32_t kCalleeSavedSize = sizeof(void *); + static const uint32_t kReturnAddressOffset = 15 * sizeof(void *) + 8; + static const uint32_t kPCOffset = sizeof(void *); + static const uint32_t kTrustedFrameSize = 16 * sizeof(void *); +#endif // _LIBUNWIND_TARGET_AARCH64 +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT +}; +} // namespace libunwind +#endif // __COMPARTMENT_INFO_HPP__ diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index e19383a4027e..fa0ce84e21af 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -20,6 +20,7 @@ #include "Registers.hpp" #include "DwarfParser.hpp" #include "config.h" +#include "CompartmentInfo.hpp" namespace libunwind { @@ -54,6 +55,17 @@ 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 size_t restoreCalleeSavedRegisters(pint_t csp, A &addressSpace, + R &newRegisters, + CompartmentInfo &CI); + 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); @@ -72,9 +84,8 @@ class DwarfInstructions { *success = true; pint_t result = (pint_t)-1; if (prolog.cfaRegister != 0) { - result = - (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + - prolog.cfaRegisterOffset); + result = registers.getRegister((int)prolog.cfaRegister); + result = (pint_t)((sint_t)result + prolog.cfaRegisterOffset); } else if (prolog.cfaExpression != 0) { result = evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, registers, 0); @@ -246,6 +257,93 @@ 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 +size_t DwarfInstructions::restoreCalleeSavedRegisters( + pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI) { + // Restore callee-saved registers + size_t i; + size_t offset; + // Restore: c19-c28 + for (i = 0, offset = CI.kCalleeSavedOffset; i < CI.kCalleeSavedCount; + ++i, offset += CI.kCalleeSavedSize) { + pint_t regValue = addressSpace.getCapability(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); + } + + return offset; +} + +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.getCapability(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.getCapability(csp + CI.kNewSPOffset); + newRegisters.setSP(nextRCSP); + CHERI_DBG("SANDBOX: SETTING RESTRICTED CSP: %#p\n", + (void *)newRegisters.getSP()); + size_t offset = + restoreCalleeSavedRegisters(csp, addressSpace, newRegisters, CI); + // Restore the frame pointer + pint_t newFP = addressSpace.getCapability(csp); + CHERI_DBG("SANDBOX: SETTING CFP %#p (offset=%zu)\n", (void *)newFP, offset); + newRegisters.setFP(newFP); + // Get the new return address. + return addressSpace.getCapability(csp + CI.kPCOffset); +} + +template +bool DwarfInstructions::isCompartmentTransitionTrampoline( + pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) { + // TODO(cheri): Use a cfp-based approach rather than the cookie. + ptraddr_t expectedReturnAddress = + addressSpace.get64(ecsp + CI.kReturnAddressOffset); + CHERI_DBG( + "isCompartmentTransitionTrampoline(): expectedReturnAddress: 0x%lx\n", + expectedReturnAddress); + return expectedReturnAddress == returnAddress; +} +#else // _LIBUNWIND_TARGET_AARCH64 +template +size_t DwarfInstructions::restoreCalleeSavedRegisters( + pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI) { + assert(0 && "not implemented on this architecture"); + return 0; +} +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, @@ -274,7 +372,9 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, // // We set the SP here to the CFA, allowing for it to be overridden // by a CFI directive later on. - newRegisters.setSP(cfa); + pint_t newSP = cfa; + CHERI_DBG("SETTING SP: %#p\n", (void *)newSP); + newRegisters.setSP(newSP); pint_t returnAddress = 0; constexpr int lastReg = R::lastDwarfRegNum(); @@ -297,15 +397,14 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, else if (i == (int)cieInfo.returnAddressRegister) { returnAddress = getSavedRegister(i, addressSpace, registers, cfa, prolog.savedRegisters[i]); - CHERI_DBG("SETTING RETURN REGISTER %d (%s): %#p \n", - i, newRegisters.getRegisterName(i), (void*)returnAddress); + CHERI_DBG("GETTING RETURN ADDRESS (saved) %d (%s): %#p \n", i, + newRegisters.getRegisterName(i), (void *)returnAddress); } else if (registers.validCapabilityRegister(i)) { - newRegisters.setCapabilityRegister( - i, getSavedCapabilityRegister(addressSpace, registers, cfa, - prolog.savedRegisters[i])); - CHERI_DBG("SETTING CAPABILITY REGISTER %d (%s): %#p \n", - i, newRegisters.getRegisterName(i), - (void*)A::to_pint_t(newRegisters.getCapabilityRegister(i))); + capability_t savedReg = getSavedCapabilityRegister( + addressSpace, registers, cfa, prolog.savedRegisters[i]); + newRegisters.setCapabilityRegister(i, savedReg); + CHERI_DBG("SETTING CAPABILITY REGISTER %d (%s): %#p \n", i, + newRegisters.getRegisterName(i), (void *)savedReg); } else if (registers.validRegister(i)) newRegisters.setRegister( i, getSavedRegister(i, addressSpace, registers, cfa, @@ -313,9 +412,11 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, else return UNW_EBADREG; } else if (i == (int)cieInfo.returnAddressRegister) { - // Leaf function keeps the return address in register and there is no - // explicit intructions how to restore it. - returnAddress = registers.getRegister(cieInfo.returnAddressRegister); + // Leaf function keeps the return address in register and there is no + // explicit intructions how to restore it. + returnAddress = registers.getRegister(cieInfo.returnAddressRegister); + CHERI_DBG("GETTING RETURN ADDRESS (leaf) %d (%s): %#p \n", i, + registers.getRegisterName(i), (void *)returnAddress); } } @@ -403,9 +504,30 @@ 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 with + // 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.getUnsealedTrustedStack(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); + CHERI_DBG("SETTING RETURN ADDRESS %#p\n", (void *)returnAddress); // Simulate the step by replacing the register set with the new ones. registers = newRegisters; diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp index 92b0b8b9bb9a..363d046b1e5a 100644 --- a/libunwind/src/Registers.hpp +++ b/libunwind/src/Registers.hpp @@ -1863,10 +1863,13 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { #ifdef __CHERI_PURE_CAPABILITY__ bool validCapabilityRegister(int num) const; uintcap_t getCapabilityRegister(int num) const; +#ifdef _LIBUNWIND_CHERI_C18N_SUPPORT + uintptr_t getUnsealedTrustedStack(uintptr_t sealer) const; +#endif void setCapabilityRegister(int num, uintcap_t value); #else CAPABILITIES_NOT_SUPPORTED -#endif +#endif // __CHERI_PURE_CAPABILITY__ uintptr_t getSP() const { return _registers.__sp; } void setSP(uintptr_t value) { _registers.__sp = value; } @@ -1874,6 +1877,9 @@ 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; } +#ifdef __CHERI_PURE_CAPABILITY__ + void setTrustedStack(uintptr_t value) { _registers.__ecsp = value; } +#endif private: struct GPRs { @@ -1882,6 +1888,9 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { uintptr_t __lr; // Link register r30 uintptr_t __sp; // Stack pointer r31 uintptr_t __pc; // Program counter +#ifdef __CHERI_PURE_CAPABILITY__ + uintptr_t __ecsp; // Executive stack pointer. +#endif uint64_t __ra_sign_state; // RA sign state register }; @@ -1898,8 +1907,8 @@ inline Registers_arm64::Registers_arm64(const void *registers) { "arm64 registers do not fit into unw_context_t"); memcpy(&_registers, registers, sizeof(_registers)); #ifdef __CHERI_PURE_CAPABILITY__ - static_assert(sizeof(GPRs) == 0x220, - "expected VFP registers to be at offset 544"); + static_assert(sizeof(GPRs) == 0x230, + "expected VFP registers to be at offset 560"); #else static_assert(sizeof(GPRs) == 0x110, "expected VFP registers to be at offset 272"); @@ -1986,6 +1995,16 @@ inline bool Registers_arm64::validCapabilityRegister(int regNum) const { return false; } +#ifdef _LIBUNWIND_CHERI_C18N_SUPPORT +inline uintptr_t +Registers_arm64::getUnsealedTrustedStack(uintptr_t sealer) const { + uintptr_t csp = _registers.__ecsp; + if (__builtin_cheri_sealed_get(csp)) + csp = __builtin_cheri_unseal(csp, sealer); + return csp; +} +#endif // _LIBUNWIND_CHERI_C18N_SUPPORT + inline uintcap_t Registers_arm64::getCapabilityRegister(int regNum) const { assert(validCapabilityRegister(regNum)); return getRegister(regNum); @@ -2198,6 +2217,8 @@ inline const char *Registers_arm64::getRegisterName(int regNum) { return "clr"; case UNW_ARM64_C31: return "csp"; + case UNW_ARM64_ECSP: + return "ecsp"; default: return "unknown register"; } @@ -4544,6 +4565,9 @@ class _LIBUNWIND_HIDDEN Registers_riscv { void setSP(reg_t value) { _registers[2] = value; } reg_t getIP() const { return _registers[0]; } void setIP(reg_t value) { _registers[0] = value; } +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT) + reg_t getUnsealedTrustedStack(uintptr_t sealer) { return getSP(); } +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT private: // _registers[0] holds the pc diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp index 2b4e51e295ab..6506d30f6355 100644 --- a/libunwind/src/UnwindCursor.hpp +++ b/libunwind/src/UnwindCursor.hpp @@ -1294,7 +1294,7 @@ class UnwindCursor : public AbstractUnwindCursor{ } #endif // defined(_LIBUNWIND_SUPPORT_TBTAB_UNWIND) - A &_addressSpace; + A &_addressSpace; R _registers; unw_proc_info_t _info; bool _unwindInfoMissing; diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S index dde5fd117307..cc6c19e10282 100644 --- a/libunwind/src/UnwindRegistersRestore.S +++ b/libunwind/src/UnwindRegistersRestore.S @@ -703,6 +703,24 @@ Lnovec: #elif defined(__aarch64__) +// +// extern "C" void __rtld_unw_setcontext(void *c0, void *c1, void *rcsp, void **buf); +// +#if defined(__CHERI_PURE_CAPABILITY__) +DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) + mov c16, c2 + ldp c2, c3, [c3, #(-0x210 + 0x20)] + mov csp, c16 +#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) +#endif + // // extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); // @@ -730,7 +748,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp c28,c29, [c0, #0x1c0] ldr c30, [c0, #0x200] // restore pcc into clr - add c16,c0, #0x220 + add c16,c0, #0x230 ldp d0, d1, [c16, #0x000] ldp d2, d3, [c16, #0x010] ldp d4, d5, [c16, #0x020] @@ -753,15 +771,10 @@ 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 c16, [c0, #0x1f0] - ldp c0, c1, [c0, #0x000] // restore c0,c1 - mov csp,c16 // restore csp -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - and x30, x30, #~1 - ret x30 // jump to pc -#else - ret // jump to pcc -#endif + ldr c2, [c0, #0x1f0] + add c3, c0, #0x210 + ldp c0, c1, [c0, #0x000] + b _rtld_unw_setcontext #else // skip restore of x0,x1 for now ldp x2, x3, [x0, #0x010] diff --git a/libunwind/src/UnwindRegistersSave.S b/libunwind/src/UnwindRegistersSave.S index 61c8c9d16b73..dd2d0330e78b 100644 --- a/libunwind/src/UnwindRegistersSave.S +++ b/libunwind/src/UnwindRegistersSave.S @@ -837,6 +837,15 @@ 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) +#endif + // // extern int __unw_getcontext(unw_context_t* thread_state) // @@ -861,12 +870,15 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) stp c24,c25, [c0, #0x180] stp c26,c27, [c0, #0x1a0] stp c28,c29, [c0, #0x1c0] - str c30, [c0, #0x1e0] mov c1,csp - str c1, [c0, #0x1f0] + stp c30, c1, [c0, #0x1e0] str c30, [c0, #0x200] // store return address as pcc + + // Prepare c1 to get our executive stack. + add c1, c0, #0x210 + // skip cpsr - add c0, c0, #0x220 + add c0, c0, #0x230 stp d0, d1, [c0, #0x000] stp d2, d3, [c0, #0x010] stp d4, d5, [c0, #0x020] @@ -885,7 +897,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) str d30, [c0, #0x0f0] str d31, [c0, #0x0f8] mov x0, #0 // return UNW_ESUCCESS - ret + b _rtld_unw_getcontext #else stp x0, x1, [x0, #0x000] stp x2, x3, [x0, #0x010] diff --git a/libunwind/src/libunwind.cpp b/libunwind/src/libunwind.cpp index 83648894a740..4b5748c3bab7 100644 --- a/libunwind/src/libunwind.cpp +++ b/libunwind/src/libunwind.cpp @@ -28,6 +28,7 @@ #if !defined(__USING_SJLJ_EXCEPTIONS__) #include "AddressSpace.hpp" +#include "CompartmentInfo.hpp" #include "UnwindCursor.hpp" @@ -42,6 +43,11 @@ 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;