Index: libunwind/CMakeLists.txt =================================================================== --- libunwind/CMakeLists.txt +++ libunwind/CMakeLists.txt @@ -52,6 +52,7 @@ # Define options. option(LIBUNWIND_BUILD_32_BITS "Build 32 bit libunwind" ${LLVM_BUILD_32_BITS}) +option(LIBUNWIND_BUILD_CET_ENABLE "Build libunwind with CET enabled" ${LLVM_BUILD_CET_ENABLE}) option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON) option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF) @@ -138,6 +139,12 @@ set(LIBUNWIND_COMPILE_FLAGS "") set(LIBUNWIND_LINK_FLAGS "") +if (LIBUNWIND_BUILD_CET_ENABLE) + set(LIBUNWIND_CET_ON True) +else() + set(LIBUNWIND_CET_ON False) +endif() + # Include macros for adding and removing libunwind flags. include(HandleLibunwindFlags) @@ -176,6 +183,10 @@ endif() add_compile_flags_if_supported(-Werror=return-type) +if (LIBUNWIND_CET_ON) + add_compile_flags_if_supported(-fcf-protection=full) + add_compile_flags_if_supported(-mshstk) +endif() # Get warning flags add_compile_flags_if_supported(-W) Index: libunwind/src/CMakeLists.txt =================================================================== --- libunwind/src/CMakeLists.txt +++ libunwind/src/CMakeLists.txt @@ -34,6 +34,7 @@ AddressSpace.hpp assembly.h CompactUnwinder.hpp + cet_unwind.h config.h dwarf2.h DwarfInstructions.hpp Index: libunwind/src/Registers.hpp =================================================================== --- libunwind/src/Registers.hpp +++ libunwind/src/Registers.hpp @@ -15,8 +15,9 @@ #include #include -#include "libunwind.h" +#include "cet_unwind.h" #include "config.h" +#include "libunwind.h" namespace libunwind { @@ -245,6 +246,43 @@ inline void Registers_x86::setVectorRegister(int, v128) { _LIBUNWIND_ABORT("no x86 vector registers"); } + +#if defined(_LIBUNWIND_CET_ENABLED) +void _LIBUNWIND_HIDDEN cetGetRegs_X86(unw_cursor_t *cursor, + Registers_x86 *regs) { + if (!cursor || !regs) { + _LIBUNWIND_ABORT("Invalid cursor or regs when trying to get x86_64 regs in " + "CET environment"); + } + + int regNum; + unw_word_t regValue = 0; + for (regNum = UNW_X86_EAX; regNum <= UNW_X86_EDI; ++regNum) { + unw_get_reg(cursor, regNum, ®Value); + regs->setRegister(regNum, regValue); + } + + unw_get_reg(cursor, UNW_REG_IP, ®Value); + regs->setRegister(UNW_REG_IP, regValue); +} + +extern "C" void __libunwind_cet_jumpto(unw_cursor_t *cursor, + unsigned int framesSkipped) { + Registers_x86 x86_Regs; + cetGetRegs_X86(cursor, &x86_Regs); + void *cetRegContext = &x86_Regs; + void *cetJumpAddress = (void *)&__libunwind_Registers_x86_jumpto; + // In CET enabled environment, each function call will also push return + // address into shadow stack for further check. We must adjust the shadow + // stack when unwinding stack frames. + _LIBUNWIND_POP_CET_SSP(framesSkipped + 1); + __asm__ volatile("push %%edi\n\t" + "sub $4, %%esp\n\t" + "jmp *%%edx\n\t" ::"D"(cetRegContext), + "d"(cetJumpAddress) + :); +} +#endif // _LIBUNWIND_CET_ENABLED #endif // _LIBUNWIND_TARGET_I386 @@ -561,6 +599,38 @@ _LIBUNWIND_ABORT("no x86_64 vector registers"); #endif } + +#if defined(_LIBUNWIND_CET_ENABLED) +void _LIBUNWIND_HIDDEN cetGetRegs_X86_64(unw_cursor_t *cursor, + Registers_x86_64 *regs) { + if (!cursor || !regs) { + _LIBUNWIND_ABORT("Invalid cursor or regs when trying to get x86_64 regs in " + "CET environment"); + } + + int regNum; + unw_word_t regValue = 0; + for (regNum = UNW_X86_64_RAX; regNum < UNW_X86_64_RIP; ++regNum) { + unw_get_reg(cursor, regNum, ®Value); + regs->setRegister(regNum, regValue); + } + + unw_get_reg(cursor, UNW_REG_IP, ®Value); + regs->setRegister(UNW_REG_IP, regValue); +} + +extern "C" void __libunwind_cet_jumpto(unw_cursor_t *cursor, + unsigned int framesSkipped) { + Registers_x86_64 x86_64_Regs; + cetGetRegs_X86_64(cursor, &x86_64_Regs); + void *cetRegContext = &x86_64_Regs; + void *cetJumpAddress = (void *)&__libunwind_Registers_x86_64_jumpto; + _LIBUNWIND_POP_CET_SSP(framesSkipped + 1); + __asm__ volatile("jmpq *%%rdx\n\t" ::"D"(cetRegContext), "d"(cetJumpAddress) + :); +} + +#endif // _LIBUNWIND_CET_ENABLED #endif // _LIBUNWIND_TARGET_X86_64 Index: libunwind/src/UnwindLevel1.c =================================================================== --- libunwind/src/UnwindLevel1.c +++ libunwind/src/UnwindLevel1.c @@ -25,6 +25,7 @@ #include #include +#include "cet_unwind.h" #include "config.h" #include "libunwind.h" #include "libunwind_ext.h" @@ -34,6 +35,16 @@ #ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND +#if defined(_LIBUNWIND_CET_ENABLED) +#define __unw_phase2_resume(cursor, frames_skipped) \ + do { \ + _LIBUNWIND_POP_CET_SSP(1); \ + __libunwind_cet_jumpto((cursor), (frames_skipped)); \ + } while (0) +#else +#define __unw_phase2_resume(cursor, frames_skipped) __unw_resume((cursor)) +#endif + static _Unwind_Reason_Code unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { __unw_init_local(cursor, uc); @@ -135,6 +146,7 @@ _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", (void *)exception_object); + unsigned int framesWalked = 0; // Walk each frame until we reach where search phase said to stop. while (true) { @@ -184,6 +196,7 @@ frameInfo.handler); } + framesWalked++; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { _Unwind_Personality_Fn p = @@ -223,8 +236,9 @@ ", sp=0x%" PRIxPTR, (void *)exception_object, pc, sp); } - __unw_resume(cursor); - // __unw_resume() only returns if there was an error. + + __unw_phase2_resume(cursor, framesWalked); + // __unw_phase2_resume() only returns if there was an error. return _URC_FATAL_PHASE2_ERROR; default: // Personality routine returned an unknown result code. @@ -246,6 +260,7 @@ _Unwind_Stop_Fn stop, void *stop_parameter) { __unw_init_local(cursor, uc); + unsigned int framesWalked = 0; // Walk each frame until we reach where search phase said to stop while (__unw_step(cursor) > 0) { @@ -290,6 +305,7 @@ return _URC_FATAL_PHASE2_ERROR; } + framesWalked++; // If there is a personality routine, tell it we are unwinding. if (frameInfo.handler != 0) { _Unwind_Personality_Fn p = @@ -313,8 +329,9 @@ "personality returned " "_URC_INSTALL_CONTEXT", (void *)exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). - __unw_resume(cursor); + __unw_phase2_resume(cursor, framesWalked); break; default: // Personality routine returned an unknown result code. Index: libunwind/src/UnwindRegistersRestore.S =================================================================== --- libunwind/src/UnwindRegistersRestore.S +++ libunwind/src/UnwindRegistersRestore.S @@ -25,6 +25,7 @@ # + return address + # +-----------------------+ <-- SP # + + + _LIBUNWIND_CET_ENDBR movl 4(%esp), %eax # set up eax and ret on new stack location movl 28(%eax), %edx # edx holds new stack pointer @@ -46,7 +47,8 @@ # skip ss # skip eflags pop %eax # eax was already pushed on new stack - ret # eip was already pushed on new stack + pop %ecx + jmp *%ecx # skip cs # skip ds # skip es @@ -70,6 +72,7 @@ # On entry, thread_state pointer is in rdi #endif + _LIBUNWIND_CET_ENDBR movq 56(%rdi), %rax # rax holds new stack pointer subq $16, %rax movq %rax, 56(%rdi) @@ -119,8 +122,8 @@ #endif movq 56(%rdi), %rsp # cut back rsp to new location pop %rdi # rdi was saved here earlier - ret # rip was saved here - + pop %rcx + jmpq *%rcx #elif defined(__powerpc64__) Index: libunwind/src/UnwindRegistersSave.S =================================================================== --- libunwind/src/UnwindRegistersSave.S +++ libunwind/src/UnwindRegistersSave.S @@ -27,6 +27,7 @@ # + + # DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + _LIBUNWIND_CET_ENDBR push %eax movl 8(%esp), %eax movl %ebx, 4(%eax) @@ -70,6 +71,7 @@ #define TMP %rsi #endif + _LIBUNWIND_CET_ENDBR movq %rax, (PTR) movq %rbx, 8(PTR) movq %rcx, 16(PTR) Index: libunwind/src/assembly.h =================================================================== --- libunwind/src/assembly.h +++ libunwind/src/assembly.h @@ -15,6 +15,13 @@ #ifndef UNWIND_ASSEMBLY_H #define UNWIND_ASSEMBLY_H +#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__) +#include +#define _LIBUNWIND_CET_ENDBR _CET_ENDBR +#else +#define _LIBUNWIND_CET_ENDBR +#endif + #if defined(__powerpc64__) #define SEPARATOR ; #define PPC64_OFFS_SRR0 0 Index: libunwind/src/cet_unwind.h =================================================================== --- /dev/null +++ libunwind/src/cet_unwind.h @@ -0,0 +1,35 @@ +//===--------------------------- cet_unwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +#ifndef _CET_UNWIND_H_ +#define _CET_UNWIND_H_ +#include "libunwind.h" +#if defined(_LIBUNWIND_TARGET_LINUX) && \ + (defined(_LIBUNWIND_TARGET_I386) || defined(_LIBUNWIND_TARGET_X86_64)) && \ + defined(__CET__) && defined(__SHSTK__) +#define _LIBUNWIND_CET_ENABLED 1 +#include +#include + +#define _LIBUNWIND_POP_CET_SSP(x) \ + do { \ + unsigned long ssp = _get_ssp(); \ + if (ssp != 0) { \ + unsigned int tmp = (x); \ + while (tmp > 255) { \ + _inc_ssp(255); \ + tmp -= 255; \ + } \ + _inc_ssp(tmp); \ + } \ + } while (0) +#endif + +extern void __libunwind_cet_jumpto(unw_cursor_t *, unsigned int); +#endif Index: libunwind/test/libunwind/test/config.py =================================================================== --- libunwind/test/libunwind/test/config.py +++ libunwind/test/libunwind/test/config.py @@ -50,6 +50,8 @@ if not self.get_lit_bool('enable_threads', True): self.cxx.compile_flags += ['-D_LIBUNWIND_HAS_NO_THREADS'] self.config.available_features.add('libunwind-no-threads') + if self.get_lit_bool('cet_on'): + self.cxx.compile_flags += ['-fcf-protection=full'] super(Configuration, self).configure_compile_flags() def configure_compile_flags_header_includes(self): Index: libunwind/test/lit.site.cfg.in =================================================================== --- libunwind/test/lit.site.cfg.in +++ libunwind/test/lit.site.cfg.in @@ -27,6 +27,7 @@ config.sysroot = "@LIBUNWIND_SYSROOT@" config.gcc_toolchain = "@LIBUNWIND_GCC_TOOLCHAIN@" config.cxx_ext_threads = @LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY@ +config.cet_on = @LIBUNWIND_CET_ON@ site.addsitedir(os.path.join(config.libunwind_src_root, 'test')) site.addsitedir(os.path.join(config.libcxx_src_root, 'utils'))