diff --git a/bin/cheribsdtest/Makefile b/bin/cheribsdtest/Makefile index 2fe4c81e45aa..8a17ec85ea74 100644 --- a/bin/cheribsdtest/Makefile +++ b/bin/cheribsdtest/Makefile @@ -13,7 +13,7 @@ SUBDIR+= purecap \ purecap-dynamic-mt \ purecap-mt -.if ${MACHINE_CPUARCH} == "aarch64" && ${MACHINE_ABI:Mpurecap} +.if ${MACHINE_ABI:Mpurecap} SUBDIR+= mt-c18n .endif .endif diff --git a/bin/cheribsdtest/cheribsdtest_registers.c b/bin/cheribsdtest/cheribsdtest_registers.c index 444b04fd346c..9e319c384f28 100644 --- a/bin/cheribsdtest/cheribsdtest_registers.c +++ b/bin/cheribsdtest/cheribsdtest_registers.c @@ -172,7 +172,7 @@ check_initreg_code(void * __capability c) (CHERI_PERMS_SWALL & ~CHERI_PERM_SW_VMEM)); /* Check that the raw permission bits match the kernel header: */ -#if defined(CHERIBSD_C18N_TESTS) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI) +#if defined(CHERIBSD_C18N_TESTS) && defined(__aarch64__) && !defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI) if (v != (CHERI_CAP_USER_CODE_PERMS & ~CHERI_PERM_EXECUTIVE)) cheribsdtest_failure_errx("perms %jx (expected %jx)", v, (uintmax_t)(CHERI_CAP_USER_CODE_PERMS & ~CHERI_PERM_EXECUTIVE)); diff --git a/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h b/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h index 5b3f0bff628c..cc14c084072c 100644 --- a/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h +++ b/contrib/subrepo-cheri-libunwind/include/__libunwind_config.h @@ -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__ diff --git a/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp b/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp index 17b05a224ebf..892c97f71873 100644 --- a/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp +++ b/contrib/subrepo-cheri-libunwind/src/AddressSpace.hpp @@ -321,12 +321,6 @@ class _LIBUNWIND_HIDDEN LocalAddressSpace { return get(addr); } capability_t getCapability(pint_t addr) { return get(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); @@ -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; diff --git a/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp b/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp index 08ff723c7220..e9429becdfa7 100644 --- a/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp +++ b/contrib/subrepo-cheri-libunwind/src/CompartmentInfo.hpp @@ -14,20 +14,72 @@ #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; + +// Must mirror the layout in rtld_c18n_machdep.h #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 +struct compart_state { + void *fp; + void *pc; + void *regs[10]; // c19 to c28 + void *sp; +}; +#else +# error "LIBUNWIND_CHERI_C18N_SUPPORT is not supported on this target" +#endif + +#pragma weak c18n_is_enabled +bool c18n_is_enabled(void) { + return false; +}; + +#pragma weak c18n_is_tramp +bool c18n_is_tramp(ptraddr_t, struct trusted_frame *); + +#pragma weak c18n_pop_trusted_stk +struct trusted_frame * +c18n_pop_trusted_stk(struct compart_state *, struct trusted_frame *); +} + +template +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 fillC18NState(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__ diff --git a/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp b/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp index dc55191e8c93..32a0dcc32aa1 100644 --- a/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp +++ b/contrib/subrepo-cheri-libunwind/src/DwarfInstructions.hpp @@ -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 { @@ -55,14 +57,6 @@ class DwarfInstructions { typedef typename CFI_Parser::FDE_Info FDE_Info; typedef typename CFI_Parser::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 ®isters, pint_t initialStackValue); @@ -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 CompartInfo{}; +#endif }; template @@ -255,75 +252,6 @@ bool DwarfInstructions::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::pint_t DwarfInstructions::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 -bool DwarfInstructions::isCompartmentTransitionTrampoline( - pint_t ecsp, A &addressSpace, CompartmentInfo &CI, pint_t returnAddress) { - ptraddr_t expectedReturnAddress = - addressSpace.template get(ecsp + CI.kReturnAddressOffset); - CHERI_DBG( - "isCompartmentTransitionTrampoline(): expectedReturnAddress: 0x%lx\n", - expectedReturnAddress); - return expectedReturnAddress == returnAddress; -} -#else // _LIBUNWIND_TARGET_AARCH64 -template -typename A::pint_t DwarfInstructions::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 -bool DwarfInstructions::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 int DwarfInstructions::stepWithDwarf(A &addressSpace, pc_t pc, pint_t fdeStart, R ®isters, @@ -484,23 +412,14 @@ int DwarfInstructions::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.fillC18NState(newRegisters, csp); } } #endif diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S index e20d2b125c95..b93e7c7b44e1 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersRestore.S @@ -703,25 +703,6 @@ Lnovec: #elif defined(__aarch64__) -// -// extern "C" void __rtld_unw_setcontext(void *c0, void *c1, -// void *rcsp, void **sealed_ecsp); -// -#if defined(__CHERI_PURE_CAPABILITY__) -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) - mov c16, c2 - ldp c2, c3, [c3, #(-0x210 + 0x20)] - mov csp, c16 -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - and x30, x30, #~1 - ret x30 -#else - ret -#endif -END_LIBUNWIND_FUNCTION(__rtld_unw_setcontext) -WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) -#endif - // // extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); // @@ -731,8 +712,12 @@ WEAK_ALIAS(__rtld_unw_setcontext, _rtld_unw_setcontext) .p2align 2 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 c18n_unwind_trusted_stk + // skip restore of c0,c1 for now - // also skip restoring c2 and c3 because they will get clobbered later on + ldp c2, c3, [c0, #0x020] ldp c4, c5, [c0, #0x040] ldp c6, c7, [c0, #0x060] ldp c8, c9, [c0, #0x080] @@ -772,17 +757,14 @@ DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) // context struct, because it is allocated on the stack, and an exception // could clobber the de-allocated portion of the stack after csp has been // restored. - ldr c2, [c0, #0x1f0] - add c3, c0, #0x210 - ldp c0, c1, [c0, #0x000] - // XXX: variant PCS is not yet supported by rtld, work around it - // using a function pointer. - adrp c16, :got:_rtld_unw_setcontext - ldr c16, [c16, :got_lo12:_rtld_unw_setcontext] + ldr c16, [c0, #0x1f0] + ldp c0, c1, [c0, #0x000] // restore c0,c1 + mov csp,c16 // restore csp #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x16 + and x30, x30, #~1 + ret x30 // jump to pc #else - br c16 + ret // jump to pcc #endif #else // skip restore of x0,x1 for now diff --git a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S index 8edcd4cb51d0..74f07812d834 100644 --- a/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S +++ b/contrib/subrepo-cheri-libunwind/src/UnwindRegistersSave.S @@ -837,13 +837,19 @@ DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) #elif defined(__aarch64__) -#if defined(__CHERI_PURE_CAPABILITY__) -DEFINE_LIBUNWIND_FUNCTION(__rtld_unw_getcontext) - mov c2, csp - str c2, [c1] - ret c30 -END_LIBUNWIND_FUNCTION(__rtld_unw_getcontext) -WEAK_ALIAS(__rtld_unw_getcontext, _rtld_unw_getcontext) +#ifdef __CHERI_PURE_CAPABILITY__ +DEFINE_LIBUNWIND_FUNCTION(_c18n_noop) +#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI + and x30, x30, #~1 + ret x30 +#else + ret +#endif +END_LIBUNWIND_FUNCTION(_c18n_noop) +// uintptr_t c18n_get_trusted_stk(uintptr_t, struct trusted_frame **); +WEAK_ALIAS(_c18n_noop, c18n_get_trusted_stk) +// uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *); +WEAK_ALIAS(_c18n_noop, c18n_unwind_trusted_stk) #endif // @@ -898,7 +904,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] diff --git a/contrib/subrepo-cheri-libunwind/src/libunwind.cpp b/contrib/subrepo-cheri-libunwind/src/libunwind.cpp index 4b5748c3bab7..83648894a740 100644 --- a/contrib/subrepo-cheri-libunwind/src/libunwind.cpp +++ b/contrib/subrepo-cheri-libunwind/src/libunwind.cpp @@ -28,7 +28,6 @@ #if !defined(__USING_SJLJ_EXCEPTIONS__) #include "AddressSpace.hpp" -#include "CompartmentInfo.hpp" #include "UnwindCursor.hpp" @@ -43,11 +42,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; diff --git a/lib/Makefile b/lib/Makefile index f1121c8f829d..405522cb7f70 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -113,7 +113,7 @@ SUBDIR= ${SUBDIR_BOOTSTRAP} \ ncurses \ nss_tacplus -.if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" +.if ${MACHINE_ABI:Mpurecap} && (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv") .if !defined(COMPAT_LIBCOMPAT) || ${COMPAT_LIBCOMPAT} == "64CB" SUBDIR+= c18n .endif diff --git a/lib/libc/aarch64/gen/_setjmp.S b/lib/libc/aarch64/gen/_setjmp.S index 1336dc154c6e..ce8a7fe0ad3b 100644 --- a/lib/libc/aarch64/gen/_setjmp.S +++ b/lib/libc/aarch64/gen/_setjmp.S @@ -55,15 +55,15 @@ ENTRY(_setjmp) #endif /* Return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c1, c0 #endif mov x0, #0 -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Tail-call to save Executive mode state */ - b _rtld_setjmp + b c18n_get_trusted_stk #else RETURN #endif @@ -81,9 +81,7 @@ ENTRY(_longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) - mov c2, c8 -#else +#ifndef CHERI_LIB_C18N mov REGN(sp), REG(8) #endif @@ -104,16 +102,17 @@ ENTRY(_longjmp) #endif /* Load the return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) - mov c3, c0 +#ifdef CHERI_LIB_C18N + ldr c2, [c0] #endif cmp x1, #0 csinc x0, x1, xzr, ne -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N + mov c1, c8 /* * Tail-call to restore Executive mode state */ - b _rtld_longjmp + b c18n_unwind_trusted_stk #else RETURN #endif diff --git a/lib/libc/aarch64/gen/setjmp.S b/lib/libc/aarch64/gen/setjmp.S index 3df8dde53469..47fc275c527a 100644 --- a/lib/libc/aarch64/gen/setjmp.S +++ b/lib/libc/aarch64/gen/setjmp.S @@ -32,11 +32,6 @@ #include #include -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) -.weak _rtld_setjmp -.weak _rtld_longjmp -#endif - ENTRY(setjmp) sub REGN(sp), REGN(sp), #(REG_WIDTH * 2) stp REG(0), REGN(lr), [REGN(sp)] @@ -70,15 +65,15 @@ ENTRY(setjmp) stp d14, d15, [REG(0)], #16 /* Return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N mov c1, c0 #endif mov x0, #0 -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Tail-call to save Executive mode state */ - b _rtld_setjmp + b c18n_get_trusted_stk #else RETURN #endif @@ -110,9 +105,7 @@ ENTRY(longjmp) /* Restore the stack pointer */ ldr REG(8), [REG(0)], #(REG_WIDTH) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) - mov c2, c8 -#else +#ifndef CHERI_LIB_C18N mov REGN(sp), REG(8) #endif @@ -131,16 +124,17 @@ ENTRY(longjmp) ldp d14, d15, [REG(0)], #16 /* Load the return value */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) - mov c3, c0 +#ifdef CHERI_LIB_C18N + ldr c2, [c0] #endif cmp x1, #0 csinc x0, x1, xzr, ne -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N + mov c1, c8 /* * Tail-call to restore Executive mode state */ - b _rtld_longjmp + b c18n_unwind_trusted_stk #else RETURN #endif diff --git a/lib/libc/gen/posix_spawn.c b/lib/libc/gen/posix_spawn.c index 1e91d99d03b4..48df35b0fbd5 100644 --- a/lib/libc/gen/posix_spawn.c +++ b/lib/libc/gen/posix_spawn.c @@ -341,7 +341,7 @@ do_posix_spawn(pid_t *pid, const char *path, p = rfork_thread(RFSPAWN, stack + stacksz, _posix_spawn_thr, &psa); free(stack); #else -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N p = __sys_rfork(RFSPAWN); #else p = rfork(RFSPAWN); diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 2ef540f2fc0c..6a000c7fa6b9 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -394,7 +394,7 @@ struct __nl_cat_d *__catopen_l(const char *name, int type, int __strerror_rl(int errnum, char *strerrbuf, size_t buflen, struct _xlocale *locale); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N __pid_t __sys_rfork(int); int sigaction_c18n(int, const struct sigaction *, struct sigaction *); #endif diff --git a/lib/libc/riscv/gen/_setjmp.S b/lib/libc/riscv/gen/_setjmp.S index 050113cb0e26..0d22ae268167 100644 --- a/lib/libc/riscv/gen/_setjmp.S +++ b/lib/libc/riscv/gen/_setjmp.S @@ -80,8 +80,19 @@ ENTRY(_setjmp) #endif /* Return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca0 +#endif li a0, 0 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + /* + * Tail-call to save trusted stack state + */ + clgc ct0, _C_LABEL(c18n_get_trusted_stk) + cjr ct0 +#else cret +#endif #else /* Store the magic value and stack pointer */ ld t0, .Lmagic @@ -140,7 +151,11 @@ ENTRY(_longjmp) bne t0, t1, botch /* Restore the stack pointer */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca3, 16(ca0) +#else clc csp, 16(ca0) +#endif cincoffset ca0, ca0, (2 * 16) /* Restore the general purpose registers and ra */ @@ -179,8 +194,20 @@ ENTRY(_longjmp) #endif /* Load the return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca2, 0(ca0) +#endif mv a0, a1 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca3 + /* + * Tail-call to restore trusted stack state + */ + clgc ct0, _C_LABEL(c18n_unwind_trusted_stk) + cjr ct0 +#else cret +#endif #else /* Check the magic value */ ld t0, 0(a0) diff --git a/lib/libc/riscv/gen/setjmp.S b/lib/libc/riscv/gen/setjmp.S index 672f7f50482b..3a9d7035ab93 100644 --- a/lib/libc/riscv/gen/setjmp.S +++ b/lib/libc/riscv/gen/setjmp.S @@ -95,8 +95,19 @@ ENTRY(setjmp) #endif /* Return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca0 +#endif li a0, 0 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + /* + * Tail-call to save trusted stack state + */ + clgc ct0, _C_LABEL(c18n_get_trusted_stk) + cjr ct0 +#else cret +#endif #else addi sp, sp, -(2 * 8) sd a0, 0(sp) @@ -186,7 +197,11 @@ ENTRY(longjmp) cincoffset csp, csp, (3 * 16) /* Restore the stack pointer */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca3, 16(ca0) +#else clc csp, 16(ca0) +#endif cincoffset ca0, ca0, (2 * 16) /* Restore the general purpose registers and ra */ @@ -225,8 +240,20 @@ ENTRY(longjmp) #endif /* Load the return value */ +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + clc ca2, 0(ca0) +#endif mv a0, a1 +#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) + cmove ca1, ca3 + /* + * Tail-call to restore trusted stack state + */ + clgc ct0, _C_LABEL(c18n_unwind_trusted_stk) + cjr ct0 +#else cret +#endif botch: clgc ct0, _C_LABEL(longjmperror) diff --git a/lib/libc/sys/sigaction.c b/lib/libc/sys/sigaction.c index 28918f6e726c..568dc7dc4526 100644 --- a/lib/libc/sys/sigaction.c +++ b/lib/libc/sys/sigaction.c @@ -35,7 +35,7 @@ __weak_reference(__sys_sigaction, __sigaction); __weak_reference(sigaction, __libc_sigaction); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * This weak symbol will always be resolved at runtime. */ diff --git a/lib/libgcc_s/Symbol-c18n.map b/lib/libgcc_s/Symbol-c18n.map index 7b817e2f56ef..d9aa2213192d 100644 --- a/lib/libgcc_s/Symbol-c18n.map +++ b/lib/libgcc_s/Symbol-c18n.map @@ -1,7 +1,7 @@ FBSDprivate_1.0 { - _rtld_unw_getcontext; - _rtld_unw_setcontext; - _rtld_unw_getcontext_unsealed; - _rtld_unw_setcontext_unsealed; - _rtld_unw_getsealer; + c18n_get_trusted_stk; + c18n_unwind_trusted_stk; + c18n_is_enabled; + c18n_is_tramp; + c18n_pop_trusted_stk; }; diff --git a/lib/libsys/interposing_table.c b/lib/libsys/interposing_table.c index 5c6d81d6ff51..989aa6f75652 100644 --- a/lib/libsys/interposing_table.c +++ b/lib/libsys/interposing_table.c @@ -56,7 +56,7 @@ static interpos_func_t __libsys_interposing[INTERPOS_MAX] = { SLOT(sendmsg, __sys_sendmsg), SLOT(sendto, __sys_sendto), SLOT(setcontext, __sys_setcontext), -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N SLOT(sigaction, sigaction_c18n), #else SLOT(sigaction, __sys_sigaction), diff --git a/lib/libsys/riscv/Makefile.sys b/lib/libsys/riscv/Makefile.sys index 2ff84735f484..5cfd48643944 100644 --- a/lib/libsys/riscv/Makefile.sys +++ b/lib/libsys/riscv/Makefile.sys @@ -1,5 +1,8 @@ SRCS+= __vdso_gettc.c \ sched_getcpu_gen.c -MDASM= cerror.S \ - vfork.S +MDASM= cerror.S + +.ifndef CHERI_LIB_C18N +MDASM+= vfork.S +.endif diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index bf5cfe3530fb..9c41a88c1755 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -56,7 +56,7 @@ static int create_stack(struct pthread_attr *pattr); static void thread_start(struct pthread *curthread) __used; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #pragma weak _thread_start = thread_start /* @@ -151,7 +151,7 @@ _pthread_create(pthread_t * __restrict thread, new_thread->flags = THR_FLAGS_NEED_SUSPEND; create_suspended = 1; } else { -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * c18n: Always block all signals when creating a new thread to * allow RTLD to set up the environment to handle signals. @@ -187,7 +187,7 @@ _pthread_create(pthread_t * __restrict thread, locked = 1; } else locked = 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N param.start_func = (void (*)(void *)) _rtld_thread_start; #else param.start_func = (void (*)(void *)) thread_start; @@ -298,7 +298,7 @@ thread_start(struct pthread *curthread) sigset_t set; bool restore_sigmask; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * At this point, curthread->tcb contains a fake wrapper TCB created by * RTLD when the thread was created. The real TCB has now been installed diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c index 21c643c8dcab..f94622c714f2 100644 --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -50,7 +50,7 @@ static void _thr_rtld_rlock_acquire(void *); static int _thr_rtld_set_flag(int); static void _thr_rtld_wlock_acquire(void *); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N void _thread_start(struct pthread *); void _thr_sighandler(int, siginfo_t *, void *); @@ -291,7 +291,7 @@ _thr_rtld_init(void) /* mask signals, also force to resolve __sys_sigprocmask PLT */ _thr_signal_block(curthread); _rtld_thread_init(&li); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N _rtld_thread_start_init(_thread_start); _rtld_sighandler_init(_thr_sighandler); #endif diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index 685561e2b07e..c77d7bc04da0 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -69,7 +69,7 @@ static void handle_signal(struct sigaction *, int, siginfo_t *, ucontext_t *); static void check_deferred_signal(struct pthread *); static void check_suspend(struct pthread *); static void check_cancel(struct pthread *curthread, ucontext_t *ucp); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #pragma weak _thr_sighandler = thr_sighandler /* @@ -296,7 +296,7 @@ handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) if (!cancel_async) curthread->cancel_enable = 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N (void)sigfunc; #else /* restore correct mask before calling user handler */ @@ -313,7 +313,7 @@ handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) * so after setjmps() returns once more, the user code may need to * re-set cancel_enable flag by calling pthread_setcancelstate(). */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N _rtld_siginvoke(sig, info, ucp, actp); #else if ((actp->sa_flags & SA_SIGINFO) != 0) { @@ -336,7 +336,7 @@ handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) /* reschedule cancellation */ check_cancel(curthread, &uc2); errno = err; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Calling sigreturn outside of sigcode does not work with * compartmentalisation. Hence we set the user context and let the @@ -432,7 +432,7 @@ check_deferred_signal(struct pthread *curthread) /* remove signal */ curthread->deferred_siginfo.si_signo = 0; handle_signal(&act, info.si_signo, &info, uc); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N setcontext(uc); #endif } @@ -500,7 +500,7 @@ _thr_signal_init(int dlopened) for (sig = 1; sig <= _SIG_MAXSIG; sig++) { if (sig == SIGCANCEL) continue; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N error = _rtld_sigaction(sig, NULL, &oact); #else error = __sys_sigaction(sig, NULL, &oact); @@ -514,7 +514,7 @@ _thr_signal_init(int dlopened) remove_thr_signals(&usa->sigact.sa_mask); nact.sa_flags &= ~SA_NODEFER; nact.sa_flags |= SA_SIGINFO; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* XXX: Ignore sigaltstack for now */ nact.sa_flags &= ~SA_ONSTACK; nact.sa_sigaction = _rtld_sighandler; @@ -650,7 +650,7 @@ __thr_sigaction(int sig, const struct sigaction *act, struct sigaction *oact) remove_thr_signals(&usa->sigact.sa_mask); newact.sa_flags &= ~SA_NODEFER; newact.sa_flags |= SA_SIGINFO; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* XXX: Ignore sigaltstack for now */ newact.sa_flags &= ~SA_ONSTACK; newact.sa_sigaction = _rtld_sighandler; diff --git a/libexec/rtld-elf/Makefile b/libexec/rtld-elf/Makefile index 87406ded2bea..c977b970cf21 100644 --- a/libexec/rtld-elf/Makefile +++ b/libexec/rtld-elf/Makefile @@ -29,7 +29,7 @@ MK_UBSAN= no .include -.if ${MACHINE_ABI:Mpurecap} && ${MACHINE_CPUARCH} == "aarch64" +.if ${MACHINE_ABI:Mpurecap} && (${MACHINE_CPUARCH} == "aarch64" || ${MACHINE_CPUARCH} == "riscv") CHERI_LIB_C18N= .endif diff --git a/libexec/rtld-elf/Symbol-c18n.map b/libexec/rtld-elf/Symbol-c18n.map index 3729640162b9..2e7fe32e69b7 100644 --- a/libexec/rtld-elf/Symbol-c18n.map +++ b/libexec/rtld-elf/Symbol-c18n.map @@ -8,13 +8,9 @@ FBSDprivate_1.0 { _rtld_sighandler; _rtld_siginvoke; _rtld_sigaction; - _rtld_setjmp; - _rtld_longjmp; - _rtld_unw_getcontext; - _rtld_unw_getcontext_unsealed; - _rtld_unw_setcontext; - _rtld_unw_setcontext_unsealed; - _rtld_unw_getsealer; - _rtld_safebox_code; - _rtld_sandbox_code; + c18n_get_trusted_stk; + c18n_unwind_trusted_stk; + c18n_is_enabled; + c18n_is_tramp; + c18n_pop_trusted_stk; }; diff --git a/libexec/rtld-elf/aarch64/reloc.c b/libexec/rtld-elf/aarch64/reloc.c index 0bd8f75672ef..b18322a8e56a 100644 --- a/libexec/rtld-elf/aarch64/reloc.c +++ b/libexec/rtld-elf/aarch64/reloc.c @@ -38,7 +38,7 @@ #include "debug.h" #include "rtld.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif #include "rtld_printf.h" @@ -112,14 +112,14 @@ init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) obj->pltgot[1] = (uintptr_t)cheri_seal(obj, sealer_pltgot); else #endif obj->pltgot[1] = (uintptr_t)obj; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) obj->pltgot[2] = (uintptr_t)&_rtld_bind_start_c18n; else @@ -510,7 +510,7 @@ reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) continue; } target = (uintptr_t)make_function_pointer(def, defobj); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { .target = (void *)target, .defobj = defobj, @@ -572,7 +572,7 @@ reloc_iresolve_one(Obj_Entry *obj, const Elf_Rela *rela, ptr = (uintptr_t)(obj->relocbase + rela->r_addend); #endif lock_release(rtld_bind_lock, lockstate); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N ptr = (uintptr_t)tramp_intern(NULL, &(struct tramp_data) { .target = (void *)ptr, .defobj = obj, @@ -663,7 +663,7 @@ reloc_gnu_ifunc(Obj_Entry *obj, int flags, continue; lock_release(rtld_bind_lock, lockstate); target = (uintptr_t)rtld_resolve_ifunc(defobj, def); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = (uintptr_t)tramp_intern(obj, &(struct tramp_data) { .target = (void *)target, .defobj = defobj, diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S index 17b699f5c533..edc8d6fb5430 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_asm.S +++ b/libexec/rtld-elf/aarch64/rtld_c18n_asm.S @@ -34,46 +34,6 @@ * See rtld_c18n.h for an overview of the design. */ -/* - * The _rtld_unw_{get,set}context_epilogue functions are stack unwinding - * helpers. See the 'Stack unwinding' section in rtld_c18n.c. - */ -ENTRY(_rtld_unw_getcontext_epilogue) - /* - * FIXME: llvm-libunwind specific ABI. This should be better specified. - */ - mov c2, csp - str c2, [c1] - RETURN -END(_rtld_unw_getcontext_epilogue) - -/* - * XXX: If compartmentalisation is not enabled, _rtld_unw_setcontext_ptr is NULL - * and we simply restore a few registers and return via retr (back to Restricted - * mode). Otherwise, call _rtld_unw_setcontext_impl via a trampoline. - */ -ENTRY(_rtld_unw_setcontext) - ldr c16, _rtld_unw_setcontext_ptr - cbnz w16, 1f - /* - * FIXME: llvm-libunwind specific ABI. This should be better specified. - */ - mov c16, c2 - ldp c2, c3, [c3, #(-0x210 + 0x20)] - mov csp, c16 -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - RETURN -#else - retr c30 -#endif -1: -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x16 -#else - br c16 -#endif -END(_rtld_unw_setcontext) - /* * The _rtld_sighandler function is the actual signal handler passed to the * kernel when the user calls sigaction. It dispatches the signal to the @@ -144,8 +104,8 @@ ENTRY(create_untrusted_stk) /* * NON-STANDARD CALLING CONVENTION * - * c19: Callee to be tail-called - * w20: Callee's compartment ID + * w19: Callee's compartment ID + * c26: Callee to be tail-called * * The function resolves the callee's stack, installs it, and tail-calls * the callee. @@ -163,7 +123,7 @@ ENTRY(create_untrusted_stk) save_arguments - mov w0, w20 + mov w0, w19 bl resolve_untrusted_stk_impl mov c10, c0 @@ -185,14 +145,14 @@ ENTRY(create_untrusted_stk) mov x18, xzr /* - * All callee-saved registers are safe except c23 + * All callee-saved registers are safe except c28 */ - mov x23, xzr + mov x28, xzr #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - br x19 + br x26 #else - brr c19 + brr c26 #endif END(create_untrusted_stk) @@ -276,85 +236,95 @@ TRAMP(tramp_push_frame) * Store the caller's current stack top in the stack lookup table. */ str c15, [STACK_TABLE_C, w12, uxtw #0] - - stp c19, c20, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 2)] - /* - * Load the target capability. - */ -1: ldr c19, #0 /* To be patched at runtime */ - /* - * Get the callee's compartment ID. - */ -2: movz w20, #0 /* To be patched at runtime */ /* * Get the length of the stack lookup table. */ gclen x13, STACK_TABLE_C + + stp c19, c20, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 2)] /* - * Use subs instead of cmp to clear a register tag. + * Get the callee's compartment ID. */ - subs x14, x13, x20 - +1: movz w19, #0 /* To be patched at runtime */ /* - * Save the callee's current stack top and old stack top. + * Use subs instead of cmp to clear a capability tag. */ - stp c15, c16, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP)] + subs x14, x13, x19 /* * If the stack lookup table index is out-of-bounds, set it to zero. */ - csel w16, w20, wzr, hi + csel w20, w19, wzr, hi /* * Load the callee's stack if the stack lookup table index is within * bounds. Otherwise the resolver will be loaded. */ - ldr c17, [STACK_TABLE_C, w16, uxtw #0] + ldr c17, [STACK_TABLE_C, w20, uxtw #0] + /* + * The tag of the return capability is set iff the condition flag is cs. + */ + chktgd c30 + /* + * Compare the return address to the landing address. The call is a + * tail-call iff the condition flag is eq. + */ + ccmp x30, x11, #0, cs stp c21, c22, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 4)] /* - * The resolver is loaded iff the condition flag is ne. + * Get the offset to the next trusted frame. */ - gcperm x21, c17 - ands x22, x21, #(1 << 15) + mov x21, #-(CAP_WIDTH * TRUSTED_FRAME_SIZE) /* - * If the resolver is loaded, keep the stack unchanged. Otherwise, - * install the callee's stack. + * If the call is a tail-call, do not bump the trusted stack pointer. */ - csel c15, c15, c17, ne + csel x22, xzr, x21, eq stp c23, c24, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 6)] /* - * If the resolver is loaded, set the branch target to it. Otherwise, - * install the callee. + * If the call is a tail-call, get the number of return value registers + * of the caller. */ - csel c23, c17, c19, ne + csinv x23, x10, xzr, eq /* - * Compare the return address to the landing address. + * Get the landing address. */ - subs x24, x30, x11 +2: adr c24, #0 /* To be patched at runtime */ stp c25, c26, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 8)] /* - * Get the tag of the return capability. + * Compute the number of return value registers. If the call is a tail- + * call, it is the minimum of that of the caller and the callee. */ - gctag x25, c30 +3: ubfm x25, x23, #48, #0 /* To be patched at runtime */ /* - * Get the offset to the next trusted frame. + * Load the target capability. + */ +4: ldr c26, #0 /* To be patched at runtime */ + + /* + * Save the caller's current stack top and old stack top. */ - mov x26, #-(CAP_WIDTH * TRUSTED_FRAME_SIZE) + stp c15, c16, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP)] /* - * The call is a tail-call iff the condition flag is eq. + * Get the permissions of the loaded value. */ - ccmp x25, #1, #0, eq + gcperm x16, c17 stp c27, c28, [TRUSTED_STACK_C, #(-CAP_WIDTH * TRUSTED_FRAME_SIZE + CAP_WIDTH * 10)] /* - * If the call is a tail call, do not bump the trusted stack pointer. + * The resolver is loaded iff the condition flag is ne. */ - csel x27, xzr, x26, eq + ands x27, x16, #(1 << 15) /* - * Get the landing address. + * If the resolver is loaded, keep the stack unchanged. Otherwise, + * install the callee's stack. */ -3: adr c28, #0 /* To be patched at runtime */ + csel c15, c15, c17, ne + /* + * If the resolver is loaded, set the branch target to it. Otherwise, + * install the callee. + */ + csel c28, c17, c26, ne /* * Save the address of the previous trusted frame and the compartment ID @@ -370,21 +340,22 @@ TRAMP(tramp_push_frame) * information about the callee regardless of whether the call is a * tail-call. */ - add TRUSTED_STACK_C, TRUSTED_STACK_C, x27 + add TRUSTED_STACK_C, TRUSTED_STACK_C, x22 /* * Save the landing address. */ - str x28, [TRUSTED_STACK_C, #TRUSTED_FRAME_LANDING] + str x24, [TRUSTED_STACK_C, #TRUSTED_FRAME_LANDING] /* - * Get the number of return value registers. + * Combine the caller's compartment ID and the number of return value + * registers. */ -4: add w28, w20, #0, lsl #12 /* To be patched at runtime */ + orr w24, w19, w25, lsl #16 /* * Save the callee's compartment ID and the number of return value * registers. */ - str w28, [TRUSTED_STACK_C, #TRUSTED_FRAME_CALLEE] + str w24, [TRUSTED_STACK_C, #TRUSTED_FRAME_CALLEE] msr TRUSTED_STACK, TRUSTED_STACK_C @@ -398,10 +369,10 @@ TRAMP(tramp_push_frame) set_untrusted_stk c15 TRAMPEND(tramp_push_frame) -PATCH_POINT(tramp_push_frame, target, 1b) -PATCH_POINT(tramp_push_frame, cid, 2b) -PATCH_POINT(tramp_push_frame, landing, 3b) -PATCH_POINT(tramp_push_frame, ret_args, 4b) +PATCH_POINT(tramp_push_frame, cid, 1b) +PATCH_POINT(tramp_push_frame, landing, 2b) +PATCH_POINT(tramp_push_frame, n_rets, 3b) +PATCH_POINT(tramp_push_frame, target, 4b) /* * Save the address of the current frame to c29 so that unwinders can locate it. @@ -417,6 +388,7 @@ TRAMPEND(tramp_update_fp_untagged) TRAMP(tramp_count_entry) 1: ldr c24, #0 /* To be patched at runtime */ + movz w25, #1 stadd w25, [c24] TRAMPEND(tramp_count_entry) @@ -440,9 +412,9 @@ PATCH_POINT(tramp_call_hook, header, 3b) TRAMP(tramp_invoke_exe) #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - blr x23 + blr x28 #else - blr c23 + blr c28 #endif TRAMPEND(tramp_invoke_exe) @@ -469,9 +441,9 @@ TRAMP(tramp_invoke_res) clrtag TRUSTED_STACK_C, TRUSTED_STACK_C #ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - blr x23 + blr x28 #else - blrr c23 + blrr c28 #endif TRAMPEND(tramp_invoke_res) @@ -526,22 +498,22 @@ TRAMP(tramp_pop_frame) str c12, [STACK_TABLE_C, w10, uxtw #0] /* - * Extract the number of return value registers. + * Extract the number of return value registers. The number of return + * value registers is encoded as follows: + * - TWO: 0b11 + * - ONE: 0b01 + * - NONE: 0b00 + * - INDIRECT: 0b00 */ - ubfx x13, x10, #48, #2 + ubfx x13, x10, #50, #2 /* - * Clear unused return value registers. The registers to clear are - * encoded as follows: - * - None: 0b00 - * - c1 only: 0b01 - * - c0 and c1: 0b1x - * Use comparison and csel to avoid branching. + * Clear unused return value registers. * - * Use subs instead of cmp to clear a register tag. + * Use subs instead of cmp to clear a capability tag. */ subs w14, w13, #0b01 - csel c0, czr, c0, hi - csel c1, czr, c1, hs + csel c0, czr, c0, lo + csel c1, czr, c1, ls /* * Clear temporary registers. diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c index 729b1b5facb8..b0b0e958065f 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.c @@ -100,10 +100,10 @@ tramp_compile(char **entry, const struct tramp_data *data) *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ } while (0) -#define PATCH_ADD(tramp, name, value) \ +#define PATCH_UBFM(tramp, name, value) \ do { \ uint32_t _value = (value); \ - _value = ((_value & 0xfff) << 10); \ + _value = ((_value & 0x3f) << 10); \ *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ } while (0) @@ -145,11 +145,18 @@ tramp_compile(char **entry, const struct tramp_data *data) size += offsetof(struct tramp_header, entry); COPY(push_frame); - PATCH_LDR_IMM(push_frame, target, target_off); PATCH_MOV(push_frame, cid, cid_to_index(data->defobj->compart_id).val); - PATCH_ADD(push_frame, ret_args, - data->sig.valid ? data->sig.ret_args << (16 - 12) : 0); landing_off = PATCH_OFF(push_frame, landing); + /* + * The number of return value registers is encoded as follows: + * - TWO: 0b1111 + * - ONE: 0b0111 + * - NONE: 0b0011 + * - INDIRECT: 0b0001 + */ + PATCH_UBFM(push_frame, n_rets, + 51 - (data->sig.valid ? data->sig.ret_args : 0)); + PATCH_LDR_IMM(push_frame, target, target_off); if (executive || ld_compartment_unwind != NULL) COPY(update_fp); @@ -206,84 +213,3 @@ tramp_compile(char **entry, const struct tramp_data *data) return (size); } - -/* - * APIs - */ -void * -_rtld_safebox_code(void *target, struct func_sig sig) -{ - const Obj_Entry *obj; - - if (!func_sig_legal(sig)) { - _rtld_error( - "_rtld_sandbox_code: Invalid signature " - C18N_SIG_FORMAT_STRING, - C18N_SIG_FORMAT(sig)); - return (NULL); - } - - if ((cheri_getperm(target) & CHERI_PERM_EXECUTIVE) != 0) - return (target); - - obj = obj_from_addr(target); - if (obj == NULL) { - _rtld_error( - "_rtld_sandbox_code: " - "%#p does not belong to any object", target); - return (NULL); - } - - if (sig.valid) { - asm ("chkssu %0, %0, %1" - : "+C" (target) - : "C" (obj->text_rodata_cap) - : "cc"); - target = cheri_seal(target, - sealer_tramp + func_sig_to_otype(sig)); - } - - return (target); -} - -void * -_rtld_sandbox_code(void *target, struct func_sig sig) -{ - const Obj_Entry *obj; - void *target_unsealed; - - if (!func_sig_legal(sig)) { - _rtld_error( - "_rtld_sandbox_code: Invalid signature " - C18N_SIG_FORMAT_STRING, - C18N_SIG_FORMAT(sig)); - return (NULL); - } - - if ((cheri_getperm(target) & CHERI_PERM_EXECUTIVE) != 0) - return (target); - - obj = obj_from_addr(target); - if (obj == NULL) { - _rtld_error( - "_rtld_sandbox_code: " - "%#p does not belong to any object", target); - return (NULL); - } - - target_unsealed = cheri_unseal(target, sealer_tramp); - if (cheri_gettag(target_unsealed)) { - if (sig.valid && cheri_gettype(target) != - (long)cheri_getbase(sealer_tramp) + func_sig_to_otype(sig)) - rtld_fatal("Signature mismatch"); - target = cheri_sealentry(target_unsealed); - } - - target = tramp_intern(NULL, &(struct tramp_data) { - .target = target, - .defobj = obj, - .sig = sig - }); - - return (target); -} diff --git a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h index 5e2607f42f08..bd1330ea55c9 100644 --- a/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h +++ b/libexec/rtld-elf/aarch64/rtld_c18n_machdep.h @@ -133,7 +133,7 @@ set_untrusted_stk(const void *sp) } #endif -struct trusted_frame { +struct compart_state { void *fp; void *pc; /* @@ -145,38 +145,6 @@ struct trusted_frame { * caller made the call. */ void *sp; - /* - * INVARIANT: This field contains the top of the caller's stack when the - * caller was last entered. - */ - void *osp; - /* - * Address of the previous trusted frame - */ - struct trusted_frame *previous; - /* - * Compartment ID of the caller - */ - stk_table_index caller; - /* - * Zeros - */ - uint16_t zeros; - /* - * Compartment ID of the callee - */ - stk_table_index callee; - /* - * Number of return value registers, encoded in enum tramp_ret_args - */ - uint8_t ret_args : 2; - uint16_t reserved : 14; - /* - * This field contains the code address in the trampoline that the - * callee should return to. This is used by trampolines to detect cross- - * compartment tail-calls. - */ - ptraddr_t landing; }; #endif #endif diff --git a/libexec/rtld-elf/aarch64/rtld_start.S b/libexec/rtld-elf/aarch64/rtld_start.S index fab9b7bff62b..4d839d39eb16 100644 --- a/libexec/rtld-elf/aarch64/rtld_start.S +++ b/libexec/rtld-elf/aarch64/rtld_start.S @@ -119,7 +119,7 @@ END(.rtld_start) * x17 = &_rtld_bind_start */ ENTRY(C18N_SYM(_rtld_bind_start)) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Get the caller's current stack top. */ @@ -205,7 +205,7 @@ ENTRY(C18N_SYM(_rtld_bind_start)) /* Restore frame pointer */ ldp PTR(29), PTR(zr), [PTRN(sp)], #(PTR_WIDTH * 2) -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Load caller's old stack top. */ @@ -246,7 +246,7 @@ ENTRY(C18N_SYM(_rtld_bind_start)) /* Call into the correct function */ #if defined(__ARM_MORELLO_PURECAP_BENCHMARK_ABI) br x16 -#elif defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#elif defined(CHERI_LIB_C18N) brr PTR(16) #else br PTR(16) diff --git a/libexec/rtld-elf/cheri/cheri_reloc.h b/libexec/rtld-elf/cheri/cheri_reloc.h index 1952eec1fd34..b432ba49dcac 100644 --- a/libexec/rtld-elf/cheri/cheri_reloc.h +++ b/libexec/rtld-elf/cheri/cheri_reloc.h @@ -35,7 +35,7 @@ #include "debug.h" #include "rtld.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -125,6 +125,14 @@ process_r_cheri_capability(Obj_Entry *obj, Elf_Word r_symndx, symname(obj, r_symndx), obj->path); return -1; } +#if defined(CHERI_LIB_C18N) && defined(__riscv) + symval = tramp_intern(NULL, &(struct tramp_data) { + .target = __DECONST(void *, symval), + .defobj = defobj, + .def = def, + .sig = sigtab_get(obj, r_symndx) + }); +#endif } else { /* Remove execute permissions and set bounds */ symval = cheri_incoffset(make_data_cap(def, defobj), addend); diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c index dae71191a316..87d042ed4eab 100644 --- a/libexec/rtld-elf/map_object.c +++ b/libexec/rtld-elf/map_object.c @@ -51,7 +51,7 @@ #include "debug.h" #include "rtld.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -278,7 +278,7 @@ map_object(int fd, const char *path, const struct stat *sb, const char* main_pat base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags); mapbase = mmap(base_addr, mapsize, PROT_NONE | PROT_MAX(_PROT_ALL), base_flags, -1, 0); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef HAS_RESTRICTED_MODE mapbase = cheri_clearperm(mapbase, c18n_code_perm_clear); #endif if (mapbase == MAP_FAILED) { diff --git a/libexec/rtld-elf/riscv/rtld_c18n_asm.S b/libexec/rtld-elf/riscv/rtld_c18n_asm.S new file mode 100644 index 000000000000..55bc6ea25d10 --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_c18n_asm.S @@ -0,0 +1,535 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Dapeng Gao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#define IN_ASM +#include "rtld_c18n_machdep.h" +#undef IN_ASM + +/* + * See rtld_c18n.h for an overview of the design. + */ + +/* + * The _rtld_sighandler function is the actual signal handler passed to the + * kernel when the user calls sigaction. It dispatches the signal to the + * appropriate handler registered by the user. + */ +ENTRY(_rtld_sighandler) + /* + * Get the interrupted compartment's current stack top. + */ + get_untrusted_stk ca3 + /* + * The function is executing on an unknown untrusted stack. Switch to + * RTLD's stack. + */ + get_rtld_stk ca4 + /* + * Allocate a sigframe on RTLD's stack. + */ + cincoffsetimm ca4, ca4, -SIG_FRAME_SIZE + set_untrusted_stk ca4 + j _rtld_sighandler_impl +END(_rtld_sighandler) + +.macro save_arguments + cincoffsetimm csp, csp, -(9 * CLEN_BYTES + 8 * 8) + + /* Save floating point arguments */ + cfsd fa0, (0 * 8)(csp) + cfsd fa1, (1 * 8)(csp) + cfsd fa2, (2 * 8)(csp) + cfsd fa3, (3 * 8)(csp) + cfsd fa4, (4 * 8)(csp) + cfsd fa5, (5 * 8)(csp) + cfsd fa6, (6 * 8)(csp) + cfsd fa7, (7 * 8)(csp) + + /* Save argument registers */ + csc ca0, (0 * CLEN_BYTES + 8 * 8)(csp) + csc ca1, (1 * CLEN_BYTES + 8 * 8)(csp) + csc ca2, (2 * CLEN_BYTES + 8 * 8)(csp) + csc ca3, (3 * CLEN_BYTES + 8 * 8)(csp) + csc ca4, (4 * CLEN_BYTES + 8 * 8)(csp) + csc ca5, (5 * CLEN_BYTES + 8 * 8)(csp) + csc ca6, (6 * CLEN_BYTES + 8 * 8)(csp) + csc ca7, (7 * CLEN_BYTES + 8 * 8)(csp) + + /* Save return address */ + csc cra, (8 * CLEN_BYTES + 8 * 8)(csp) +.endmacro + +.macro restore_arguments + /* Restore floating point arguments */ + cfsd fa0, (0 * 8)(csp) + cfsd fa1, (1 * 8)(csp) + cfsd fa2, (2 * 8)(csp) + cfsd fa3, (3 * 8)(csp) + cfsd fa4, (4 * 8)(csp) + cfsd fa5, (5 * 8)(csp) + cfsd fa6, (6 * 8)(csp) + cfsd fa7, (7 * 8)(csp) + + /* Restore argument registers */ + clc ca0, (0 * CLEN_BYTES + 8 * 8)(csp) + clc ca1, (1 * CLEN_BYTES + 8 * 8)(csp) + clc ca2, (2 * CLEN_BYTES + 8 * 8)(csp) + clc ca3, (3 * CLEN_BYTES + 8 * 8)(csp) + clc ca4, (4 * CLEN_BYTES + 8 * 8)(csp) + clc ca5, (5 * CLEN_BYTES + 8 * 8)(csp) + clc ca6, (6 * CLEN_BYTES + 8 * 8)(csp) + clc ca7, (7 * CLEN_BYTES + 8 * 8)(csp) + + /* Restore return address */ + clc cra, (8 * CLEN_BYTES + 8 * 8)(csp) + + cincoffsetimm csp, csp, (9 * CLEN_BYTES + 8 * 8) +.endmacro + +/* + * The create_untrusted_stack function has non-standard ABI and is only called + * by trampolines when the destination compartment's stack has not been + * allocated yet. + */ +ENTRY(create_untrusted_stk) + /* + * NON-STANDARD CALLING CONVENTION + * + * s8: Callee to be tail-called + * s9: Callee's compartment ID + * + * The function resolves the callee's stack, installs it, and tail-calls + * the callee. + * + * All argument registers must be preserved. All temporary registers and + * callee-saved registers must be cleared. + */ + + /* + * The execution stack is still the caller's stack. Switch to RTLD's + * stack. + */ + get_rtld_stk ct0 + set_untrusted_stk ct0 + + save_arguments + + move a0, s9 + cjal resolve_untrusted_stk_impl + cmove ct0, ca0 + + restore_arguments + + set_untrusted_stk ct0 + + /* + * Clear temporary registers, except: + * t0: Callee's stack + */ + move t1, x0 + move t2, x0 + move t3, x0 + move t4, x0 + move t5, x0 + move t6, x0 + + /* + * All callee-saved registers are safe except s4 + */ + move s4, x0 + + cjr cs8 +END(create_untrusted_stk) + +/* + * The trampoline templates are assembly code sequences used to construct + * trampolines by tramp_compile. They are code but reside in rodata. Hence a new + * macro is defined to describe them. + */ +#define TRAMP(sym) \ + .section .rodata; .globl sym; .type sym,%object; sym: + +#define TRAMPEND(sym) \ + end_##sym: \ + EEND(sym); \ + .section .rodata; .globl size_##sym; .align 3; \ + .type size_##sym,%object; .size size_##sym, 8; size_##sym: \ + .quad end_##sym - sym + +#define PATCH_POINT(tramp, name, label) \ + .section .rodata; .globl patch_##tramp##_##name; .align 2; \ + .type patch_##tramp##_##name,%object; \ + .size patch_##tramp##_##name, 4; patch_##tramp##_##name: \ + .word label - end_##tramp + +#define TRUSTED_STACK_C ct3 + +TRAMP(tramp_push_frame) +1: auipcc ct0, 0 + /* + * Get the unsealer for the thread identifier buffer. + */ +.option push +.option norvc +2: clc ct1, 0(ct0) /* To be patched at runtime */ +.option pop + /* + * Get and unseal the thread identifier buffer. + */ + cspecialr ct2, TIDC + cunseal ct1, ct2, ct1 + /* + * Get the trusted stack. + */ + clc TRUSTED_STACK_C, TRUSTED_STACK(ct1) + /* + * Load the caller's compartment ID from the previous trusted frame. + */ + clhu t4, TRUSTED_FRAME_CALLEE(TRUSTED_STACK_C) + /* + * Get the stack lookup table. + */ + clc STACK_TABLE_C, STACK_TABLE(ct1) + /* + * Load the caller's old stack top from the stack lookup table. + */ + csetoffset STACK_TABLE_C, STACK_TABLE_C, t4 + clc ct5, 0(STACK_TABLE_C) + /* + * Store the caller's current stack top in the stack lookup table. + */ + csc csp, 0(STACK_TABLE_C) + + csc cs11, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 12)(TRUSTED_STACK_C) + /* + * Get the length of the stack lookup table. + */ + cgetlen s11, STACK_TABLE_C + + csc cs10, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 11)(TRUSTED_STACK_C) + /* + * Get the callee's compartment ID. + */ +.option push +.option norvc +3: addi s10, x0, 0 /* To be patched at runtime */ +.option pop + csc cs9, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 10)(TRUSTED_STACK_C) + addi s9, s10, 0 + /* + * Compare the compartment ID to the length of the stack lookup table. + * If the stack lookup table index is out-of-bounds, set it to zero. + */ + bltu s10, s11, 4f + addi s10, x0, 0 +4: + /* + * Load the callee's stack if the stack lookup table index is within + * bounds. Otherwise the resolver will be loaded. + */ + csetoffset STACK_TABLE_C, STACK_TABLE_C, s10 + clc ct6, 0(STACK_TABLE_C) + + /* + * Load the target capability. + */ + csc cs8, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 9)(TRUSTED_STACK_C) +.option push +.option norvc +5: clc cs8, 0(ct0) /* To be patched at runtime */ +.option pop + /* + * Get the permissions of the loaded value. + */ + csc cs7, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 8)(TRUSTED_STACK_C) + cgetperm s7, ct6 + /* + * The resolver is loaded iff the value is non-zero. + */ + csc cs6, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 7)(TRUSTED_STACK_C) + andi s6, s7, (1 << 1) + /* + * If the resolver is loaded, keep the stack unchanged. Otherwise, + * install the callee's stack. + */ + csc cs5, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 6)(TRUSTED_STACK_C) + cmove cs5, ct6 + /* + * If the resolver is loaded, set the branch target to it. Otherwise, + * install the callee. + */ + csc cs4, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 5)(TRUSTED_STACK_C) + cmove cs4, cs8 + beqz s6, 6f + cmove cs5, csp + cmove cs4, ct6 +6: + + csc cs3, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 4)(TRUSTED_STACK_C) + /* + * Load the landing address from the previous trusted frame. + */ + cld s3, TRUSTED_FRAME_LANDING(TRUSTED_STACK_C) + csc cs2, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 3)(TRUSTED_STACK_C) + /* + * Load the existing number of return value registers. + */ + clbu s2, (TRUSTED_FRAME_CALLEE + 2)(TRUSTED_STACK_C) + /* + * Get the tag of the return capability. + */ + csc cs1, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 2)(TRUSTED_STACK_C) + cgettag s1, cra + + csc cra, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 1)(TRUSTED_STACK_C) + csc cs0, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + CLEN_BYTES * 0)(TRUSTED_STACK_C) + + /* + * Save the caller's current stack top and old stack top. + */ + csc csp, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP)(TRUSTED_STACK_C) + csc ct5, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_SP_OSP + CLEN_BYTES)(TRUSTED_STACK_C) + /* + * Save the address of the previous trusted frame. + */ + csc TRUSTED_STACK_C, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_PREV)(TRUSTED_STACK_C) + /* + * Save the the compartment ID of the caller. + */ + csw t4, (-CLEN_BYTES * TRUSTED_FRAME_SIZE + TRUSTED_FRAME_CALLER)(TRUSTED_STACK_C) + + /* + * Get the landing address. + */ +.option push +.option norvc +7: addi t0, t0, 0 /* To be patched at runtime */ +.option pop + + /* + * Compare the return address to the landing address. + * + * Bump the trusted stack pointer if the call is not a tail-call. The + * callee-related portion of the trsuted stack must be written after + * this so that the topmost trusted frame contains the correct + * information about the callee regardless of whether the call is a + * tail-call. + */ + bne ra, s3, 8f + bnez s1, 9f +8: + cincoffsetimm TRUSTED_STACK_C, TRUSTED_STACK_C, (-CLEN_BYTES * TRUSTED_FRAME_SIZE) + csc TRUSTED_STACK_C, TRUSTED_STACK(ct1) + addi s2, x0, 0 +9: + + /* + * Save the callee's compartment ID. + */ + csh s9, TRUSTED_FRAME_CALLEE(TRUSTED_STACK_C) + /* + * Compute and save the number of return value registers. + */ +.option push +.option norvc +10: ori t1, s2, 0 /* To be patched at runtime */ +.option pop + csb t1, (TRUSTED_FRAME_CALLEE + 2)(TRUSTED_STACK_C) + /* + * Save the landing address. + */ + csd t0, TRUSTED_FRAME_LANDING(TRUSTED_STACK_C) + + /* + * Install the callee's stack. + * + * Note: If an interrupt occurs between this instruction and the + * previous store instruction, the callee as identified by the topmost + * trusted frame would be inconsistent with the untrusted stack. + */ + set_untrusted_stk cs5 +TRAMPEND(tramp_push_frame) + +PATCH_POINT(tramp_push_frame, pcc, 1b) +PATCH_POINT(tramp_push_frame, unsealer, 2b) +PATCH_POINT(tramp_push_frame, cid, 3b) +PATCH_POINT(tramp_push_frame, target, 5b) +PATCH_POINT(tramp_push_frame, landing, 7b) +PATCH_POINT(tramp_push_frame, n_rets, 10b) + +/* + * Save the address of the current frame to fp so that unwinders can locate it. + * When transitioning to Restricted mode code, its tag must be cleared. + */ +TRAMP(tramp_update_fp) + cmove cs0, TRUSTED_STACK_C +TRAMPEND(tramp_update_fp) + +TRAMP(tramp_update_fp_untagged) + ccleartag cs0, TRUSTED_STACK_C +TRAMPEND(tramp_update_fp_untagged) + +TRAMP(tramp_clear_args_a67) +/* xx */ +1: cclear 2, 0b00000000 /* To be patched at runtime */ +TRAMPEND(tramp_clear_args_a67) + +PATCH_POINT(tramp_clear_args_a67, a67, 1b) + +TRAMP(tramp_clear_args_a05) +/* xxxxxx */ +1: cclear 1, 0b00000000 /* To be patched at runtime */ +TRAMPEND(tramp_clear_args_a05) + +PATCH_POINT(tramp_clear_args_a05, a05, 1b) + +TRAMP(tramp_clear_args) + /* + * Each instruction here is 2 bytes long. + */ + addi a7, x0, 0 + addi a6, x0, 0 + addi a5, x0, 0 + addi a4, x0, 0 + addi a3, x0, 0 + addi a2, x0, 0 + addi a1, x0, 0 + addi a0, x0, 0 +TRAMPEND(tramp_clear_args) + +TRAMP(tramp_invoke) + ccleartag TRUSTED_STACK_C, TRUSTED_STACK_C + cjalr cra, cs4 +TRAMPEND(tramp_invoke) + +TRAMP(tramp_pop_frame) +1: auipcc ct4, 0 + /* + * Get the unsealer for the thread identifier buffer. + */ +.option push +.option norvc +2: clc ct4, 0(ct4) /* To be patched at runtime */ +.option pop + /* + * Get and unseal the thread identifier buffer. + */ + cspecialr ct0, TIDC + cunseal ct4, ct0, ct4 + + /* + * Get the trusted stack. + */ + clc TRUSTED_STACK_C, TRUSTED_STACK(ct4) + /* + * Load the address of the previous trusted frame, the compartment ID of + * the caller, and the compartment ID of the caller. + */ + clc ct5, TRUSTED_FRAME_PREV(TRUSTED_STACK_C) + clhu t1, TRUSTED_FRAME_CALLER(TRUSTED_STACK_C) + clbu t2, (TRUSTED_FRAME_CALLEE + 2)(TRUSTED_STACK_C) + /* + * Load the caller's current stack top and old stack top. + */ + clc ca7, (TRUSTED_FRAME_SP_OSP)(TRUSTED_STACK_C) + clc ca6, (TRUSTED_FRAME_SP_OSP + CLEN_BYTES)(TRUSTED_STACK_C) + + /* + * Get the stack lookup table. + */ + clc STACK_TABLE_C, STACK_TABLE(ct4) + + /* + * Restore callee-saved registers. + */ + clc cs0, (CLEN_BYTES * 0)(TRUSTED_STACK_C) + clc cra, (CLEN_BYTES * 1)(TRUSTED_STACK_C) + clc cs1, (CLEN_BYTES * 2)(TRUSTED_STACK_C) + clc cs2, (CLEN_BYTES * 3)(TRUSTED_STACK_C) + clc cs3, (CLEN_BYTES * 4)(TRUSTED_STACK_C) + clc cs4, (CLEN_BYTES * 5)(TRUSTED_STACK_C) + clc cs5, (CLEN_BYTES * 6)(TRUSTED_STACK_C) + clc cs6, (CLEN_BYTES * 7)(TRUSTED_STACK_C) + clc cs7, (CLEN_BYTES * 8)(TRUSTED_STACK_C) + clc cs8, (CLEN_BYTES * 9)(TRUSTED_STACK_C) + clc cs9, (CLEN_BYTES * 10)(TRUSTED_STACK_C) + clc cs10, (CLEN_BYTES * 11)(TRUSTED_STACK_C) + clc cs11, (CLEN_BYTES * 12)(TRUSTED_STACK_C) + + /* + * Install the caller's stack. + * + * Note: If an interrupt occurs between this instruction and the next, + * the callee as identified by the topmost trusted frame would be + * inconsistent with the untrusted stack. + */ + set_untrusted_stk ca7 + /* + * Bump the trusted stack pointer. + */ + csc ct5, TRUSTED_STACK(ct4) + + /* + * Store the caller's old stack top in the stack lookup table. + */ + csetoffset STACK_TABLE_C, STACK_TABLE_C, t1 + csc ca6, 0(STACK_TABLE_C) + + /* + * Extract the number of return value registers. The number of return + * value registers is encoded as follows: + * - TWO: 0b00 + * - ONE: 0b01 + * - NONE: 0b10 + * - INDIRECT: 0b11 + */ + beqz t2, 3f + addi a1, x0, 1 + beq t2, a1, 3f + addi a0, x0, 0 +3: + + /* + * Clear temporary registers. + * cclear 1, 0b11110000 a2 to a5 + * cclear 3, 0b11110000 t3 to t6 + */ + addi a2, x0, 0 + addi a3, x0, 0 + addi a4, x0, 0 + addi a5, x0, 0 + addi t3, x0, 0 + addi t4, x0, 0 + addi t5, x0, 0 + addi t6, x0, 0 + + cret +TRAMPEND(tramp_pop_frame) + +PATCH_POINT(tramp_pop_frame, pcc, 1b) +PATCH_POINT(tramp_pop_frame, unsealer, 2b) diff --git a/libexec/rtld-elf/riscv/rtld_c18n_machdep.c b/libexec/rtld-elf/riscv/rtld_c18n_machdep.c new file mode 100644 index 000000000000..762ca09e674f --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_c18n_machdep.c @@ -0,0 +1,156 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Dapeng Gao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include + +#include + +#include "debug.h" +#include "rtld.h" +#include "rtld_c18n.h" +#include "rtld_libc.h" + +/* + * Trampolines + */ +size_t +tramp_compile(char **entry, const struct tramp_data *data) +{ +#define IMPORT(template) \ + extern const uint32_t tramp_##template[]; \ + extern const size_t size_tramp_##template + + IMPORT(push_frame); + IMPORT(update_fp); + IMPORT(update_fp_untagged); + IMPORT(clear_args); + IMPORT(invoke); + IMPORT(pop_frame); + + size_t size = 0; + char *buf = *entry; + size_t sealer_off, target_off, pcc_off, landing_off, unused_regs; + +#define COPY_VALUE(val) ({ \ + size_t _old_size = size; \ + *(typeof(val) *)(buf + size) = val; \ + size += sizeof(val); \ + _old_size; \ +}) + +#define COPY(template) \ + do { \ + memcpy(buf + size, tramp_##template, \ + size_tramp_##template); \ + size += size_tramp_##template; \ + } while(0) + +#define PATCH_INS(offset) ((uint32_t *)(buf + (offset))) \ + +#define PATCH_OFF(tramp, name) ({ \ + extern const int32_t patch_tramp_##tramp##_##name; \ + size + patch_tramp_##tramp##_##name; \ + }) + +#define PATCH_I_TYPE(tramp, name, value) \ + do { \ + uint32_t _value = (value); \ + _value = ((_value & 0xfff) << 20); \ + *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ + } while (0) + +#define PATCH_LANDING(landing_off, value) \ + do { \ + uint32_t _value = (value); \ + _value = ((_value & 0xfff) << 20); \ + *PATCH_INS(landing_off) |= _value; \ + } while (0) + +#define PATCH_CClear(tramp, name, value) \ + do { \ + uint32_t _value = (value); \ + _value = ((_value & 0x1f) << 7) | \ + ((_value & 0xe0) << 10); \ + *PATCH_INS(PATCH_OFF(tramp, name)) |= _value; \ + } while (0) + + sealer_off = COPY_VALUE(sealer_tidc); + + *(struct tramp_header *)(buf + size) = (struct tramp_header) { + .target = data->target, + .defobj = data->defobj, + .symnum = data->def == NULL ? + 0 : data->def - data->defobj->symtab, + .sig = data->sig + }; + target_off = size + offsetof(struct tramp_header, target); + *entry = buf + size; + size += offsetof(struct tramp_header, entry); + + COPY(push_frame); + pcc_off = PATCH_OFF(push_frame, pcc); + PATCH_I_TYPE(push_frame, unsealer, sealer_off - pcc_off); + PATCH_I_TYPE(push_frame, cid, + cid_to_index(data->defobj->compart_id).val); + PATCH_I_TYPE(push_frame, target, target_off - pcc_off); + landing_off = PATCH_OFF(push_frame, landing); + /* + * The number of return value registers is encoded as follows: + * - TWO: 0b00 + * - ONE: 0b01 + * - NONE: 0b10 + * - INDIRECT: 0b11 + */ + PATCH_I_TYPE(push_frame, n_rets, + data->sig.valid ? data->sig.ret_args : 0); + + if (ld_compartment_unwind != NULL) + COPY(update_fp); + else + COPY(update_fp_untagged); + + if (data->sig.valid) { + /* Each instruction here is 2 bytes long. */ + unused_regs = (8 - data->sig.reg_args) * + sizeof(*tramp_clear_args) / 2; + memcpy(buf + size, tramp_clear_args, unused_regs); + size += unused_regs; + } + COPY(invoke); + PATCH_LANDING(landing_off, size - pcc_off); + + COPY(pop_frame); + pcc_off = PATCH_OFF(pop_frame, pcc); + PATCH_I_TYPE(pop_frame, unsealer, sealer_off - pcc_off); + + return (size); +} diff --git a/libexec/rtld-elf/riscv/rtld_c18n_machdep.h b/libexec/rtld-elf/riscv/rtld_c18n_machdep.h new file mode 100644 index 000000000000..f5ce245c961d --- /dev/null +++ b/libexec/rtld-elf/riscv/rtld_c18n_machdep.h @@ -0,0 +1,140 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2024 Dapeng Gao + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef RTLD_C18N_MACHDEP_H +#define RTLD_C18N_MACHDEP_H + +#include +#include + +#define TRUSTED_FRAME_SIZE 17 +#define TRUSTED_FRAME_SP_OSP (16 * 13) +#define TRUSTED_FRAME_PREV (16 * 15) +#define TRUSTED_FRAME_CALLER (16 * 16) +#define TRUSTED_FRAME_CALLEE (16 * 16 + 4) +#define TRUSTED_FRAME_LANDING (16 * 16 + 8) + +#define SIG_FRAME_SIZE 1376 + +#define UNTRUSTED_STACK csp +#define TIDC 3 +#define TRUSTED_STACK 0 +#define STACK_TABLE CLEN_BYTES +#define STACK_TABLE_N 6 +#define STACK_TABLE_C __CONCAT(ct, STACK_TABLE_N) +#define STACK_TABLE_RTLD 32 + +#ifdef IN_ASM + +.macro get_untrusted_stk reg + cmove \reg, UNTRUSTED_STACK +.endmacro + +.macro set_untrusted_stk reg + cmove UNTRUSTED_STACK, \reg +.endmacro + +.macro get_rtld_stk reg + clgc \reg, sealer_tidc + clc \reg, 0(\reg) + cspecialr STACK_TABLE_C, TIDC + cunseal STACK_TABLE_C, STACK_TABLE_C, \reg + clc STACK_TABLE_C, STACK_TABLE(STACK_TABLE_C) + clc \reg, STACK_TABLE_RTLD(STACK_TABLE_C) +.endmacro + +#else + +static inline void * +get_trusted_tp(void) +{ + void *ptr; + + asm volatile ("cmove %0, ctp" : "=C" (ptr)); + return (ptr); +} + +struct tidc { + struct trusted_frame *trusted_stk; + struct stk_table *table; +}; + +static inline struct stk_table * +get_stk_table(void) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + return (tidc->table); +} + +static inline void +set_stk_table(struct stk_table *table) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + tidc->table = table; +} + +static inline struct trusted_frame * +get_trusted_stk(void) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + return (tidc->trusted_stk); +} + +static inline void +set_trusted_stk(struct trusted_frame *tf) +{ + struct tidc *tidc; + + asm volatile ("cspecialr %0, " __XSTRING(TIDC) : "=C" (tidc)); + tidc = cheri_unseal(tidc, sealer_tidc); + tidc->trusted_stk = tf; +} + +struct compart_state { + void *fp; /* s0 */ + void *pc; + /* + * s1 to s11 + */ + void *regs[11]; + /* + * INVARIANT: This field contains the top of the caller's stack when the + * caller made the call. + */ + void *sp; +}; +#endif +#endif diff --git a/libexec/rtld-elf/riscv/rtld_machdep.h b/libexec/rtld-elf/riscv/rtld_machdep.h index aefc352efa24..4a403985212c 100644 --- a/libexec/rtld-elf/riscv/rtld_machdep.h +++ b/libexec/rtld-elf/riscv/rtld_machdep.h @@ -154,11 +154,29 @@ make_data_cap(const Elf_Sym *def, const struct Struct_Obj_Entry *defobj) #define call_init_pointer(obj, target) rtld_fatal("%s: _init or _fini used!", obj->path) /* TODO: Per-function captable/PLT/FNDESC support (needs CGP) */ +#ifdef CHERI_LIB_C18N +#define call_init_array_pointer(_obj, _target) \ + (((InitArrFunc)tramp_intern(NULL, &(struct tramp_data) { \ + .target = (void *)(_target).value, \ + .defobj = _obj, \ + .sig = (struct func_sig) { .valid = true, \ + .reg_args = 3, .mem_args = false, .ret_args = NONE }\ + }))(main_argc, main_argv, environ)) + +#define call_fini_array_pointer(_obj, _target) \ + (((InitFunc)tramp_intern(NULL, &(struct tramp_data) { \ + .target = (void *)(_target).value, \ + .defobj = _obj, \ + .sig = (struct func_sig) { .valid = true, \ + .reg_args = 0, .mem_args = false, .ret_args = NONE }\ + }))()) +#else #define call_init_array_pointer(obj, target) \ (((InitArrFunc)(target).value)(main_argc, main_argv, environ)) #define call_fini_array_pointer(obj, target) \ (((InitFunc)(target).value)()) +#endif #else /* __CHERI_PURE_CAPABILITY__ */ diff --git a/libexec/rtld-elf/riscv/rtld_start.S b/libexec/rtld-elf/riscv/rtld_start.S index e220a158f695..5a2c03750997 100644 --- a/libexec/rtld-elf/riscv/rtld_start.S +++ b/libexec/rtld-elf/riscv/rtld_start.S @@ -55,6 +55,8 @@ * _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) */ +#ifndef C18N_VARIANT + ENTRY(.rtld_start) #ifdef __CHERI_PURE_CAPABILITY__ cmove cs0, ca0 /* Put aux in a callee-saved register */ @@ -226,3 +228,5 @@ ENTRY(_rtld_bind_start) JR_PTR PTR(t0) #endif /* defined(__CHERI_PURE_CAPABILITY__) */ END(_rtld_bind_start) + +#endif /* C18N_VARIANT */ diff --git a/libexec/rtld-elf/rtld-libc/rtld_libc.h b/libexec/rtld-elf/rtld-libc/rtld_libc.h index 2132769c1d3e..7b1891aedf30 100644 --- a/libexec/rtld-elf/rtld-libc/rtld_libc.h +++ b/libexec/rtld-elf/rtld-libc/rtld_libc.h @@ -51,7 +51,7 @@ int __sys___getcwd(char *, size_t); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_thr_kill(long, int); int __sys_thr_self(long *); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N void __sys_thr_exit(long *); struct sigaction; int __sys_sigaction(int, const struct sigaction *, struct sigaction *); diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index f831e683f651..2920b46e0879 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -68,7 +68,7 @@ #include "rtld_utrace.h" #include "notes.h" #include "rtld_libc.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -396,7 +396,7 @@ enum { LD_SHOW_AUXV, LD_STATIC_TLS_EXTRA, LD_SKIP_INIT_FUNCS, -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N LD_UTRACE_COMPARTMENT, LD_COMPARTMENT_ENABLE, LD_COMPARTMENT_DISABLE, @@ -446,7 +446,7 @@ static struct ld_env_var_desc ld_env_vars[] = { LD_ENV_DESC(SHOW_AUXV, false), LD_ENV_DESC(STATIC_TLS_EXTRA, false), LD_ENV_DESC(SKIP_INIT_FUNCS, true), -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N LD_ENV_DESC(UTRACE_COMPARTMENT, false), LD_ENV_DESC(COMPARTMENT_ENABLE, false), LD_ENV_DESC(COMPARTMENT_DISABLE, false), @@ -682,7 +682,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (aux_info[AT_BSDFLAGS] != NULL) { if ((aux_info[AT_BSDFLAGS]->a_un.a_val & ELF_BSDF_SIGFASTBLK) != 0) ld_fast_sigblock = true; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if ((aux_info[AT_BSDFLAGS]->a_un.a_val & ELF_BSDF_CHERI_C18N) != 0) ld_compartment_enable = true; #endif @@ -854,7 +854,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) if (!ld_tracing) ld_tracing = ld_get_env_var(LD_TRACE_LOADED_OBJECTS); ld_utrace = ld_get_env_var(LD_UTRACE); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N ld_compartment_utrace = ld_get_env_var(LD_UTRACE_COMPARTMENT); ld_compartment_policy = ld_get_env_var(LD_COMPARTMENT_POLICY); ld_compartment_overhead = ld_get_env_var(LD_COMPARTMENT_OVERHEAD); @@ -873,7 +873,9 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) ld_elf_hints_default = _PATH_ELF_HINTS_C18N; ld_path_libmap_conf = _PATH_LIBMAP_CONF_C18N; ld_standard_library_path = STANDARD_LIBRARY_PATH_C18N; +#ifdef HAS_RESTRICTED_MODE c18n_code_perm_clear = CHERI_PERM_EXECUTIVE; +#endif } #endif @@ -921,7 +923,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); assert(aux_info[AT_ENTRY] != NULL); imgentry = (dlfunc_t) aux_info[AT_ENTRY]->a_un.a_ptr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef HAS_RESTRICTED_MODE imgentry = (dlfunc_t) cheri_clearperm(imgentry, c18n_code_perm_clear); #endif dbg("Values from kernel:\n\tAT_PHDR=" PTR_FMT "\n" @@ -988,7 +990,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) linkmap_add(obj_main); linkmap_add(&obj_rtld); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) { c18n_init(&obj_rtld, aux_info); @@ -1064,9 +1066,9 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) exit (0); } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) - c18n_init2(&obj_rtld); + c18n_init2(); #endif /* @@ -1183,7 +1185,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) /* Return the exit procedure and the program entry point. */ if (rtld_exit_ptr == NULL) { rtld_exit_ptr = make_rtld_function_pointer(rtld_exit); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { .target = rtld_exit_ptr, .defobj = &obj_rtld, @@ -1197,7 +1199,7 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) *exit_proc = rtld_exit_ptr; *objp = obj_main; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N return ((func_ptr_type)tramp_intern(NULL, &(struct tramp_data) { .target = cheri_sealentry(obj_main->entry), .defobj = obj_main, @@ -1218,7 +1220,7 @@ rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) uintptr_t target; ptr = (void *)make_function_pointer(def, obj); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N ptr = tramp_intern(NULL, &(struct tramp_data) { .target = ptr, .defobj = obj, @@ -1240,7 +1242,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) uintptr_t *where; uintptr_t target; RtldLockState lockstate; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct trusted_frame *tf; if (C18N_ENABLED) { @@ -1295,7 +1297,7 @@ _rtld_bind(Obj_Entry *obj, Elf_Size reloff) */ target = reloc_jmpslot(where, target, defobj, obj, rel); lock_release(rtld_bind_lock, &lockstate); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) tf = pop_dummy_rtld_trusted_frame(tf); #endif @@ -1572,7 +1574,7 @@ digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, assert(dynp->d_un.d_val == sizeof(Elf_Sym)); break; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N case DT_CHERI_C18N_SIG: if (ld_compartment_sig != NULL) obj->sigtab = (const struct func_sig *) @@ -3386,7 +3388,7 @@ Obj_Entry * obj_from_addr(const void *addr) { Obj_Entry *obj; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct tramp_header *header; if (C18N_ENABLED) { @@ -3555,7 +3557,7 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) lock_release(rtld_bind_lock, lockstate); if (reg != NULL) { func_ptr_type exit_ptr = make_rtld_function_pointer(rtld_exit); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N exit_ptr = tramp_intern(NULL, &(struct tramp_data) { .target = exit_ptr, .defobj = &obj_rtld, @@ -3568,7 +3570,7 @@ objlist_call_init(Objlist *list, RtldLockState *lockstate) dbg("Calling __libc_atexit(rtld_exit (" PTR_FMT "))", (void*)exit_ptr); reg(exit_ptr); rtld_exit_ptr = make_rtld_function_pointer(rtld_nop_exit); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N rtld_exit_ptr = tramp_intern(NULL, &(struct tramp_data) { .target = rtld_exit_ptr, .defobj = &obj_rtld, @@ -4497,9 +4499,23 @@ do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, if (ELF_ST_TYPE(def->st_info) == STT_FUNC) { sym = __DECONST(void*, make_function_pointer(def, defobj)); dbg("dlsym(%s) is function: " PTR_FMT, name, sym); +#if defined(CHERI_LIB_C18N) && defined(__riscv) + sym = tramp_intern(NULL, &(struct tramp_data) { + .target = sym, + .defobj = defobj, + .def = def + }); +#endif } else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { sym = rtld_resolve_ifunc(defobj, def); dbg("dlsym(%s) is ifunc. Resolved to: " PTR_FMT, name, sym); +#if defined(CHERI_LIB_C18N) && defined(__riscv) + sym = tramp_intern(NULL, &(struct tramp_data) { + .target = sym, + .defobj = defobj, + .def = def + }); +#endif } else if (ELF_ST_TYPE(def->st_info) == STT_TLS) { ti.ti_module = defobj->tlsindex; ti.ti_offset = def->st_value; @@ -4534,7 +4550,7 @@ dlsym(void *handle, const char *name) { void *retaddr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); @@ -4552,7 +4568,7 @@ dlfunc(void *handle, const char *name) } rv; void *retaddr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); @@ -4573,7 +4589,7 @@ dlvsym(void *handle, const char *name, const char *version) ventry.hash = elf_hash(version); ventry.flags= 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); @@ -4685,7 +4701,7 @@ dlinfo(void *handle, int request, void *p) if (handle == NULL || handle == RTLD_SELF) { void *retaddr; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N retaddr = c18n_return_address(); #else retaddr = __builtin_return_address(0); /* __GNUC__ only */ @@ -4759,7 +4775,7 @@ dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) init_marker(&marker); error = 0; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N callback = tramp_intern(NULL, &(struct tramp_data) { .target = callback, .defobj = obj_from_addr(callback), @@ -5070,7 +5086,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) return (NULL); if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC) { void *target = make_function_pointer(req.sym_out, req.defobj_out); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = tramp_intern(NULL, &(struct tramp_data) { .target = target, .defobj = req.defobj_out, @@ -5080,7 +5096,7 @@ get_program_var_addr(const char *name, RtldLockState *lockstate) return ((const void **)target); } else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC) { void *target = rtld_resolve_ifunc(req.defobj_out, req.sym_out); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N target = tramp_intern(NULL, &(struct tramp_data) { .target = target, .defobj = req.defobj_out, @@ -5817,14 +5833,14 @@ tls_get_addr_common(uintptr_t **dtvp, int index, size_t offset) dtv[index + 1] != 0)) return ((void *)(dtv[index + 1] + offset)); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct trusted_frame *tf; if (C18N_ENABLED) tf = push_dummy_rtld_trusted_frame(get_trusted_stk()); #endif ret = tls_get_addr_slow(dtvp, index, offset, false); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) tf = pop_dummy_rtld_trusted_frame(tf); #endif @@ -6188,7 +6204,7 @@ _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) wlock_acquire(rtld_bind_lock, &lockstate); ret = allocate_tls(globallist_curr(TAILQ_FIRST(&obj_list)), oldtls, tcbsize, tcbalign); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N /* * Create a fake wrapper TCB containing pointers to the real TCB and stack * lookup table. This is passed to the new thread as its initial TCB. Once @@ -6208,7 +6224,7 @@ _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) RtldLockState lockstate; wlock_acquire(rtld_bind_lock, &lockstate); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED) c18n_free_tcb(); #endif @@ -6228,7 +6244,7 @@ object_add_name(Obj_Entry *obj, const char *name) if (entry != NULL) { strcpy(entry->name, name); STAILQ_INSERT_TAIL(&obj->names, entry, link); -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N if (C18N_ENABLED && obj->compart_id == 0) obj->compart_id = compart_id_allocate(entry->name); #endif diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h index 06e29c5a93d2..32790aa00ec2 100644 --- a/libexec/rtld-elf/rtld.h +++ b/libexec/rtld-elf/rtld.h @@ -277,7 +277,7 @@ typedef struct Struct_Obj_Entry { Ver_Entry *vertab; /* Versions required /defined by this object */ int vernum; /* Number of entries in vertab */ -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N uint16_t compart_id; const struct func_sig *sigtab; #endif diff --git a/libexec/rtld-elf/rtld_c18n.c b/libexec/rtld-elf/rtld_c18n.c index 99395627a166..0e744e99bc9e 100644 --- a/libexec/rtld-elf/rtld_c18n.c +++ b/libexec/rtld-elf/rtld_c18n.c @@ -69,6 +69,9 @@ #include #include +#ifdef CHERI_LIB_C18N +#include +#endif #include @@ -87,7 +90,7 @@ _Static_assert( TRUSTED_FRAME_SIZE * sizeof(uintptr_t) == sizeof(struct trusted_frame), "Unexpected struct trusted_frame size"); _Static_assert( - TRUSTED_FRAME_SP_OSP == offsetof(struct trusted_frame, sp), + TRUSTED_FRAME_SP_OSP == offsetof(struct trusted_frame, state.sp), "Unexpected struct trusted_frame member offset"); _Static_assert( TRUSTED_FRAME_PREV == offsetof(struct trusted_frame, previous), @@ -110,23 +113,26 @@ _Static_assert(sizeof(struct func_sig) == sizeof(func_sig_int), "Unexpected func_sig size"); _Static_assert( - SIG_FRAME_SIZE == sizeof(struct sigframe), + SIG_FRAME_SIZE <= sizeof(struct sigframe), "Unexpected struct sigframe size"); /* * Sealers for RTLD privileged information */ static uintptr_t sealer_tcb; -static uintptr_t sealer_jmpbuf; -static uintptr_t sealer_unwbuf; +static uintptr_t sealer_trusted_stk; uintptr_t sealer_pltgot, sealer_tramp; -/* Enable compartmentalisation */ -bool ld_compartment_enable; - +#ifdef HAS_RESTRICTED_MODE /* Permission bit to be cleared for user code */ uint64_t c18n_code_perm_clear; +#else +uintptr_t sealer_tidc; +#endif + +/* Enable compartmentalisation */ +bool ld_compartment_enable; /* Use utrace() to log compartmentalisation-related events */ const char *ld_compartment_utrace; @@ -337,6 +343,17 @@ compart_id_allocate(const char *lib) if (string_base_search(&comparts.data[i].libs, lib) != -1) return (i); +#ifdef __riscv + /* + * XXX: All compartments have the same compartment ID on CHERI-RISC-V to + * work around the lack of bounded memory and variadic arguments. + */ + lib = "[grand]"; + for (i = RTLD_COMPART_ID; i < comparts.size; ++i) + if (string_base_search(&comparts.data[i].libs, lib) != -1) + return (i); +#endif + com = add_comparts_data(lib); string_base_push(&com->libs, lib); @@ -565,8 +582,14 @@ tramp_should_include(const Obj_Entry *reqobj, const struct tramp_data *data) if (reqobj == NULL) return (true); +#ifndef __riscv + /* + * XXX: All compartments have the same compartment ID on CHERI-RISC-V to + * work around the lack of bounded memory and variadic arguments. + */ if (reqobj->compart_id == data->defobj->compart_id) return (false); +#endif if (string_base_search(&comparts.data[reqobj->compart_id].trusts, sym) != -1) @@ -598,6 +621,9 @@ struct tcb_wrapper { struct tcb header __attribute__((cheri_no_subobject_bounds)); struct tcb *tcb; struct stk_table *table; +#ifndef HAS_RESTRICTED_MODE + struct tidc tidc; +#endif }; static void @@ -724,6 +750,9 @@ init_stk_table(struct stk_table *table, struct tcb_wrapper *wrap) char *sp; size_t size; struct trusted_frame *tf; +#ifndef HAS_RESTRICTED_MODE + struct tidc *tidc; +#endif /* * Save the fake tcb in the stack lookup table. @@ -747,7 +776,7 @@ init_stk_table(struct stk_table *table, struct tcb_wrapper *wrap) /* * Record RTLD's stack in the stack lookup table. */ -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifndef USE_RESTRICTED_MODE sp = cheri_setoffset(cheri_getstack(), 0); #else /* @@ -772,6 +801,19 @@ init_stk_table(struct stk_table *table, struct tcb_wrapper *wrap) }; table->entries[RTLD_COMPART_ID].stack = sp + size; +#ifndef HAS_RESTRICTED_MODE + /* + * Install the tidc buffer. + */ + tidc = cheri_setboundsexact(&wrap->tidc, sizeof(wrap->tidc)); + tidc = cheri_seal(tidc, sealer_tidc); + if (sysarch(RISCV_SET_UTIDC, &tidc) != 0) { + rtld_fdprintf(STDERR_FILENO, "c18n: Cannot set tidc %#p\n", + tidc); + abort(); + } +#endif + /* * Push a dummy trusted frame indicating that the 'root' compartment is * RTLD. @@ -859,16 +901,19 @@ resolve_untrusted_stk_impl(stk_table_index index) /* * Stack unwinding + * + * APIs exposed to stack unwinders (e.g., libc setjmp/longjmp and libunwind) */ -/* - * Assembly functions that are tail-called when compartmentalisation is - * disabled. - */ -uintptr_t _rtld_unw_getcontext_epilogue(uintptr_t, void **); -struct jmp_args _rtld_unw_setcontext_epilogue(struct jmp_args, void *, void **); +uintptr_t c18n_get_trusted_stk(uintptr_t, struct trusted_frame **); +uintptr_t c18n_unwind_trusted_stk(uintptr_t, void *, struct trusted_frame *); -static void * -unwind_cursor(void) +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 *); + +uintptr_t +c18n_get_trusted_stk(uintptr_t ret, struct trusted_frame **buf) { /* * This helper is used by functions like setjmp. Before setjmp is @@ -882,39 +927,14 @@ unwind_cursor(void) * buffer. */ - return (get_trusted_stk()->previous); -} - -uintptr_t _rtld_setjmp(uintptr_t, void **); -uintptr_t _rtld_unw_getcontext(uintptr_t, void **); -uintptr_t _rtld_unw_getcontext_unsealed(uintptr_t, void **); - -uintptr_t -_rtld_setjmp(uintptr_t ret, void **buf) -{ - *buf = cheri_seal(unwind_cursor(), sealer_jmpbuf); + if (C18N_ENABLED) + *buf = cheri_seal(get_trusted_stk()->previous, + sealer_trusted_stk); return (ret); } uintptr_t -_rtld_unw_getcontext(uintptr_t ret, void **buf) -{ - if (!C18N_ENABLED) { - __attribute__((musttail)) - return (_rtld_unw_getcontext_epilogue(ret, buf)); - } - *buf = cheri_seal(unwind_cursor(), sealer_unwbuf); - return (ret); -} - -/* - * Returning this struct allows us to control the content of unused return value - * registers. - */ -struct jmp_args { uintptr_t ret1; uintptr_t ret2; }; - -static struct jmp_args -unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) +c18n_unwind_trusted_stk(uintptr_t ret, void *rcsp, struct trusted_frame *target) { /* * This helper is used by functions like longjmp. Before longjmp is @@ -936,6 +956,9 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) struct trusted_frame *cur, *tf; sigset_t nset, oset; + if (!C18N_ENABLED) + return (ret); + /* * Make the function re-entrant by blocking all signals. */ @@ -943,6 +966,7 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) sigprocmask(SIG_SETMASK, &nset, &oset); tf = get_trusted_stk(); + target = cheri_unseal(target, sealer_trusted_stk); if (!cheri_is_subset(tf, target) || tf->previous >= target) { rtld_fdprintf(STDERR_FILENO, @@ -990,7 +1014,7 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) abort(); } - tf->sp = rcsp; + tf->state.sp = rcsp; tf->osp = *ospp; tf->previous = cur; tf->caller = index; @@ -1000,26 +1024,25 @@ unwind_stack(struct jmp_args ret, void *rcsp, struct trusted_frame *target) return (ret); } -struct jmp_args _rtld_longjmp(struct jmp_args, void *, void **); -struct jmp_args _rtld_unw_setcontext_impl(struct jmp_args, void *, void **); - -struct jmp_args -_rtld_longjmp(struct jmp_args ret, void *rcsp, void **buf) +bool +c18n_is_enabled(void) { - return (unwind_stack(ret, rcsp, cheri_unseal(*buf, sealer_jmpbuf))); + return (C18N_ENABLED); } -struct jmp_args -_rtld_unw_setcontext_impl(struct jmp_args ret, void *rcsp, void **buf) +bool +c18n_is_tramp(ptraddr_t pc, struct trusted_frame *tf) { - return (unwind_stack(ret, rcsp, cheri_unseal(*buf, sealer_unwbuf))); + tf = cheri_unseal(tf, sealer_trusted_stk); + return (pc == tf->landing); } -uintptr_t _rtld_unw_getsealer(void); -uintptr_t -_rtld_unw_getsealer(void) +struct trusted_frame * +c18n_pop_trusted_stk(struct compart_state *state, struct trusted_frame *tf) { - return (sealer_unwbuf); + tf = cheri_unseal(tf, sealer_trusted_stk); + *state = tf->state; + return (cheri_seal(tf->previous, sealer_trusted_stk)); } /* @@ -1209,9 +1232,9 @@ tramp_hook_impl(int event, const struct tramp_header *hdr, memcpy(ut.sig, C18N_UTRACE_SIG, C18N_UTRACE_SIG_SZ); ut.event = event; ut.symnum = hdr->symnum; - ut.fp = tf->fp; - ut.pc = tf->pc; - ut.sp = tf->sp; + ut.fp = tf->state.fp; + ut.pc = tf->state.pc; + ut.sp = tf->state.sp; ut.osp = tf->osp; ut.previous = tf->previous; memcpy(&ut.fsig, &hdr->sig, sizeof(ut.fsig)); @@ -1263,6 +1286,7 @@ tramp_pgs_append(const struct tramp_data *data) memcpy(tramp, buf, len); header = (struct tramp_header *)(tramp + (bufp - buf)); +#ifdef __aarch64__ /* * Ensure i- and d-cache coherency after writing executable code. The * __clear_cache procedure rounds the addresses to cache-line-aligned @@ -1270,6 +1294,9 @@ tramp_pgs_append(const struct tramp_data *data) * sufficiently large bounds to contain these rounded addresses. */ __clear_cache(cheri_copyaddress(pg, header->entry), tramp + len); +#elif __riscv + asm volatile ("fence.i"); +#endif return (header); } @@ -1319,9 +1346,7 @@ tramp_make_entry(const struct tramp_header *header) const void *entry = header->entry; entry = cheri_clearperm(entry, FUNC_PTR_REMOVE_PERMS); -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI entry = cheri_capmode(entry); -#endif return (cheri_sealentry(entry)); } @@ -1497,11 +1522,14 @@ tramp_reflect(const void *data) if (!cheri_gettag(data) || !cheri_getsealed(data) || cheri_gettype(data) != CHERI_OTYPE_SENTRY || (cheri_getperm(data) & CHERI_PERM_LOAD) == 0 || - (cheri_getperm(data) & CHERI_PERM_EXECUTE) == 0 || - (cheri_getperm(data) & CHERI_PERM_EXECUTIVE) == 0) + (cheri_getperm(data) & CHERI_PERM_EXECUTE) == 0 +#ifdef HAS_EXECUTIVE_MODE + || (cheri_getperm(data) & CHERI_PERM_EXECUTIVE) == 0 +#endif + ) return (NULL); -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE data = (const char *)data - 1; #endif data = __containerof(data, struct tramp_header, entry); @@ -1612,18 +1640,11 @@ c18n_init(Obj_Entry *obj_rtld, Elf_Auxinfo *aux_info[]) */ #define MAX_TRAMP_PG_SIZE (4 * 1024 * 1024) -/* - * XXX: Manually wrap _rtld_unw_setcontext_impl in a trampoline for now because - * it is called via a function pointer. - */ -extern struct jmp_args (*_rtld_unw_setcontext_ptr)(struct jmp_args, void *, - void **); -struct jmp_args (*_rtld_unw_setcontext_ptr)(struct jmp_args, void *, void **); - void -c18n_init2(Obj_Entry *obj_rtld) +c18n_init2(void) { uintptr_t sealer; + struct tcb_wrapper *wrap; /* * Allocate otypes for RTLD use @@ -1638,11 +1659,13 @@ c18n_init2(Obj_Entry *obj_rtld) sealer_tcb = cheri_setboundsexact(sealer, 1); sealer += 1; - sealer_jmpbuf = cheri_setboundsexact(sealer, 1); + sealer_trusted_stk = cheri_setboundsexact(sealer, 1); sealer += 1; - sealer_unwbuf = cheri_setboundsexact(sealer, 1); +#ifndef HAS_RESTRICTED_MODE + sealer_tidc = cheri_setboundsexact(sealer, 1); sealer += 1; +#endif sealer_tramp = cheri_setboundsexact(sealer, C18N_FUNC_SIG_COUNT); sealer += C18N_FUNC_SIG_COUNT; @@ -1651,7 +1674,9 @@ c18n_init2(Obj_Entry *obj_rtld) * All libraries have been loaded. Create and initialise a stack lookup * table with the same size as the number of compartments. */ - init_stk_table(expand_stk_table(NULL, comparts.size), NULL); + wrap = c18n_malloc(sizeof(*wrap)); + *wrap = (struct tcb_wrapper) {}; + init_stk_table(expand_stk_table(NULL, comparts.size), wrap); /* * Create a trampoline table with 2^9 = 512 entries. @@ -1670,19 +1695,6 @@ c18n_init2(Obj_Entry *obj_rtld) assert(tramp_pg_size > 0); atomic_store_explicit(&tramp_pgs.head, tramp_pg_new(NULL), memory_order_relaxed); - - /* - * XXX: Manually wrap _rtld_unw_setcontext_impl in a trampoline for now - * because it is called via a function pointer. - */ - _rtld_unw_setcontext_ptr = tramp_intern(NULL, &(struct tramp_data) { - .target = &_rtld_unw_setcontext_impl, - .defobj = obj_rtld, - .sig = (struct func_sig) { - .valid = true, - .reg_args = 4, .mem_args = false, .ret_args = TWO - } - }); } /* @@ -1697,7 +1709,9 @@ void _rtld_thr_exit(long *); void _rtld_thread_start_init(void (*p)(struct pthread *)) { +#ifdef HAS_EXECUTIVE_MODE assert((cheri_getperm(p) & CHERI_PERM_EXECUTIVE) == 0); +#endif assert(thr_thread_start == NULL); thr_thread_start = tramp_intern(NULL, &(struct tramp_data) { .target = p, @@ -1721,7 +1735,14 @@ _rtld_thread_start(struct pthread *curthread) * new thread. Extract and install the actual tcb and stack lookup * table. */ - wrap = __containerof(get_trusted_tp(), struct tcb_wrapper, header); + tcb = get_trusted_tp(); +#ifdef __riscv + /* + * The TCB is shifted by the kernel on RISC-V. See `cpu_set_user_tls`. + */ + tcb -= 1; +#endif + wrap = __containerof(tcb, struct tcb_wrapper, header); tcb = cheri_unseal(wrap->tcb, sealer_tcb); *tcb = wrap->header; @@ -1746,6 +1767,16 @@ _rtld_thr_exit(long *state) set_stk_table(NULL); set_trusted_stk(NULL); +#ifndef HAS_RESTRICTED_MODE + /* + * Uninstall the tidc buffer. + */ + if (sysarch(RISCV_SET_UTIDC, &(void *) { NULL }) != 0) { + rtld_fdprintf(STDERR_FILENO, "c18n: Cannot clear tidc\n"); + abort(); + } +#endif + /* * Clear RTLD's stack lookup table entry. */ @@ -1833,7 +1864,9 @@ static __siginfohandler_t *signal_dispatcher = sigdispatch; void _rtld_sighandler_init(__siginfohandler_t *handler) { +#ifdef HAS_EXECUTIVE_MODE assert((cheri_getperm(handler) & CHERI_PERM_EXECUTIVE) == 0); +#endif assert(signal_dispatcher == sigdispatch); signal_dispatcher = tramp_intern(NULL, &(struct tramp_data) { .target = handler, @@ -1845,7 +1878,7 @@ _rtld_sighandler_init(__siginfohandler_t *handler) }); } -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifndef USE_RESTRICTED_MODE void _rtld_sighandler_impl(int, siginfo_t *, ucontext_t *, void *, struct sigframe *); @@ -1870,7 +1903,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) table = get_stk_table(); -#ifdef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifndef USE_RESTRICTED_MODE /* * Move the sigframe to RTLD's stack. */ @@ -1939,7 +1972,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) intr = RTLD_COMPART_ID; if (cheri_is_subset(table->meta->compart_stk[intr].begin, nsp)) goto found_trusted; -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE found_failed: #endif rtld_fdprintf(STDERR_FILENO, @@ -1947,7 +1980,7 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) "Please file a bug report!\n", nsp); abort(); found_trusted: -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#ifdef USE_RESTRICTED_MODE /* * The untrusted stack can only become temporarily inconsistent when * running code in Executive mode. This performs a quick sanity check. @@ -1977,7 +2010,9 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) */ ntf = tf - 2; *ntf = (struct trusted_frame) { - .sp = nsp, + .state = (struct compart_state) { + .sp = nsp + }, .osp = osp, .previous = tf, .caller = intr_idx, @@ -1993,7 +2028,11 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) * If the interrupted code has loaded the stack lookup table, it would * be located in register STACK_TABLE_N. Check if this is the case. */ +#ifdef __aarch64__ table_reg = &ucp->uc_mcontext.mc_capregs.cap_x[STACK_TABLE_N]; +#elif defined(__riscv) + table_reg = &ucp->uc_mcontext.mc_capregs.cp_ct[STACK_TABLE_N]; +#endif if (!cheri_equal_exact(table, *table_reg)) table_reg = NULL; @@ -2031,8 +2070,8 @@ _rtld_sighandler_impl(int sig, siginfo_t *info, ucontext_t *ucp, void *nsp) * stack and let the kernel install the stack of the interrupted * compartment. */ -#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI - set_untrusted_stk(ntf->sp); +#ifdef USE_RESTRICTED_MODE + set_untrusted_stk(ntf->state.sp); #endif } diff --git a/libexec/rtld-elf/rtld_c18n.h b/libexec/rtld-elf/rtld_c18n.h index 646d48b23779..86a581019e79 100644 --- a/libexec/rtld-elf/rtld_c18n.h +++ b/libexec/rtld-elf/rtld_c18n.h @@ -30,11 +30,22 @@ #include +#ifdef __aarch64__ +#define HAS_RESTRICTED_MODE +#ifndef __ARM_MORELLO_PURECAP_BENCHMARK_ABI +#define USE_RESTRICTED_MODE +#endif +#endif + /* * Global symbols */ -extern size_t c18n_code_perm_clear; extern uintptr_t sealer_pltgot, sealer_tramp; +#ifdef HAS_RESTRICTED_MODE +extern size_t c18n_code_perm_clear; +#else +extern uintptr_t sealer_tidc; +#endif extern const char *ld_compartment_utrace; extern const char *ld_compartment_policy; extern const char *ld_compartment_overhead; @@ -120,6 +131,47 @@ struct stk_table { #include "rtld_c18n_machdep.h" +struct trusted_frame { + /* + * Architecture-specific callee-saved registers, including fp, sp, and + * the return address + */ + struct compart_state state; + /* + * INVARIANT: This field contains the top of the caller's stack when the + * caller was last entered. + */ + void *osp; + /* + * Address of the previous trusted frame + */ + struct trusted_frame *previous; + /* + * Compartment ID of the caller + */ + stk_table_index caller; + /* + * This padding space must be filled with zeros so that an optimised + * trampoline can use a wide load to load multiple fields of the trusted + * frame and then use a word-sized register to extract the caller field. + */ + uint16_t zeros; + /* + * Compartment ID of the callee + */ + stk_table_index callee; + /* + * Number of return value registers with architecture-specific encoding + */ + uint16_t n_rets; + /* + * This field contains the code address in the trampoline that the + * callee should return to. This is used by trampolines to detect cross- + * compartment tail-calls. + */ + ptraddr_t landing; +}; + struct tcb *c18n_allocate_tcb(struct tcb *); void c18n_free_tcb(void); @@ -217,8 +269,8 @@ func_sig_legal(struct func_sig sig) /* * This macro can only be used in a function directly invoked by a trampoline. */ -#define c18n_return_address() \ - (C18N_ENABLED ? get_trusted_stk()->pc : __builtin_return_address(0)) +#define c18n_return_address() (C18N_ENABLED ? \ + get_trusted_stk()->state.pc : __builtin_return_address(0)) void *_rtld_sandbox_code(void *, struct func_sig); void *_rtld_safebox_code(void *, struct func_sig); @@ -229,5 +281,5 @@ void *_rtld_tlsdesc_undef_c18n(void *); void *_rtld_tlsdesc_dynamic_c18n(void *); void c18n_init(Obj_Entry *, Elf_Auxinfo *[]); -void c18n_init2(Obj_Entry *); +void c18n_init2(void); #endif diff --git a/libexec/rtld-elf/rtld_c18n_mi.S b/libexec/rtld-elf/rtld_c18n_mi.S index 3027ce5c97d1..8f0abe055b9d 100644 --- a/libexec/rtld-elf/rtld_c18n_mi.S +++ b/libexec/rtld-elf/rtld_c18n_mi.S @@ -38,7 +38,7 @@ c18n_default_policy_end: .size c18n_default_policy, . - c18n_default_policy .globl c18n_default_policy_size -.type c18n_default_policy_size,#object +.type c18n_default_policy_size,%object .align 3 c18n_default_policy_size: .quad c18n_default_policy_end - c18n_default_policy diff --git a/libexec/rtld-elf/rtld_c18n_policy.txt b/libexec/rtld-elf/rtld_c18n_policy.txt index d373837982ab..401637b62619 100644 --- a/libexec/rtld-elf/rtld_c18n_policy.txt +++ b/libexec/rtld-elf/rtld_c18n_policy.txt @@ -1,13 +1,5 @@ Version 1 -compartment [TCB] - libc.so.7 - libthr.so.3 - libsys.so.7 - -compartment [libunwind] - libgcc_s.so.1 - caller * trust memset @@ -44,23 +36,18 @@ trust _setjmp sigsetjmp unw_getcontext + __sys_vfork + __sys_rfork + _execve + execve + fxecve + execl + execlp + execle + exect + execv + execvp + execvpe + execvP _rtld_thread_start _rtld_sighandler - -callee [RTLD] -export to [TCB] - _rtld_thread_start_init - _rtld_thread_start - _rtld_thr_exit - _rtld_sighandler_init - _rtld_sighandler - _rtld_siginvoke - _rtld_sigaction - _rtld_setjmp - _rtld_longjmp - -callee [RTLD] -export to [libunwind] - _rtld_unw_getcontext - _rtld_unw_setcontext - _rtld_unw_getsealer diff --git a/libexec/rtld-elf/rtld_lock.c b/libexec/rtld-elf/rtld_lock.c index 8d9306dd93dd..9647c3977483 100644 --- a/libexec/rtld-elf/rtld_lock.c +++ b/libexec/rtld-elf/rtld_lock.c @@ -66,7 +66,7 @@ #include "rtld.h" #include "rtld_machdep.h" #include "rtld_libc.h" -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #include "rtld_c18n.h" #endif @@ -248,7 +248,7 @@ thread_mask_clear(int mask) lockinfo.thread_clr_flag(mask); } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N #define RTLD_LOCK_CNT 4 #else #define RTLD_LOCK_CNT 3 @@ -261,7 +261,7 @@ static struct rtld_lock { rtld_lock_t rtld_bind_lock = &rtld_locks[0]; rtld_lock_t rtld_libc_lock = &rtld_locks[1]; rtld_lock_t rtld_phdr_lock = &rtld_locks[2]; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N rtld_lock_t rtld_tramp_lock = &rtld_locks[3]; #endif @@ -410,7 +410,7 @@ _rtld_thread_init(struct RtldLockInfo *pli) SymLook req; void *locks[RTLD_LOCK_CNT]; int flags, i, res; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N struct RtldLockInfo tmplockinfo; #endif @@ -425,7 +425,7 @@ _rtld_thread_init(struct RtldLockInfo *pli) if (res == 0) lockinfo.rtli_version = pli->rtli_version; } -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N tmplockinfo = *pli; #define WRAP(_target, _valid, _reg_args, _mem_args, _ret_args) \ _target = tramp_intern(NULL, &(struct tramp_data) { \ diff --git a/libexec/rtld-elf/rtld_lock.h b/libexec/rtld-elf/rtld_lock.h index 5d13a6e4d664..c677029a67fa 100644 --- a/libexec/rtld-elf/rtld_lock.h +++ b/libexec/rtld-elf/rtld_lock.h @@ -82,7 +82,7 @@ typedef struct rtld_lock *rtld_lock_t; extern rtld_lock_t rtld_bind_lock; extern rtld_lock_t rtld_libc_lock; extern rtld_lock_t rtld_phdr_lock; -#if defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#ifdef CHERI_LIB_C18N extern rtld_lock_t rtld_tramp_lock; #endif diff --git a/sys/cheri/cheri.h b/sys/cheri/cheri.h index f4f783b2e168..69717f390766 100644 --- a/sys/cheri/cheri.h +++ b/sys/cheri/cheri.h @@ -168,9 +168,7 @@ extern u_int security_cheri_debugger_on_sandbox_syscall; extern u_int security_cheri_syscall_violations; extern u_int security_cheri_bound_legacy_capabilities; extern u_int cheri_cloadtags_stride; -#ifdef __aarch64__ extern bool security_cheri_lib_based_c18n_default; -#endif #ifdef __CHERI_PURE_CAPABILITY__ /* diff --git a/sys/cheri/cheri_sysctl.c b/sys/cheri/cheri_sysctl.c index 66dd6df95b6a..f72bee34af29 100644 --- a/sys/cheri/cheri_sysctl.c +++ b/sys/cheri/cheri_sysctl.c @@ -80,7 +80,6 @@ SYSCTL_INT(_security_cheri, OID_AUTO, bound_legacy_capabilities, CTLFLAG_RWTUN, &security_cheri_bound_legacy_capabilities, 0, "Set bounds on userspace capabilities created by legacy ABIs."); -#ifdef __aarch64__ /* * Set the default state of library-based compartmentalisation (c18n) in * userspace. @@ -89,7 +88,6 @@ bool security_cheri_lib_based_c18n_default = false; SYSCTL_BOOL(_security_cheri, OID_AUTO, lib_based_c18n_default, CTLFLAG_RWTUN, &security_cheri_lib_based_c18n_default, 0, "Userspace library-based compartmentalisation default"); -#endif #ifdef CHERI_CAPREVOKE /* diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 161660a3e956..f1c3b2afbae0 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -1839,7 +1839,7 @@ __elfN(freebsd_copyout_auxargs)(struct image_params *imgp, uintcap_t base) oc = atomic_load_int(&vm_overcommit); bsdflags |= (oc & (SWAP_RESERVE_FORCE_ON | SWAP_RESERVE_RLIMIT_ON)) != 0 ? ELF_BSDF_VMNOOVERCOMMIT : 0; -#if defined(__ELF_CHERI) && defined(__aarch64__) +#ifdef __ELF_CHERI /* * ELF_BSDF_CHERI_C18N tells the runtime linker to enable library-based * compartmentalisation. diff --git a/sys/riscv/include/frame.h b/sys/riscv/include/frame.h index 4a28fd62f763..86ecb38c7e9a 100644 --- a/sys/riscv/include/frame.h +++ b/sys/riscv/include/frame.h @@ -54,6 +54,7 @@ struct trapframe { uintcap_t tf_a[8]; uintcap_t tf_sepc; uintcap_t tf_ddc; + uintcap_t tf_tidc; #else uint64_t tf_ra; uint64_t tf_sp; diff --git a/sys/riscv/include/reg.h b/sys/riscv/include/reg.h index 5883e24c51d2..8355f2e1953e 100644 --- a/sys/riscv/include/reg.h +++ b/sys/riscv/include/reg.h @@ -69,6 +69,7 @@ struct capreg { __uintcap_t ca[8]; /* function arguments */ __uintcap_t sepcc; /* exception program counter */ __uintcap_t ddc; + __uintcap_t tidc; __uint64_t tagmask; __uint64_t pad; }; diff --git a/sys/riscv/include/sysarch.h b/sys/riscv/include/sysarch.h index 8cf1243b57bb..19b2887a343e 100644 --- a/sys/riscv/include/sysarch.h +++ b/sys/riscv/include/sysarch.h @@ -43,6 +43,8 @@ #define QEMU_SET_QTRACE 101 /* Set (or clear) QEMU tracing. */ #define QEMU_SET_QTRACE_USER 102 /* Set (or clear) QEMU per-thread user-only tracing mode */ +#define RISCV_SET_UTIDC 103 /* Set user thread id capability */ + #ifndef _KERNEL __BEGIN_DECLS diff --git a/sys/riscv/include/ucontext.h b/sys/riscv/include/ucontext.h index 7352c4abda4b..15020eeb91d8 100644 --- a/sys/riscv/include/ucontext.h +++ b/sys/riscv/include/ucontext.h @@ -65,6 +65,7 @@ struct capregs { __uintcap_t cp_ca[8]; __uintcap_t cp_sepcc; __uintcap_t cp_ddc; + __uintcap_t cp_tidc; __register_t cp_sstatus; __register_t cp_pad; }; diff --git a/sys/riscv/riscv/exception.S b/sys/riscv/riscv/exception.S index a1f718a40075..bb9b74cb3b06 100644 --- a/sys/riscv/riscv/exception.S +++ b/sys/riscv/riscv/exception.S @@ -126,6 +126,13 @@ cspecialw sscratchc, ct0 cspecialr ct0, sepcc csc ct0, (TF_SEPC)(csp) + /* Store user tidc */ +.if \mode == 1 + cspecialr ct0, 11 /* stidc */ +.else + cspecialr ct0, 3 /* utidc */ +.endif + csc ct0, (TF_TIDC)(csp) cspecialr ct0, ddc csc ct0, (TF_DDC)(csp) @@ -280,6 +287,12 @@ */ clc ct0, (TF_DDC)(csp) cspecialw ddc, ct0 + clc ct0, (TF_TIDC)(csp) +.if \mode == 1 + cspecialw 11, ct0 /* stidc */ +.else + cspecialw 3, ct0 /* utidc */ +.endif clc ct0, (TF_SEPC)(csp) cspecialw sepcc, ct0 diff --git a/sys/riscv/riscv/exec_machdep.c b/sys/riscv/riscv/exec_machdep.c index 6657c6b71a52..35a58afc2c0c 100644 --- a/sys/riscv/riscv/exec_machdep.c +++ b/sys/riscv/riscv/exec_machdep.c @@ -72,8 +72,8 @@ static void get_fpcontext(struct thread *td, mcontext_t *mcp); static void set_fpcontext(struct thread *td, mcontext_t *mcp); #if __has_feature(capabilities) -_Static_assert(sizeof(mcontext_t) == 1152, "mcontext_t size incorrect"); -_Static_assert(sizeof(ucontext_t) == 1248, "ucontext_t size incorrect"); +_Static_assert(sizeof(mcontext_t) == 1168, "mcontext_t size incorrect"); +_Static_assert(sizeof(ucontext_t) == 1264, "ucontext_t size incorrect"); _Static_assert(sizeof(siginfo_t) == 112, "siginfo_t size incorrect"); #else _Static_assert(sizeof(mcontext_t) == 864, "mcontext_t size incorrect"); @@ -235,6 +235,8 @@ _Static_assert(offsetof(struct trapframe, tf_sepc) == offsetof(struct capreg, sepcc), "sepcc mismatch"); _Static_assert(offsetof(struct trapframe, tf_ddc) == offsetof(struct capreg, ddc), "ddc mismatch"); +_Static_assert(offsetof(struct trapframe, tf_tidc) == + offsetof(struct capreg, tidc), "tidc mismatch"); int fill_capregs(struct thread *td, struct capreg *regs) @@ -418,6 +420,7 @@ get_mcontext(struct thread *td, mcontext_t *mcp, int clear_ret) mcp->mc_capregs.cp_ctp = tf->tf_tp; mcp->mc_capregs.cp_sepcc = tf->tf_sepc; mcp->mc_capregs.cp_ddc = tf->tf_ddc; + mcp->mc_capregs.cp_tidc = tf->tf_tidc; mcp->mc_capregs.cp_sstatus = tf->tf_sstatus; #else memcpy(mcp->mc_gpregs.gp_t, tf->tf_t, sizeof(mcp->mc_gpregs.gp_t)); @@ -478,6 +481,7 @@ set_mcontext(struct thread *td, mcontext_t *mcp) tf->tf_gp = mcp->mc_capregs.cp_cgp; tf->tf_sepc = mcp->mc_capregs.cp_sepcc; tf->tf_ddc = mcp->mc_capregs.cp_ddc; + tf->tf_tidc = mcp->mc_capregs.cp_tidc; tf->tf_sstatus = mcp->mc_capregs.cp_sstatus; #else memcpy(tf->tf_t, mcp->mc_gpregs.gp_t, sizeof(tf->tf_t)); diff --git a/sys/riscv/riscv/genassym.c b/sys/riscv/riscv/genassym.c index 7ed53ddbc4d8..1c9aa4abf3b2 100644 --- a/sys/riscv/riscv/genassym.c +++ b/sys/riscv/riscv/genassym.c @@ -99,6 +99,7 @@ ASSYM(TF_A, offsetof(struct trapframe, tf_a)); ASSYM(TF_SEPC, offsetof(struct trapframe, tf_sepc)); #if __has_feature(capabilities) ASSYM(TF_DDC, offsetof(struct trapframe, tf_ddc)); +ASSYM(TF_TIDC, offsetof(struct trapframe, tf_tidc)); #endif ASSYM(TF_STVAL, offsetof(struct trapframe, tf_stval)); ASSYM(TF_SCAUSE, offsetof(struct trapframe, tf_scause)); diff --git a/sys/riscv/riscv/swtch.S b/sys/riscv/riscv/swtch.S index 45d460fb4677..36d3d64128aa 100644 --- a/sys/riscv/riscv/swtch.S +++ b/sys/riscv/riscv/swtch.S @@ -682,6 +682,9 @@ ENTRY(fork_trampoline) clc ct0, (TF_DDC)(csp) cspecialw ddc, ct0 + clc ct0, (TF_TIDC)(csp) + cspecialw 3, ct0 /* utidc */ + /* Restore exception program counter */ clc ct0, (TF_SEPC)(csp) cspecialw sepcc, ct0 diff --git a/sys/riscv/riscv/sys_machdep.c b/sys/riscv/riscv/sys_machdep.c index f6d7d56f1a86..75fa8a8ac127 100644 --- a/sys/riscv/riscv/sys_machdep.c +++ b/sys/riscv/riscv/sys_machdep.c @@ -71,6 +71,15 @@ sysarch(struct thread *td, struct sysarch_args *uap) else td->td_md.md_flags &= ~MDTD_QTRACE_USERMODE; return (0); +#endif +#if __has_feature(capabilities) + case RISCV_SET_UTIDC: { + uintcap_t val; + int error = copyincap(uap->parms, &val, sizeof(val)); + if (!error) + td->td_frame->tf_tidc = val; + return (error); + } #endif default: break; diff --git a/sys/sys/link_elf.h b/sys/sys/link_elf.h index 88bd92a912c1..f999a296d945 100644 --- a/sys/sys/link_elf.h +++ b/sys/sys/link_elf.h @@ -74,7 +74,7 @@ struct r_debug { RT_DELETE /* removing a shared library */ } r_state; void *r_ldbase; /* Base address of rtld */ -#if defined(IN_RTLD) && defined(__CHERI_PURE_CAPABILITY__) && defined(CHERI_LIB_C18N) +#if defined(IN_RTLD) && defined(CHERI_LIB_C18N) enum { RCT_CONSISTENT, /* vector is stable */ RCT_ADD, /* adding a compartment */ diff --git a/sys/sys/procctl.h b/sys/sys/procctl.h index f3bdb15fa6bd..d3813b7884eb 100644 --- a/sys/sys/procctl.h +++ b/sys/sys/procctl.h @@ -67,7 +67,7 @@ #define PROC_WXMAP_STATUS 22 /* query W^X */ #define PROC_CHERI_REVOKE_CTL 1001 /* en/dis CHERI revocation */ #define PROC_CHERI_REVOKE_STATUS 1002 /* query CHERI revocation status */ -#if __has_feature(capabilities) && defined(__aarch64__) +#if __has_feature(capabilities) #define PROC_CHERI_C18N_CTL 1003 /* en/dis CHERI compartmentalisation */ #define PROC_CHERI_C18N_STATUS 1004 /* query CHERI compartmentalisation status */ #endif