Skip to content

Commit 87f9a3f

Browse files
committedJul 18, 2024
libunwind: Use APIs exposed by RTLD to unwind the trusted stack
1 parent 0119b31 commit 87f9a3f

7 files changed

+83
-145
lines changed
 

‎libunwind/include/__libunwind_config.h

-5
Original file line numberDiff line numberDiff line change
@@ -235,9 +235,4 @@
235235
# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287
236236
#endif // _LIBUNWIND_IS_NATIVE_ONLY
237237

238-
#if defined(_LIBUNWIND_CHERI_C18N_SUPPORT) && \
239-
!defined(_LIBUNWIND_TARGET_AARCH64)
240-
# error "LIBUNWIND_CHERI_C18N_SUPPORT is only supported on Morello"
241-
#endif
242-
243238
#endif // ____LIBUNWIND_CONFIG_H__

‎libunwind/src/AddressSpace.hpp

-24
Original file line numberDiff line numberDiff line change
@@ -321,12 +321,6 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace {
321321
return get<v128>(addr);
322322
}
323323
capability_t getCapability(pint_t addr) { return get<capability_t>(addr); }
324-
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
325-
static pint_t getUnwindSealer();
326-
static bool isValidSealer(pint_t sealer) {
327-
return __builtin_cheri_tag_get(sealer);
328-
}
329-
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT
330324
__attribute__((always_inline))
331325
uintptr_t getP(pint_t addr);
332326
uint64_t getRegister(pint_t addr);
@@ -415,24 +409,6 @@ inline uint64_t LocalAddressSpace::getRegister(pint_t addr) {
415409
#endif
416410
}
417411

