Skip to content

Commit

Permalink
libunwind: Use APIs exposed by RTLD to unwind the trusted stack
Browse files Browse the repository at this point in the history
  • Loading branch information
dpgao committed Jun 21, 2024
1 parent 6ce7200 commit 8e36b3f
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 144 deletions.
5 changes: 0 additions & 5 deletions contrib/subrepo-cheri-libunwind/include/__libunwind_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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__
24 changes: 0 additions & 24 deletions contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,12 +321,6 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace {
return get<v128>(addr);
}
capability_t getCapability(pint_t addr) { return get<capability_t>(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);
Expand Down Expand Up @@ -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;
Expand Down
84 changes: 71 additions & 13 deletions contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,78 @@
#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;

#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
// Must mirror the layout in rtld_c18n_machdep.h
struct compart_state {
void *fp;
void *pc;
void *regs[10]; // c19 to c28
void *sp;
};
#else
# error "LIBUNWIND_CHERI_C18N_SUPPORT is only supported on Morello"
#endif

bool c18n_is_enabled(void);
bool c18n_is_tramp(ptraddr_t, struct trusted_frame *);
struct trusted_frame *c18n_pop_trusted_stk(struct compart_state *,
struct trusted_frame *);

bool _c18n_is_enabled(void) {
return false;
}
bool _c18n_is_tramp(ptraddr_t, struct trusted_frame *) {
return false;
}
struct trusted_frame *_c18n_pop_trusted_stk(struct compart_state *,
struct trusted_frame *) {
return NULL;
}

_LIBUNWIND_WEAK_ALIAS(_c18n_is_enabled, c18n_is_enabled)
_LIBUNWIND_WEAK_ALIAS(_c18n_is_tramp, c18n_is_tramp)
_LIBUNWIND_WEAK_ALIAS(_c18n_pop_trusted_stk, c18n_pop_trusted_stk)
}

template <typename A, typename R>
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 getC18NState(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__
101 changes: 10 additions & 91 deletions contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -55,14 +57,6 @@ class DwarfInstructions {
typedef typename CFI_Parser<A>::FDE_Info FDE_Info;
typedef typename CFI_Parser<A>::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 &registers,
pint_t initialStackValue);
Expand Down Expand Up @@ -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<A, R> CompartInfo{};
#endif
};

template <typename R>
Expand Down Expand Up @@ -255,75 +252,6 @@ bool DwarfInstructions<A, R>::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, typename R>
typename A::pint_t DwarfInstructions<A, R>::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 <typename A, typename R>
bool DwarfInstructions<A, R>::isCompartmentTransitionTrampoline(
pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) {
ptraddr_t expectedReturnAddress =
addressSpace.template get<ptraddr_t>(ecsp + CI.kReturnAddressOffset);
CHERI_DBG(
"isCompartmentTransitionTrampoline(): expectedReturnAddress: 0x%lx\n",
expectedReturnAddress);
return expectedReturnAddress == returnAddress;
}
#else // _LIBUNWIND_TARGET_AARCH64
template <typename A, typename R>
typename A::pint_t DwarfInstructions<A, R>::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 <typename A, typename R>
bool DwarfInstructions<A, R>::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 <typename A, typename R>
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pc_t pc,
pint_t fdeStart, R &registers,
Expand Down Expand Up @@ -484,23 +412,14 @@ int DwarfInstructions<A, R>::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.getC18NState(newRegisters, csp);
}
}
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
10 changes: 5 additions & 5 deletions contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S
Original file line number Diff line number Diff line change
Expand Up @@ -838,16 +838,16 @@ 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)
WEAK_ALIAS(_c18n_noop, c18n_get_trusted_stk)
WEAK_ALIAS(_c18n_noop, c18n_unwind_trusted_stk)
#endif

//
Expand Down Expand Up @@ -902,7 +902,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]
Expand Down
5 changes: 0 additions & 5 deletions contrib/subrepo-cheri-libunwind/src/libunwind.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,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;

Expand Down

0 comments on commit 8e36b3f

Please sign in to comment.