diff --git a/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h b/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h index fd5fec8c0b27..5d0146ddc27f 100644 --- a/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h +++ b/contrib/subrepo-cheri-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 diff --git a/contrib/subrepo-cheri-libunwind/include/libunwind.h b/contrib/subrepo-cheri-libunwind/include/libunwind.h index fb38d3fe53f7..c1a1d432794e 100644 --- a/contrib/subrepo-cheri-libunwind/include/libunwind.h +++ b/contrib/subrepo-cheri-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 = 230, }; // 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1. diff --git a/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp b/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp index 6c5457c2c9ca..abc76bfa7b1f 100644 --- a/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp +++ b/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp @@ -22,6 +22,7 @@ #include "dwarf2.h" #include "EHHeaderParser.hpp" #include "Registers.hpp" +#include "unwind_cheri.h" // We can no longer include C++ headers so duplicate std::min() here template T uw_min(T a, T b) { return a < b ? a : b; } @@ -319,7 +320,15 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace { v128 getVector(pint_t addr) { return get(addr); } - capability_t getCapability(pint_t addr) { return get(addr); } + capability_t getCapability(pint_t addr) { return get(addr); } +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + static uintcap_t getUnwindSealer(); + capability_t getSealedCapability(pint_t addr) { + capability_t sealer = getUnwindSealer(); + assert(sealer != (capability_t)-1 && "Sealer not initialized"); + return __builtin_cheri_seal(get(addr), sealer); + } +#endif __attribute__((always_inline)) uintptr_t getP(pint_t addr); uint64_t getRegister(pint_t addr); @@ -408,6 +417,24 @@ inline uint64_t LocalAddressSpace::getRegister(pint_t addr) { #endif } +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) +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)-1; +} +_LIBUNWIND_WEAK_ALIAS(__rtld_unw_getsealer, _rtld_unw_getsealer) +} + +/// C++ wrapper for calling into RTLD. +inline uintcap_t LocalAddressSpace::getUnwindSealer() { + return _rtld_unw_getsealer(); +} +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES + /// 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; @@ -930,7 +957,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/contrib/subrepo-cheri-libunwind/src/CMakeLists.txt b/contrib/subrepo-cheri-libunwind/src/CMakeLists.txt index fcb3054e038a..e17d3bba5fd5 100644 --- a/contrib/subrepo-cheri-libunwind/src/CMakeLists.txt +++ b/contrib/subrepo-cheri-libunwind/src/CMakeLists.txt @@ -80,6 +80,7 @@ set(LIBUNWIND_HEADERS Registers.hpp RWMutex.hpp Unwind-EHABI.h + unwind_cheri.h UnwindCursor.hpp ../include/libunwind.h ../include/unwind.h diff --git a/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp b/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp index 18556c6f2d4b..dd0fa559423b 100644 --- a/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp +++ b/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp @@ -21,7 +21,6 @@ #include "DwarfParser.hpp" #include "config.h" - namespace libunwind { @@ -36,6 +35,10 @@ class DwarfInstructions { typedef typename A::pc_t pc_t; typedef typename A::capability_t capability_t; +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + static void restoreRegistersFromSandbox(A &addressSpace, R ®isters, + R &newRegisters, uintcap_t sealer); +#endif static int stepWithDwarf(A &addressSpace, pc_t pc, pint_t fdeStart, R ®isters, bool &isSignalFrame); @@ -72,9 +75,12 @@ 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); +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + if (__builtin_cheri_sealed_get(result)) + result = __builtin_cheri_unseal(result, addressSpace.getUnwindSealer()); +#endif + result = (pint_t)((sint_t)result + prolog.cfaRegisterOffset); } else if (prolog.cfaExpression != 0) { result = evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, registers, 0); @@ -84,6 +90,7 @@ class DwarfInstructions { *success = false; return (pint_t)-1; } + if (!is_pointer_in_bounds(result, true)) { _LIBUNWIND_LOG("evaluated out-of-bounds/invalid CFA " "expression for pc %#tx: " _LIBUNWIND_FMT_PTR "\n", @@ -245,6 +252,64 @@ bool DwarfInstructions::getRA_SIGN_STATE(A &addressSpace, R registers, } #endif +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) +template +void DwarfInstructions::restoreRegistersFromSandbox(A &addressSpace, + R ®isters, + R &newRegisters, + uintcap_t sealer) { + // Get the unsealed executive CSP + uintcap_t csp = registers.getUnsealedExecutiveStack(sealer); + assert(__builtin_cheri_tag_get((void *)csp) && + "Executive stack should be tagged!"); + // Derive the new executive CSP + ptraddr_t nextCSPAddr = addressSpace.get64(csp); + uintcap_t nextCSP = __builtin_cheri_address_set(csp, nextCSPAddr); + // Seal ECSP + nextCSP = __builtin_cheri_seal(nextCSP, sealer); + assert(__builtin_cheri_tag_get((void *)nextCSP) && + "Next executive stack should be tagged!"); + CHERI_DBG("SETTING EXECUTIVE CSP %#p\n", (void *)nextCSP); + newRegisters.setRegister(UNW_ARM64_ECSP, nextCSP, sealer); + // Restore the next RCSP from the executive stack + uintptr_t endOfOldRestrictedStack = addressSpace.getCapability(csp + 16); + uintptr_t newSP = addressSpace.getCapability(endOfOldRestrictedStack - 16); + // Seal RCSP + newSP = __builtin_cheri_seal(newSP, sealer); + newRegisters.setSP(newSP); + CHERI_DBG("SETTING SP %#p\n", (void *)newRegisters.getSP()); + // Get the new return address. We can't seal this because a return address + // will be a sentry. + uintptr_t newIP = addressSpace.getCapability(csp + 32); + newRegisters.setIP(newIP); + CHERI_DBG("SETTING RETURN ADDRESS %#p\n", (void *)newRegisters.getIP()); + + // Restore callee-saved registers. We seal these if they aren't sealed + // already. + // + // XXX: Sentries get handed out and we can't really prevent the untrusted + // context from using those right now. + int i; + size_t offset; + for (i = 0, offset = 48; i < 10; ++i, offset += 16) { + uintcap_t regValue = addressSpace.getCapability(csp + offset); + if (!__builtin_cheri_sealed_get(regValue)) + regValue = __builtin_cheri_seal(regValue, sealer); + newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, regValue); + CHERI_DBG("SETTING CALLEE SAVED CAPABILITY REGISTER:%d (%s): %#p\n", + UNW_ARM64_C19 + i, + newRegisters.getRegisterName(UNW_ARM64_C19 + i), + (void *)regValue); + } + + // Restore the frame pointer + uintcap_t newFP = addressSpace.getCapability(csp + offset); + newFP = __builtin_cheri_seal(newFP, sealer); + CHERI_DBG("SETTING CFP %#p\n", (void *)newFP); + newRegisters.setFP(newFP); +} +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES + template int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, pint_t fdeStart, R ®isters, @@ -273,7 +338,13 @@ 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); + uintptr_t newSP = cfa; +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + uintcap_t sealer = addressSpace.getUnwindSealer(); + if (sealer != (uintcap_t)-1) + newSP = __builtin_cheri_seal(newSP, sealer); +#endif + newRegisters.setSP(newSP); pint_t returnAddress = 0; constexpr int lastReg = R::lastDwarfRegNum(); @@ -296,15 +367,33 @@ 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("SETTING RETURN REGISTER %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))); + uintcap_t savedReg = getSavedCapabilityRegister( + addressSpace, registers, cfa, prolog.savedRegisters[i]); + if (i == UNW_ARM64_CFP) { + // When sandboxing, we want to seal the frame pointer so that we + // make sure we can unseal it during resume. This is simply a + // decision to structure the code this way, instead of adding a + // check in resume. We don't really care if callee saved registers + // are unsealed because if we are resuming before the current + // compartment boundary, these arguments are never being leaked to + // another compartment. +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + assert((savedReg == 0 || __builtin_cheri_tag_get(savedReg)) && + "Value should be tagged"); + if (sealer != (uintcap_t)-1) + savedReg = __builtin_cheri_seal(savedReg, sealer); +#endif + newRegisters.setFP(savedReg); + CHERI_DBG("SETTING FRAME POINTER %#p\n", + (void *)newRegisters.getFP()); + } else { + 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, @@ -312,9 +401,9 @@ 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); } } @@ -402,9 +491,25 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, } #endif +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + // If the sealer is not -1 (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. + if (sealer != (uintcap_t)-1 && (__builtin_cheri_perms_get(returnAddress) & + _LIBUNWIND_CHERI_PERM_EXECUTIVE) != 0) { + restoreRegistersFromSandbox(addressSpace, registers, newRegisters, + sealer); + registers = newRegisters; + return UNW_STEP_SUCCESS; + } +#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/contrib/subrepo-cheri-libunwind/src/Registers.hpp b/contrib/subrepo-cheri-libunwind/src/Registers.hpp index 92b0b8b9bb9a..8141356c9441 100644 --- a/contrib/subrepo-cheri-libunwind/src/Registers.hpp +++ b/contrib/subrepo-cheri-libunwind/src/Registers.hpp @@ -18,6 +18,7 @@ #include "cet_unwind.h" #include "config.h" #include "libunwind.h" +#include "unwind_cheri.h" namespace libunwind { @@ -1831,6 +1832,7 @@ inline const char *Registers_ppc64::getRegisterName(int regNum) { #if defined(_LIBUNWIND_TARGET_AARCH64) + /// Registers_arm64 holds the register state of a thread in a 64-bit arm /// process. class _LIBUNWIND_HIDDEN Registers_arm64; @@ -1842,7 +1844,11 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { bool validRegister(int num) const; uintptr_t getRegister(int num) const; +#ifdef _LIBUNWIND_SANDBOX_OTYPES + void setRegister(int num, uintptr_t value, uintcap_t sealer = -1); +#else void setRegister(int num, uintptr_t value); +#endif bool validFloatRegister(int num) const; double getFloatRegister(int num) const; void setFloatRegister(int num, double value); @@ -1851,6 +1857,31 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { void setVectorRegister(int num, v128 value); static const char *getRegisterName(int num); void jumpto() { __libunwind_Registers_arm64_jumpto(this); } +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + void unsealSP(uintcap_t sealer) { + assert(__builtin_cheri_sealed_get(_registers.__sp) && "Value must be sealed"); + _registers.__sp = __builtin_cheri_unseal(_registers.__sp, sealer); + } + void unsealFP(uintcap_t sealer) { + assert(__builtin_cheri_sealed_get(_registers.__fp) && "Value must be sealed"); + _registers.__fp = __builtin_cheri_unseal(_registers.__fp, sealer); + } + void unsealCalleeSavedRegisters(uintcap_t sealer) { + for (auto i = 0; i < 10; ++i) { + uintcap_t sealedValue = getRegister(UNW_ARM64_C19 + i); + assert(__builtin_cheri_sealed_get(sealedValue) && "Value must be sealed"); + uintcap_t unsealedValue = __builtin_cheri_unseal(sealedValue, sealer); + // If the tag gets cleared when we attempt to unseal our value, that means + // that we either have a capability that was sealed to begin with, and + // therefore we should just return it that way, or we have a sentry which + // we cannot unseal. + if (!__builtin_cheri_tag_get(unsealedValue) && + __builtin_cheri_tag_get(sealedValue)) + unsealedValue = sealedValue; + setCapabilityRegister(UNW_ARM64_C19 + i, unsealedValue); + } + } +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES static constexpr int lastDwarfRegNum() { #ifdef __CHERI_PURE_CAPABILITY__ return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MORELLO; @@ -1863,10 +1894,14 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { #ifdef __CHERI_PURE_CAPABILITY__ bool validCapabilityRegister(int num) const; uintcap_t getCapabilityRegister(int num) const; +#ifdef _LIBUNWIND_SANDBOX_OTYPES + uintcap_t getSealedExecutiveStack(uintcap_t sealer) const; + uintcap_t getUnsealedExecutiveStack(uintcap_t sealer) const; +#endif // _LIBUNWIND_SANDBOX_OTYPES 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; } @@ -1882,6 +1917,9 @@ class _LIBUNWIND_HIDDEN Registers_arm64 { uintptr_t __lr; // Link register r30 uintptr_t __sp; // Stack pointer r31 uintptr_t __pc; // Program counter +#if defined(__CHERI_PURE_CAPABILITY__) + uintptr_t __csp; // Executive stack pointer. +#endif uint64_t __ra_sign_state; // RA sign state register }; @@ -1898,8 +1936,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"); @@ -1924,6 +1962,8 @@ inline bool Registers_arm64::validRegister(int regNum) const { #ifdef __CHERI_PURE_CAPABILITY__ if ((regNum >= UNW_ARM64_C0) && (regNum <= UNW_ARM64_C31)) return true; + if (regNum == UNW_ARM64_ECSP) + return true; #endif if (regNum > 95) return false; @@ -1954,7 +1994,12 @@ inline uintptr_t Registers_arm64::getRegister(int regNum) const { _LIBUNWIND_ABORT("unsupported arm64 register"); } +#ifdef _LIBUNWIND_SANDBOX_OTYPES +inline void Registers_arm64::setRegister(int regNum, uintptr_t value, + uintcap_t sealer) { +#else inline void Registers_arm64::setRegister(int regNum, uintptr_t value) { +#endif if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) _registers.__pc = value; else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) @@ -1970,7 +2015,14 @@ inline void Registers_arm64::setRegister(int regNum, uintptr_t value) { #ifdef __CHERI_PURE_CAPABILITY__ else if ((regNum >= UNW_ARM64_C0) && (regNum <= UNW_ARM64_C31)) _registers.__x[regNum - UNW_ARM64_C0] = value; -#endif +#ifdef _LIBUNWIND_SANDBOX_OTYPES + else if (regNum == UNW_ARM64_ECSP) { + if (sealer != (uintptr_t)-1) { + _registers.__csp = value; + } + } +#endif // _LIBUNWIND_SANDBOX_OTYPES +#endif // __CHERI_PURE_CAPABILITY__ else _LIBUNWIND_ABORT("unsupported arm64 register"); } @@ -1983,9 +2035,34 @@ inline bool Registers_arm64::validCapabilityRegister(int regNum) const { return true; if ((regNum >= UNW_ARM64_C0) && (regNum <= UNW_ARM64_C31)) return true; + if (regNum == UNW_ARM64_ECSP) + return true; return false; } +#ifdef _LIBUNWIND_SANDBOX_OTYPES +inline uintcap_t +Registers_arm64::getSealedExecutiveStack(uintcap_t sealer) const { + assert(sealer != (uintcap_t)-1 && "Sealer not initialized"); + if (__builtin_cheri_sealed_get(_registers.__csp)) + return _registers.__csp; + else { + CHERI_DBG("Seal CSP=%#p with sealer=%#p\n", (void *)_registers.__csp, + (void *)sealer); + return __builtin_cheri_seal(_registers.__csp, sealer); + } +} + +inline uintcap_t +Registers_arm64::getUnsealedExecutiveStack(uintcap_t sealer) const { + assert(sealer != (uintcap_t)-1 && "Sealer not initialized"); + if (__builtin_cheri_sealed_get(_registers.__csp)) + return __builtin_cheri_unseal(_registers.__csp, sealer); + else + return _registers.__csp; +} +#endif + inline uintcap_t Registers_arm64::getCapabilityRegister(int regNum) const { assert(validCapabilityRegister(regNum)); return getRegister(regNum); @@ -2198,6 +2275,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"; } diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindCursor.hpp b/contrib/subrepo-cheri-libunwind/src/UnwindCursor.hpp index 2b4e51e295ab..4dc280c90dec 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindCursor.hpp +++ b/contrib/subrepo-cheri-libunwind/src/UnwindCursor.hpp @@ -474,6 +474,18 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor { } #endif +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + virtual void unsealSP(uintcap_t = -1) { + _LIBUNWIND_ABORT("unsealSP not implemented"); + } + virtual void unsealFP(uintcap_t = -1) { + _LIBUNWIND_ABORT("unsealFP not implemented"); + } + virtual void unsealCalleeSavedRegisters(uintcap_t = -1) { + _LIBUNWIND_ABORT("unsealCalleeSavedRegisters not implemented"); + } +#endif + #if defined(_LIBUNWIND_USE_CET) virtual void *get_registers() { _LIBUNWIND_ABORT("get_registers not implemented"); @@ -561,8 +573,7 @@ class UnwindCursor : public AbstractUnwindCursor { CONTEXT _msContext; UNWIND_HISTORY_TABLE _histTable; bool _unwindInfoMissing; -}; - + }; template UnwindCursor::UnwindCursor(unw_context_t *context, A &as) @@ -941,6 +952,11 @@ class UnwindCursor : public AbstractUnwindCursor{ virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); virtual const char *getRegisterName(int num); +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + virtual void unsealSP(uintcap_t sealer = -1); + virtual void unsealFP(uintcap_t sealer = -1); + virtual void unsealCalleeSavedRegisters(uintcap_t = -1); +#endif #ifdef __arm__ virtual void saveVFPAsX(); #endif @@ -1384,6 +1400,22 @@ const char *UnwindCursor::getRegisterName(int regNum) { return _registers.getRegisterName(regNum); } +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) +template +void UnwindCursor::unsealSP(uintcap_t sealer) { + return _registers.unsealSP(sealer); +} +template +void UnwindCursor::unsealFP(uintcap_t sealer) { + return _registers.unsealFP(sealer); +} +template +void UnwindCursor::unsealCalleeSavedRegisters(uintcap_t sealer) { + return _registers.unsealCalleeSavedRegisters(sealer); +} +#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES + + template bool UnwindCursor::isSignalFrame() { return _isSignalFrame; } diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S index dde5fd117307..faf8599987de 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S @@ -703,6 +703,13 @@ Lnovec: #elif defined(__aarch64__) +#if defined(__CHERI_PURE_CAPABILITY__) +DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) + ret +END_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) +WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) +#endif + // // extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); // @@ -728,9 +735,13 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) ldp c24,c25, [c0, #0x180] ldp c26,c27, [c0, #0x1a0] ldp c28,c29, [c0, #0x1c0] - ldr c30, [c0, #0x200] // restore pcc into clr + add c0, c0, #0x210 + // Restore Executive stack pointer here + bl _rtld_unw_setcontext + ldr c30, [c0, #-0x10] // restore pcc into clr + sub c0, c0, #0x210 + add c16,c0, #0x230 - add c16,c0, #0x220 ldp d0, d1, [c16, #0x000] ldp d2, d3, [c16, #0x010] ldp d4, d5, [c16, #0x020] diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S index 61c8c9d16b73..82645ad80300 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S +++ b/contrib/subrepo-cheri-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) + // c1 == csp + str c1, [c0] + ret +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 + add c0, c0, #0x210 + // Store Executive stack pointer here + bl _rtld_unw_getcontext + ldr c30, [c0, #-0x10] // skip cpsr - add c0, c0, #0x220 + add c0, c0, #0x20 stp d0, d1, [c0, #0x000] stp d2, d3, [c0, #0x010] stp d4, d5, [c0, #0x020] diff --git a/contrib/subrepo-cheri-libunwind/src/libunwind.cpp b/contrib/subrepo-cheri-libunwind/src/libunwind.cpp index 83648894a740..59425531b477 100644 --- a/contrib/subrepo-cheri-libunwind/src/libunwind.cpp +++ b/contrib/subrepo-cheri-libunwind/src/libunwind.cpp @@ -223,6 +223,14 @@ _LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) { __asan_handle_no_return(); #endif AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; +#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES) + uintcap_t sealer = LocalAddressSpace::sThisAddressSpace.getUnwindSealer(); + if (sealer != (uintcap_t)-1) { + co->unsealSP(sealer); + co->unsealFP(sealer); + co->unsealCalleeSavedRegisters(sealer); + } +#endif co->jumpto(); return UNW_EUNSPEC; } diff --git a/contrib/subrepo-cheri-libunwind/src/unwind_cheri.h b/contrib/subrepo-cheri-libunwind/src/unwind_cheri.h new file mode 100644 index 000000000000..3228027f11e4 --- /dev/null +++ b/contrib/subrepo-cheri-libunwind/src/unwind_cheri.h @@ -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 +// +// Provides the constants and helpers for CHERI. +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_CHERI_H__ +#define __UNWIND_CHERI_H__ + +#ifdef __CHERI_PURE_CAPABILITY__ +#define _LIBUNWIND_CHERI_PERM_GLOBAL (1 << 0) /* 0x00000001 */ +#define _LIBUNWIND_CHERI_PERM_EXECUTIVE (1 << 1) /* 0x00000002 */ +#define _LIBUNWIND_CHERI_PERM_SW0 (1 << 2) /* 0x00000004 */ +#define _LIBUNWIND_CHERI_PERM_SW1 (1 << 3) /* 0x00000008 */ +#define _LIBUNWIND_CHERI_PERM_SW2 (1 << 4) /* 0x00000010 */ +#define _LIBUNWIND_CHERI_PERM_SW3 (1 << 5) /* 0x00000020 */ +#define _LIBUNWIND_CHERI_PERM_MUTABLE_LOAD (1 << 6) /* 0x00000040 */ +#define _LIBUNWIND_CHERI_PERM_COMPARTMENT_ID (1 << 7) /* 0x00000080 */ +#define _LIBUNWIND_CHERI_PERM_BRANCH_SEALED_PAIR (1 << 8) /* 0x00000100 */ +#define _LIBUNWIND_CHERI_PERM_INVOKE CHERI_PERM_BRANCH_SEALED_PAIR +#define _LIBUNWIND_CHERI_PERM_SYSTEM (1 << 9) /* 0x00000200 */ +#define _LIBUNWIND_CHERI_PERM_SYSTEM_REGS CHERI_PERM_SYSTEM +#define _LIBUNWIND_CHERI_PERM_UNSEAL (1 << 10) /* 0x00000400 */ +#define _LIBUNWIND_CHERI_PERM_SEAL (1 << 11) /* 0x00000800 */ +#define _LIBUNWIND_CHERI_PERM_STORE_LOCAL_CAP (1 << 12) /* 0x00001000 */ +#define _LIBUNWIND_CHERI_PERM_STORE_CAP (1 << 13) /* 0x00002000 */ +#define _LIBUNWIND_CHERI_PERM_LOAD_CAP (1 << 14) /* 0x00004000 */ +#define _LIBUNWIND_CHERI_PERM_EXECUTE (1 << 15) /* 0x00008000 */ +#define _LIBUNWIND_CHERI_PERM_STORE (1 << 16) /* 0x00010000 */ +#define _LIBUNWIND_CHERI_PERM_LOAD (1 << 17) /* 0x00020000 */ +#endif // __CHERI_PURE_CAPABILITY__ + +#endif // __UNWIND_CHERI_H__ diff --git a/lib/Makefile b/lib/Makefile index 5cefb593a177..3d6e139fa8e9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -115,7 +115,7 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \ .if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" .if !defined(COMPAT_LIBCOMPAT) || ${COMPAT_LIBCOMPAT} == "64CB" -SUBDIR+= libc_c18n libthr_c18n +SUBDIR+= libc_c18n libthr_c18n# libgcc_s_c18n .endif .endif diff --git a/lib/libgcc_s/Makefile b/lib/libgcc_s/Makefile index fdf5a67d8045..f0412af29204 100644 --- a/lib/libgcc_s/Makefile +++ b/lib/libgcc_s/Makefile @@ -11,16 +11,20 @@ MK_UBSAN:= no MK_SSP= no WARNS?= 2 +.if !defined(LIBGCC_S_SRCTOP) +LIBGCC_S_SRCTOP:= ${.PARSEDIR} +.endif + LDFLAGS+= -nodefaultlibs LIBADD+= c -VERSION_DEF= ${.CURDIR}/Versions.def -SYMBOL_MAPS= ${.CURDIR}/Symbol.map +VERSION_DEF= ${LIBGCC_S_SRCTOP}/Versions.def +SYMBOL_MAPS= ${LIBGCC_S_SRCTOP}/Symbol.map # Export ARM AEABI unwind routines needed by libc and libthr. -.if exists(${.CURDIR}/${MACHINE_CPUARCH}/Symbol.map) -SYMBOL_MAPS+= ${.CURDIR}/${MACHINE_CPUARCH}/Symbol.map +.if exists(${LIBGCC_S_SRCTOP}/${MACHINE_CPUARCH}/Symbol.map) +SYMBOL_MAPS+= ${LIBGCC_S_SRCTOP}/${MACHINE_CPUARCH}/Symbol.map .else -SYMBOL_MAPS+= ${.CURDIR}/SymbolDefault.map +SYMBOL_MAPS+= ${LIBGCC_S_SRCTOP}/SymbolDefault.map .endif .include "../libcompiler_rt/Makefile.inc" @@ -53,4 +57,9 @@ SRCS+= s_logbl.c SRCS+= s_scalbnl.c .endif +RTLD_SANDBOX= +.if defined(RTLD_SANDBOX) +CFLAGS+= -D_LIBUNWIND_SANDBOX_OTYPES +SYMBOL_MAPS+= ${LIBGCC_S_SRCTOP}/Symbol-c18n.map +.endif .include diff --git a/lib/libgcc_s/Symbol-c18n.map b/lib/libgcc_s/Symbol-c18n.map new file mode 100644 index 000000000000..6cddda834561 --- /dev/null +++ b/lib/libgcc_s/Symbol-c18n.map @@ -0,0 +1,5 @@ +FBSDprivate_1.0 { + _rtld_unw_getcontext; + _rtld_unw_setcontext; + _rtld_unw_getsealer; +}; diff --git a/lib/libgcc_s/Versions.def b/lib/libgcc_s/Versions.def index d28e9042f744..706fc514451c 100644 --- a/lib/libgcc_s/Versions.def +++ b/lib/libgcc_s/Versions.def @@ -31,3 +31,6 @@ GCC_4.3.0 { GCC_4.6.0 { } GCC_4.3.0; + +FBSDprivate_1.0 { +} GCC_4.6.0; diff --git a/lib/libgcc_s_c18n/Makefile b/lib/libgcc_s_c18n/Makefile new file mode 100644 index 000000000000..fb53396612f7 --- /dev/null +++ b/lib/libgcc_s_c18n/Makefile @@ -0,0 +1,5 @@ +RTLD_SANDBOX=yes +SHLIBDIR= /usr/lib${COMPAT_libcompat:U}/c18n +LIBDIR= /usr/lib${COMPAT_libcompat:U}/c18n + +.include "${SRCTOP}/lib/libgcc_s/Makefile" diff --git a/libexec/rtld-elf-c18n/Makefile b/libexec/rtld-elf-c18n/Makefile index beb937da75b8..5af52751323d 100644 --- a/libexec/rtld-elf-c18n/Makefile +++ b/libexec/rtld-elf-c18n/Makefile @@ -3,6 +3,7 @@ MAN= MLINKS= rtld.1 ld-elf-c18n.so.1.1 RTLD_SANDBOX= +DEBUG= .PATH: ${SRCTOP}/libexec/rtld-elf .include "${SRCTOP}/libexec/rtld-elf/Makefile" diff --git a/libexec/rtld-elf/Symbol-c18n.map b/libexec/rtld-elf/Symbol-c18n.map index fe3122bc8caa..6a8b0634bae5 100644 --- a/libexec/rtld-elf/Symbol-c18n.map +++ b/libexec/rtld-elf/Symbol-c18n.map @@ -8,4 +8,7 @@ FBSDprivate_1.0 { _rtld_longjmp; _rtld_safebox_code; _rtld_sandbox_code; + _rtld_unw_getcontext; + _rtld_unw_setcontext; + _rtld_unw_getsealer; }; diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index 4161057845cb..02e2b9a65473 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -220,6 +220,30 @@ ENTRY(_rtld_tramp_hook) #endif END(_rtld_tramp_hook) +ENTRY(_rtld_unw_getcontext) + /* + * Get the address of the executive stack frame from the trusted frame + * and derive an executive stack capability that the unwinder gets. + */ + ldr c2, sealer_unwbuf + ldr x10, [csp] + scvalue c1, csp, x10 + cseal c1, c1, c2 + mov x2, xzr + str c1, [c0] + retr c30 +END(_rtld_unw_getcontext) + +ENTRY(_rtld_unw_setcontext) + /* + * Store the executive stack frame address to be entered on the trusted frame. + */ + ldr c1, [c0] + gcvalue x1, c1 + str x1, [csp] /* Store the next CSP */ + retr c30 +END(_rtld_unw_setcontext) + /* * Trampoline templates are code but reside in rodata. Hence a new macro is * defined to describe them. diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index cbe2dd0fe820..45453aad1089 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -43,6 +43,8 @@ * Sealers for RTLD privileged information */ static uintptr_t sealer_jmpbuf; +extern uintptr_t sealer_unwbuf; +uintptr_t sealer_unwbuf; uintptr_t sealer_pltgot, sealer_tramp; @@ -448,6 +450,15 @@ _rtld_setjmp_impl(void **buf, void *val, struct trusted_frame *csp) return ((struct jmp_args) { .buf = buf, .val = val }); } +uintptr_t _rtld_unw_getsealer(void); + +uintptr_t +_rtld_unw_getsealer(void) +{ + + return (sealer_unwbuf); +} + struct jmp_args _rtld_longjmp_impl(void **, void *, struct trusted_frame *); struct jmp_args @@ -964,6 +975,9 @@ tramp_init(void) sealer_jmpbuf = cheri_setboundsexact(sealer, 1); sealer += 1; + sealer_unwbuf = cheri_setboundsexact(sealer, 1); + sealer += 1; + sealer_tramp = cheri_setboundsexact(sealer, C18N_FUNC_SIG_COUNT); sealer += C18N_FUNC_SIG_COUNT;