418-
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
419-
extern "C" {
420-
/// Call into the RTLD to get a sealer capability. This sealer will be used to
421-
/// seal information in the unwinding context.
422-
uintptr_t _rtld_unw_getsealer();
423-
uintptr_t __rtld_unw_getsealer();
424-
_LIBUNWIND_HIDDEN uintptr_t __rtld_unw_getsealer() {
425-
return (uintptr_t)0;
426-
}
427-
_LIBUNWIND_WEAK_ALIAS(__rtld_unw_getsealer, _rtld_unw_getsealer)
428-
}
429-
430-
/// C++ wrapper for calling into RTLD.
431-
inline LocalAddressSpace::pint_t LocalAddressSpace::getUnwindSealer() {
432-
return _rtld_unw_getsealer();
433-
}
434-
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT
435-
436412
/// Read a ULEB128 into a 64-bit word.
437413
inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) {
438414
const uint8_t *p = (uint8_t *)addr;

‎libunwind/src/CompartmentInfo.hpp

+65-13
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,72 @@
1414
#define __COMPARTMENT_INFO_HPP__
1515

1616
namespace libunwind {
17-
class _LIBUNWIND_HIDDEN CompartmentInfo {
18-
public:
19-
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
20-
static CompartmentInfo sThisCompartmentInfo;
21-
// Per-architecture trusted stack frame layout.
17+
18+
extern "C" {
19+
20+
struct trusted_frame;
21+
22+
// Must mirror the layout in rtld_c18n_machdep.h
2223
#if defined(_LIBUNWIND_TARGET_AARCH64)
23-
static const uint32_t kNewSPOffset = 12 * sizeof(void *);
24-
static const uint32_t kNextOffset = 14 * sizeof(void *);
25-
static const uint32_t kCalleeSavedOffset = 2 * sizeof(void *);
26-
static const uint32_t kCalleeSavedCount = 10;
27-
static const uint32_t kReturnAddressOffset = 15 * sizeof(void *) + 8;
28-
static const uint32_t kPCOffset = sizeof(void *);
29-
#endif // _LIBUNWIND_TARGET_AARCH64
30-
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT
24+
struct compart_state {
25+
void *fp;
26+
void *pc;
27+
void *regs[10]; // c19 to c28
28+
void *sp;
29+
};
30+
#else
31+
# error "LIBUNWIND_CHERI_C18N_SUPPORT is not supported on this target"
32+
#endif
33+
34+
#pragma weak c18n_is_enabled
35+
bool c18n_is_enabled(void) {
36+
return false;
37+
};
38+
39+
#pragma weak c18n_is_tramp
40+
bool c18n_is_tramp(ptraddr_t, struct trusted_frame *);
41+
42+
#pragma weak c18n_pop_trusted_stk
43+
struct trusted_frame *
44+
c18n_pop_trusted_stk(struct compart_state *, struct trusted_frame *);
45+
}
46+
47+
template <typename A, typename R>
48+
struct CompartmentInfo {
49+
typedef typename A::pint_t pint_t;
50+
51+
static bool isC18NEnabled() {
52+
return c18n_is_enabled();
53+
}
54+
55+
static bool isC18NTramp(pint_t pc, pint_t tf) {
56+
return c18n_is_tramp(pc, (struct trusted_frame *)tf);
57+
}
58+
59+
static pint_t fillC18NState(R &newRegisters, pint_t tf) {
60+
struct compart_state state;
61+
tf = (pint_t)c18n_pop_trusted_stk(&state, (struct trusted_frame *)tf);
62+
63+
newRegisters.setTrustedStack(tf);
64+
CHERI_DBG("C18N: SET TRUSTED STACK %#p\n", (void *)tf);
65+
66+
newRegisters.setFP((pint_t)state.fp);
67+
CHERI_DBG("C18N: SET FP %#p\n", state.fp);
68+
69+
newRegisters.setSP((pint_t)state.sp);
70+
CHERI_DBG("C18N: SET SP: %#p\n", state.sp);
71+
72+
for (size_t i = 0; i < sizeof(state.regs) / sizeof(*state.regs); ++i) {
73+
newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i,
74+
(pint_t)state.regs[i]);
75+
CHERI_DBG("C18N: SET REGISTER: %lu (%s): %#p\n",
76+
UNW_ARM64_C19 + i,
77+
newRegisters.getRegisterName(UNW_ARM64_C19 + i),
78+
state.regs[i]);
79+
}
80+
81+
return (pint_t)state.pc;
82+
}
3183
};
3284
} // namespace libunwind
3385
#endif // __COMPARTMENT_INFO_HPP__

‎libunwind/src/DwarfInstructions.hpp

+10-91
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
#include "Registers.hpp"
2121
#include "DwarfParser.hpp"
2222
#include "config.h"
23+
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
2324
#include "CompartmentInfo.hpp"
25+
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT
2426

2527

2628
namespace libunwind {
@@ -55,14 +57,6 @@ class DwarfInstructions {
5557
typedef typename CFI_Parser<A>::FDE_Info FDE_Info;
5658
typedef typename CFI_Parser<A>::CIE_Info CIE_Info;
5759

58-
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
59-
static pint_t restoreRegistersFromSandbox(pint_t csp, A &addressSpace,
60-
R &newRegisters,
61-
CompartmentInfo &CI, pint_t sealer);
62-
static bool isCompartmentTransitionTrampoline(pint_t ecsp, A &addressSpace,
63-
CompartmentInfo &CI,
64-
pint_t returnAddress);
65-
#endif
6660
static pint_t evaluateExpression(pint_t expression, A &addressSpace,
6761
const R &registers,
6862
pint_t initialStackValue);
@@ -105,6 +99,9 @@ class DwarfInstructions {
10599
static bool getRA_SIGN_STATE(A &addressSpace, R registers, pint_t cfa,
106100
PrologInfo &prolog);
107101
#endif
102+
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
103+
static constexpr CompartmentInfo<A, R> CompartInfo{};
104+
#endif
108105
};
109106

110107
template <typename R>
@@ -255,75 +252,6 @@ bool DwarfInstructions<A, R>::getRA_SIGN_STATE(A &addressSpace, R registers,
255252
}
256253
#endif
257254

258-
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
259-
#if defined(_LIBUNWIND_TARGET_AARCH64)
260-
template <typename A, typename R>
261-
typename A::pint_t DwarfInstructions<A, R>::restoreRegistersFromSandbox(
262-
pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI,
263-
pint_t sealer) {
264-
// Get the unsealed executive CSP
265-
assert(__builtin_cheri_tag_get((void *)csp) &&
266-
"Executive stack should be tagged!");
267-
// Derive the new executive CSP
268-
pint_t nextCSP = addressSpace.getP(csp + CI.kNextOffset);
269-
// Seal ECSP
270-
nextCSP = __builtin_cheri_seal(nextCSP, sealer);
271-
assert(__builtin_cheri_tag_get((void *)nextCSP) &&
272-
"Next executive stack should be tagged!");
273-
CHERI_DBG("SANDBOX: SETTING EXECUTIVE CSP %#p\n", (void *)nextCSP);
274-
newRegisters.setTrustedStack(nextCSP);
275-
// Restore the next RCSP
276-
pint_t nextRCSP = addressSpace.getP(csp + CI.kNewSPOffset);
277-
newRegisters.setSP(nextRCSP);
278-
CHERI_DBG("SANDBOX: SETTING RESTRICTED CSP: %#p\n",
279-
(void *)newRegisters.getSP());
280-
// Restore callee-saved registers
281-
// Restore: c19-c28
282-
for (size_t i = 0, offset = CI.kCalleeSavedOffset; i < CI.kCalleeSavedCount;
283-
++i, offset += sizeof(void *)) {
284-
pint_t regValue = addressSpace.getP(csp + offset);
285-
newRegisters.setCapabilityRegister(UNW_ARM64_C19 + i, regValue);
286-
CHERI_DBG("SETTING CALLEE SAVED CAPABILITY REGISTER: %lu (%s): %#p "
287-
"(offset=%zu)\n",
288-
UNW_ARM64_C19 + i,
289-
newRegisters.getRegisterName(UNW_ARM64_C19 + i), (void *)regValue,
290-
offset);
291-
}
292-
// Restore the frame pointer
293-
pint_t newFP = addressSpace.getP(csp);
294-
CHERI_DBG("SANDBOX: SETTING CFP %#p\n", (void *)newFP);
295-
newRegisters.setFP(newFP);
296-
// Get the new return address.
297-
return addressSpace.getP(csp + CI.kPCOffset);
298-
}
299-
300-
template <typename A, typename R>
301-
bool DwarfInstructions<A, R>::isCompartmentTransitionTrampoline(
302-
pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) {
303-
ptraddr_t expectedReturnAddress =
304-
addressSpace.template get<ptraddr_t>(ecsp + CI.kReturnAddressOffset);
305-
CHERI_DBG(
306-
"isCompartmentTransitionTrampoline(): expectedReturnAddress: 0x%lx\n",
307-
expectedReturnAddress);
308-
return expectedReturnAddress == returnAddress;
309-
}
310-
#else // _LIBUNWIND_TARGET_AARCH64
311-
template <typename A, typename R>
312-
typename A::pint_t DwarfInstructions<A, R>::restoreRegistersFromSandbox(
313-
pint_t csp, A &addressSpace, R &newRegisters, CompartmentInfo &CI,
314-
pint_t sealer) {
315-
assert(0 && "not implemented on this architecture");
316-
return (pint_t)0;
317-
}
318-
template <typename A, typename R>
319-
bool DwarfInstructions<A, R>::isCompartmentTransitionTrampoline(
320-
pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) {
321-
assert(0 && "not implemented on this architecture");
322-
return false;
323-
}
324-
#endif // _LIBUNWIND_TARGET_AARCH64
325-
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT
326-
327255
template <typename A, typename R>
328256
int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pc_t pc,
329257
pint_t fdeStart, R &registers,
@@ -484,23 +412,14 @@ int DwarfInstructions<A, R>::stepWithDwarf(A &addressSpace, pc_t pc,
484412
#endif
485413

486414
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
487-
// If the sealer is not valid (only the case when we're running without
488-
// c18n), check if the return address has the executive mode bit set.
489-
// If so, we should be calling into the c18n RTLD as this is a
490-
// compartment boundary. We need to restore registers from the executive
491-
// stack and ask rtld for it.
492-
uintptr_t sealer = addressSpace.getUnwindSealer();
493-
if (addressSpace.isValidSealer(sealer)) {
415+
// If c18n is enabled, check if we are at a compartment boundary. If so,
416+
// restore registers from the trusted stack by asking rtld for them.
417+
if (CompartInfo.isC18NEnabled()) {
494418
pint_t csp = registers.getTrustedStack();
495-
if (__builtin_cheri_sealed_get(csp))
496-
csp = __builtin_cheri_unseal(csp, sealer);
497-
CompartmentInfo &CI = CompartmentInfo::sThisCompartmentInfo;
498-
if (csp != 0 && isCompartmentTransitionTrampoline(csp, addressSpace, CI,
499-
returnAddress)) {
419+
if (CompartInfo.isC18NTramp(returnAddress, csp)) {
500420
CHERI_DBG("%#p: detected a trampoline, unwinding from sandbox\n",
501421
(void *)returnAddress);
502-
returnAddress = restoreRegistersFromSandbox(
503-
csp, addressSpace, newRegisters, CI, sealer);
422+
returnAddress = CompartInfo.fillC18NState(newRegisters, csp);
504423
}
505424
}
506425
#endif

‎libunwind/src/UnwindRegistersRestore.S

+1-1
Original file line numberDiff line numberDiff line change
@@ -714,7 +714,7 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto)
714714
#ifdef __CHERI_PURE_CAPABILITY__
715715
ldr c1, [c0, #0x1f0] // Pass the target untrusted stack pointer
716716
ldr c2, [c0, #0x210] // Pass the target trusted stack pointer
717-
bl _rtld_unw_setcontext
717+
bl c18n_unwind_trusted_stk
718718

719719
// skip restore of c0,c1 for now
720720
ldp c2, c3, [c0, #0x020]

‎libunwind/src/UnwindRegistersSave.S

+7-5
Original file line numberDiff line numberDiff line change
@@ -838,16 +838,18 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
838838
#elif defined(__aarch64__)
839839

840840
#ifdef __CHERI_PURE_CAPABILITY__
841-
DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_noop)
841+
DEFINE_LIBUNWIND_FUNCTION(_c18n_noop)
842842
#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI
843843
and x30, x30, #~1
844844
ret x30
845845
#else
846846
ret
847847
#endif
848-
END_LIBUNWIND_FUNCTION(__rtld_unw_noop)
849-
WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_getcontext)
850-
WEAK_ALIAS(__rtld_unw_noop, _rtld_unw_setcontext)
848+
END_LIBUNWIND_FUNCTION(_c18n_noop)
849+
// uintptr_t c18n_get_trusted_stk(uintptr_t, struct trusted_frame **);
850+
WEAK_ALIAS(_c18n_noop, c18n_get_trusted_stk)
851+
// uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *);
852+
WEAK_ALIAS(_c18n_noop, c18n_unwind_trusted_stk)
851853
#endif
852854

853855
//
@@ -902,7 +904,7 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext)
902904
str d30, [c0, #0x0f0]
903905
str d31, [c0, #0x0f8]
904906
mov x0, #0 // return UNW_ESUCCESS
905-
b _rtld_unw_getcontext
907+
b c18n_get_trusted_stk
906908
#else
907909
stp x0, x1, [x0, #0x000]
908910
stp x2, x3, [x0, #0x010]

‎libunwind/src/libunwind.cpp

-6
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
#if !defined(__USING_SJLJ_EXCEPTIONS__)
3030
#include "AddressSpace.hpp"
31-
#include "CompartmentInfo.hpp"
3231
#include "UnwindCursor.hpp"
3332

3433

@@ -43,11 +42,6 @@ using namespace libunwind;
4342
/// internal object to represent this processes address space
4443
LocalAddressSpace LocalAddressSpace::sThisAddressSpace;
4544

46-
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
47-
/// internal object to represent this processes compartment information
48-
CompartmentInfo CompartmentInfo::sThisCompartmentInfo;
49-
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT
50-
5145
_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space =
5246
(unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace;
5347

0 commit comments

Comments
 (0)
Please sign in to comment.