diff --git a/libunwind/CMakeLists.txt b/libunwind/CMakeLists.txt
index 08ebd632ade9..379b813c0f71 100644
--- a/libunwind/CMakeLists.txt
+++ b/libunwind/CMakeLists.txt
@@ -51,6 +51,7 @@ option(LIBUNWIND_USE_FRAME_HEADER_CACHE "Cache frame headers for unwinding. Requ
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_SANDBOX_OTYPES "Use a libunwind implementation that assumes a c18n RTLD using otypes." OFF)
+option(LIBUNWIND_SANDBOX_HARDENED "Harden the current sandbox implementation." OFF)
set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING
"Define suffix of library directory name (32/64)")
@@ -302,6 +303,11 @@ if (LIBUNWIND_SANDBOX_OTYPES)
add_compile_flags(-D_LIBUNWIND_SANDBOX_OTYPES)
endif()
+# Hardening
+if (LIBUNWIND_SANDBOX_HARDENED)
+ add_compile_flags(-D_LIBUNWIND_SANDBOX_HARDENED)
+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 7507b58a6619..7fc88e009b8a 100644
--- a/libunwind/include/__libunwind_config.h
+++ b/libunwind/include/__libunwind_config.h
@@ -11,6 +11,14 @@
#define _LIBUNWIND_VERSION 15000
+#if defined(_LIBUNWIND_SANDBOX_HARDENED) && !defined(_LIBUNWIND_SANDBOX_OTYPES)
+#error "_LIBUNWIND_SANDBOX_HARDENED is invalid without a sandboxing mechanism"
+#endif
+
+#if defined(_LIBUNWIND_SANDBOX_OTYPES) && defined(_LIBUNWIND_NO_HEAP)
+#error "_LIBUNWIND_NO_HEAP cannot be used with _LIBUNWIND_SANDBOX_OTYPES"
+#endif
+
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \
!defined(__ARM_DWARF_EH__) && !defined(__SEH__)
#define _LIBUNWIND_ARM_EHABI
diff --git a/libunwind/src/AddressSpace.hpp b/libunwind/src/AddressSpace.hpp
index c97ccb9deeaf..e7c040f4cb4e 100644
--- a/libunwind/src/AddressSpace.hpp
+++ b/libunwind/src/AddressSpace.hpp
@@ -417,7 +417,9 @@ inline uint64_t LocalAddressSpace::getRegister(pint_t addr) {
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES)
extern "C" {
-/// Call into the RTLD to get a sealer capability.
+/// Call into the RTLD to get a sealer capability. This sealer will be used to
+/// seal information in the unwinding context if _LIBUNWIND_SANDBOX_HARDENED is
+/// specified.
LocalAddressSpace::pint_t _rtld_unw_getsealer(void);
LocalAddressSpace::pint_t __rtld_unw_getsealer();
_LIBUNWIND_HIDDEN LocalAddressSpace::pint_t __rtld_unw_getsealer() {
diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp
index baf9fccfd3c6..652e301bd21a 100644
--- a/libunwind/src/DwarfInstructions.hpp
+++ b/libunwind/src/DwarfInstructions.hpp
@@ -87,6 +87,10 @@ class DwarfInstructions {
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES)
CHERI_DBG("getRegister(%d) = %#p\n", (int)prolog.cfaRegister,
(void *)result);
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ if (__builtin_cheri_sealed_get(result))
+ result = __builtin_cheri_unseal(result, addressSpace.getUnwindSealer());
+#endif // _LIBUNWIND_SANDBOX_HARDENED
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES
result = (pint_t)((sint_t)result + prolog.cfaRegisterOffset);
} else if (prolog.cfaExpression != 0) {
@@ -270,12 +274,21 @@ size_t DwarfInstructions::restoreCalleeSavedRegisters(pint_t csp,
pint_t sealer) {
// Restore callee-saved registers. We seal these if they aren't sealed
// already.
+ //
+ // XXX: When _LIBUNWIND_SANDBOX_HARDENED is specified, sentries get handed out
+ // and we can't really prevent the untrusted context from using those right
+ // now.
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);
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ if (addressSpace.isValidSealer(sealer) &&
+ !__builtin_cheri_sealed_get(regValue))
+ regValue = __builtin_cheri_seal(regValue, sealer);
+#endif
newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, regValue);
CHERI_DBG("SETTING CALLEE SAVED CAPABILITY REGISTER: %lu (%s): %#p "
"(offset=%zu)\n",
@@ -296,12 +309,20 @@ typename A::pint_t DwarfInstructions::restoreRegistersFromSandbox(
"Executive stack should be tagged!");
// Derive the new executive CSP
pint_t nextCSP = addressSpace.getCapability(csp + CI.kNextOffset);
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ // Seal ECSP
+ nextCSP = __builtin_cheri_seal(nextCSP, sealer);
+#endif
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);
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ // Seal RCSP
+ nextRCSP = __builtin_cheri_seal(nextRCSP, sealer);
+#endif
newRegisters.setSP(nextRCSP);
CHERI_DBG("SANDBOX: SETTING RESTRICTED CSP: %#p\n",
(void *)newRegisters.getSP());
@@ -309,6 +330,9 @@ typename A::pint_t DwarfInstructions::restoreRegistersFromSandbox(
restoreCalleeSavedRegisters(csp, addressSpace, newRegisters, CI, sealer);
// Restore the frame pointer
pint_t newFP = addressSpace.getCapability(csp);
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ newFP = __builtin_cheri_seal(newFP, sealer);
+#endif
CHERI_DBG("SANDBOX: SETTING CFP %#p (offset=%zu)\n", (void *)newFP, offset);
newRegisters.setFP(newFP);
// Get the new return address. We can't seal this because a return address
@@ -385,6 +409,10 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc,
pint_t newSP = cfa;
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_SANDBOX_OTYPES)
pint_t sealer = addressSpace.getUnwindSealer();
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ if (addressSpace.isValidSealer(sealer))
+ newSP = __builtin_cheri_seal(newSP, sealer);
+#endif // _LIBUNWIND_SANDBOX_HARDENED
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES
CHERI_DBG("SETTING SP: %#p\n", (void *)newSP);
newRegisters.setSP(newSP);
@@ -415,6 +443,16 @@ int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc,
} else if (registers.validCapabilityRegister(i)) {
capability_t savedReg = getSavedCapabilityRegister(
addressSpace, registers, cfa, prolog.savedRegisters[i]);
+#if defined(__CHERI_PURE_CAPABILITY__) && \
+ defined(_LIBUNWIND_SANDBOX_OTYPES) && defined(_LIBUNWIND_SANDBOX_HARDENED)
+ // Seal all the capability registers. This enforces the invariant
+ // that unsealed capabilities are never stored in the context that
+ // aren't explicitly set through unw_set_reg() by a consumer.
+ if (addressSpace.isValidSealer(sealer) &&
+ !__builtin_cheri_sealed_get(savedReg))
+ savedReg = __builtin_cheri_seal(savedReg, sealer);
+#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES &&
+ // _LIBUNWIND_SANDBOX_HARDENED
newRegisters.setCapabilityRegister(i, savedReg);
CHERI_DBG("SETTING CAPABILITY REGISTER %d (%s): %#p \n", i,
newRegisters.getRegisterName(i), (void *)savedReg);
diff --git a/libunwind/src/Registers.hpp b/libunwind/src/Registers.hpp
index 80874ea69d9a..d6b0b01db40f 100644
--- a/libunwind/src/Registers.hpp
+++ b/libunwind/src/Registers.hpp
@@ -1852,6 +1852,45 @@ 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) && defined(_LIBUNWIND_SANDBOX_HARDENED)
+ void unsealSP(uintptr_t sealer) {
+ assert(__builtin_cheri_sealed_get(_registers.__sp) && "Value must be sealed");
+ _registers.__sp = __builtin_cheri_unseal(_registers.__sp, sealer);
+ }
+ void unsealFP(uintptr_t sealer) {
+ assert(__builtin_cheri_sealed_get(_registers.__fp) && "Value must be sealed");
+ _registers.__fp = __builtin_cheri_unseal(_registers.__fp, sealer);
+ }
+ void unsealCalleeSavedRegisters(uintptr_t sealer) {
+ for (auto i = 0; i < 10; ++i) {
+ uintptr_t sealedValue = getRegister(UNW_ARM64_C19 + i);
+ // FIXME: Would be nice to enforce this invariant, but right now
+ // unw_set_reg() gets called to set what ends up in private_1, and it
+ // would require breaking libunwind's public API to seal registers through
+ // that particular path, and therefore we can't assert this.
+#if 0
+ assert((!__builtin_cheri_tag_get(sealedValue) ||
+ __builtin_cheri_sealed_get(sealedValue)) &&
+ "Value must be sealed");
+#endif
+ uintptr_t unsealedValue = sealedValue;
+ // 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(sealedValue) &&
+ __builtin_cheri_sealed_get(sealedValue)) {
+ unsealedValue = __builtin_cheri_unseal(sealedValue, sealer);
+ if (!__builtin_cheri_tag_get(unsealedValue)) {
+ unsealedValue = sealedValue;
+ }
+ }
+ setCapabilityRegister(UNW_ARM64_C19 + i, unsealedValue);
+ }
+ }
+#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES &&
+ // _LIBUNWIND_SANDBOX_HARDENED
static constexpr int lastDwarfRegNum() {
#ifdef __CHERI_PURE_CAPABILITY__
return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MORELLO;
@@ -2000,6 +2039,10 @@ inline bool Registers_arm64::validCapabilityRegister(int regNum) const {
inline uintptr_t
Registers_arm64::getUnsealedTrustedStack(uintptr_t sealer) const {
uintptr_t csp = _registers.__ecsp;
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ if (__builtin_cheri_sealed_get(csp))
+ csp = __builtin_cheri_unseal(csp, sealer);
+#endif // _LIBUNWIND_SANDBOX_HARDENED
return csp;
}
#endif // _LIBUNWIND_SANDBOX_OTYPES
diff --git a/libunwind/src/UnwindCursor.hpp b/libunwind/src/UnwindCursor.hpp
index 6506d30f6355..c2a47b8be60e 100644
--- a/libunwind/src/UnwindCursor.hpp
+++ b/libunwind/src/UnwindCursor.hpp
@@ -474,6 +474,19 @@ class _LIBUNWIND_HIDDEN AbstractUnwindCursor {
}
#endif
+#if defined(__CHERI_PURE_CAPABILITY__) && \
+ defined(_LIBUNWIND_SANDBOX_OTYPES) && defined(_LIBUNWIND_SANDBOX_HARDENED)
+ virtual void unsealSP(uintptr_t) {
+ _LIBUNWIND_ABORT("unsealSP not implemented");
+ }
+ virtual void unsealFP(uintptr_t) {
+ _LIBUNWIND_ABORT("unsealFP not implemented");
+ }
+ virtual void unsealCalleeSavedRegisters(uintptr_t) {
+ _LIBUNWIND_ABORT("unsealCalleeSavedRegisters not implemented");
+ }
+#endif
+
#if defined(_LIBUNWIND_USE_CET)
virtual void *get_registers() {
_LIBUNWIND_ABORT("get_registers not implemented");
@@ -941,6 +954,12 @@ 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) && defined(_LIBUNWIND_SANDBOX_HARDENED)
+ virtual void unsealSP(pint_t sealer);
+ virtual void unsealFP(pint_t sealer);
+ virtual void unsealCalleeSavedRegisters(pint_t sealer);
+#endif
#ifdef __arm__
virtual void saveVFPAsX();
#endif
@@ -1384,6 +1403,23 @@ const char *UnwindCursor::getRegisterName(int regNum) {
return _registers.getRegisterName(regNum);
}
+#if defined(__CHERI_PURE_CAPABILITY__) && \
+ defined(_LIBUNWIND_SANDBOX_OTYPES) && defined(_LIBUNWIND_SANDBOX_HARDENED)
+template
+void UnwindCursor::unsealSP(pint_t sealer) {
+ return _registers.unsealSP(sealer);
+}
+template
+void UnwindCursor::unsealFP(pint_t sealer) {
+ return _registers.unsealFP(sealer);
+}
+template
+void UnwindCursor::unsealCalleeSavedRegisters(pint_t sealer) {
+ return _registers.unsealCalleeSavedRegisters(sealer);
+}
+#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES &&
+ // _LIBUNWIND_SANDBOX_HARDENED
+
template bool UnwindCursor::isSignalFrame() {
return _isSignalFrame;
}
diff --git a/libunwind/src/UnwindRegistersRestore.S b/libunwind/src/UnwindRegistersRestore.S
index 68509c19c500..6d23fb7164d0 100644
--- a/libunwind/src/UnwindRegistersRestore.S
+++ b/libunwind/src/UnwindRegistersRestore.S
@@ -718,8 +718,12 @@ DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_setcontext)
ret
#endif
END_LIBUNWIND_FUNCTION(__rtld_unw_setcontext)
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext)
+#else
WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext_unsealed)
#endif
+#endif
//
// extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *);
@@ -774,7 +778,11 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
ldr c2, [c0, #0x1f0]
add c3, c0, #0x210
ldp c0, c1, [c0, #0x000]
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ b _rtld_unw_setcontext
+#else
b _rtld_unw_setcontext_unsealed
+#endif
#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 d2ef1034bf11..56d6fa9a3b72 100644
--- a/libunwind/src/UnwindRegistersSave.S
+++ b/libunwind/src/UnwindRegistersSave.S
@@ -843,8 +843,12 @@ DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_getcontext)
str c2, [c1]
ret c30
END_LIBUNWIND_FUNCTION(__rtld_unw_getcontext)
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+WEAK_ALIAS(__rtld_unw_getcontext, _rtld_unw_getcontext)
+#else
WEAK_ALIAS(__rtld_unw_getcontext, _rtld_unw_getcontext_unsealed)
#endif
+#endif
//
// extern int __unw_getcontext(unw_context_t* thread_state)
@@ -897,7 +901,11 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
str d30, [c0, #0x0f0]
str d31, [c0, #0x0f8]
mov x0, #0 // return UNW_ESUCCESS
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ b _rtld_unw_getcontext
+#else
b _rtld_unw_getcontext_unsealed
+#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 49ae34b488e9..ca067a4ad1be 100644
--- a/libunwind/src/libunwind.cpp
+++ b/libunwind/src/libunwind.cpp
@@ -234,6 +234,11 @@ _LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) {
LocalAddressSpace &addressSpace = LocalAddressSpace::sThisAddressSpace;
pint_t sealer = addressSpace.getUnwindSealer();
if (addressSpace.isValidSealer(sealer)) {
+#ifdef _LIBUNWIND_SANDBOX_HARDENED
+ co->unsealSP(sealer);
+ co->unsealFP(sealer);
+ co->unsealCalleeSavedRegisters(sealer);
+#endif // _LIBUNWIND_SANDBOX_HARDENED
}
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_SANDBOX_OTYPES
co->jumpto();