Skip to content

Commit

Permalink
git subrepo pull contrib/subrepo-cheri-libunwind
Browse files Browse the repository at this point in the history
subrepo:
  subdir:   "contrib/subrepo-cheri-libunwind"
  merged:   "af9f3c32d12f"
upstream:
  origin:   "https://github.com/CTSRD-CHERI/libunwind.git"
  branch:   "monorepo-mirror"
  commit:   "af9f3c32d12f"
git-subrepo:
  version:  "0.4.1"
  origin:   "https://github.com/ingydotnet/git-subrepo.git"
  commit:   "1f13869"
  • Loading branch information
jrtc27 committed Jun 13, 2024
1 parent 85ac625 commit 5b7ece3
Show file tree
Hide file tree
Showing 12 changed files with 291 additions and 31 deletions.
4 changes: 2 additions & 2 deletions contrib/subrepo-cheri-libunwind/.gitrepo
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[subrepo]
remote = https://github.com/CTSRD-CHERI/libunwind.git
branch = monorepo-mirror
commit = 6c62b215fdf717402c5f87b1aecd04277a93a497
parent = 81ffb8bee5182099dfc5a7961ebaf1fd28f73963
commit = af9f3c32d12fdf70f8b93fa77e42a2bedafd6c9a
parent = 85ac6254113dc6e04c7efc4882d4e97453375f25
method = rebase
cmdver = 0.4.1
9 changes: 9 additions & 0 deletions contrib/subrepo-cheri-libunwind/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)")
Expand Down Expand Up @@ -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 "ARM64" 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
Expand Down
9 changes: 7 additions & 2 deletions contrib/subrepo-cheri-libunwind/include/__libunwind_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
# error "LIBUNWIND_CHERI_C18N_SUPPORT is only supported on Morello"
#endif

#endif // ____LIBUNWIND_CONFIG_H__
6 changes: 5 additions & 1 deletion contrib/subrepo-cheri-libunwind/include/libunwind.h
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,11 @@ enum {
UNW_ARM64_C30 = 228,
UNW_ARM64_CLR = 228,
UNW_ARM64_C31 = 229,
UNW_ARM64_CSP = 229
UNW_ARM64_CSP = 229,
// Use 240 for ECSP (executive stack pointer). ECSP is not a real DWARF
// register, but we need it to implement c18n-aware unwinding. We pick 240
// because it is far enough away from the range of reserved registers on Arm.
UNW_ARM64_ECSP = 240,
};

// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1.
Expand Down
30 changes: 28 additions & 2 deletions contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace {
}
bool isNull() const { return value == nullptr; }
bool isValid() const {
#ifdef __CHERI_PURE_CAPABILITY__
#if defined(__CHERI_PURE_CAPABILITY__) && \
!defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI)
return __builtin_cheri_tag_get(value);
#else
return !isNull();
Expand Down Expand Up @@ -320,6 +321,12 @@ 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 @@ -408,6 +415,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();
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 Expand Up @@ -932,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<bool>(found);
#endif
Expand Down
33 changes: 33 additions & 0 deletions contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// 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 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
};
} // namespace libunwind
#endif // __COMPARTMENT_INFO_HPP__
125 changes: 114 additions & 11 deletions contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "Registers.hpp"
#include "DwarfParser.hpp"
#include "config.h"
#include "CompartmentInfo.hpp"


namespace libunwind {
Expand Down Expand Up @@ -54,6 +55,14 @@ 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 @@ -246,6 +255,75 @@ 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 @@ -274,6 +352,7 @@ int DwarfInstructions<A, R>::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.
CHERI_DBG("SETTING SP: %#p\n", (void *)cfa);
newRegisters.setSP(cfa);

pint_t returnAddress = 0;
Expand All @@ -297,25 +376,26 @@ int DwarfInstructions<A, R>::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,
prolog.savedRegisters[i]));
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);
}
}

Expand Down Expand Up @@ -403,9 +483,32 @@ 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)) {
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)) {
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;
Expand Down
15 changes: 12 additions & 3 deletions contrib/subrepo-cheri-libunwind/src/Registers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1866,14 +1866,18 @@ class _LIBUNWIND_HIDDEN Registers_arm64 {
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; }
uintptr_t getIP() const { return _registers.__pc; }
void setIP(uintptr_t value) { _registers.__pc = value; }
uintptr_t getFP() const { return _registers.__fp; }
void setFP(uintptr_t value) { _registers.__fp = value; }
#if defined(__CHERI_PURE_CAPABILITY__) && defined(_LIBUNWIND_CHERI_C18N_SUPPORT)
uintptr_t getTrustedStack() const { return _registers.__ecsp; }
void setTrustedStack(uintptr_t value) { _registers.__ecsp = value; }
#endif // __CHERI_PURE_CAPABILITY__ && _LIBUNWIND_CHERI_C18N_SUPPORT

private:
struct GPRs {
Expand All @@ -1882,6 +1886,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
};

Expand All @@ -1898,8 +1905,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");
Expand Down Expand Up @@ -2198,6 +2205,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";
}
Expand Down
Loading

0 comments on commit 5b7ece3

Please sign in to comment.