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_ENABLE_CET "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) @@ -176,6 +177,10 @@ endif() add_compile_flags_if_supported(-Werror=return-type) +if (LIBUNWIND_ENABLE_CET) + 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 { @@ -42,6 +43,12 @@ #if defined(_LIBUNWIND_TARGET_I386) class _LIBUNWIND_HIDDEN Registers_x86; extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *); +#if defined(_LIBUNWIND_CET_ENABLED) +extern "C" void *__libunwind_get_cet_jump_target() { + return reinterpret_cast(&__libunwind_Registers_x86_jumpto); +} +#endif + /// Registers_x86 holds the register state of a thread in a 32-bit intel /// process. class _LIBUNWIND_HIDDEN Registers_x86 { @@ -245,6 +252,7 @@ inline void Registers_x86::setVectorRegister(int, v128) { _LIBUNWIND_ABORT("no x86 vector registers"); } + #endif // _LIBUNWIND_TARGET_I386 @@ -253,6 +261,13 @@ /// process. class _LIBUNWIND_HIDDEN Registers_x86_64; extern "C" void __libunwind_Registers_x86_64_jumpto(Registers_x86_64 *); + +#if defined(_LIBUNWIND_CET_ENABLED) +extern "C" void *__libunwind_get_cet_jump_target() { + return reinterpret_cast(&__libunwind_Registers_x86_64_jumpto); +} +#endif + class _LIBUNWIND_HIDDEN Registers_x86_64 { public: Registers_x86_64(); @@ -561,6 +576,7 @@ _LIBUNWIND_ABORT("no x86_64 vector registers"); #endif } + #endif // _LIBUNWIND_TARGET_X86_64 Index: libunwind/src/UnwindCursor.hpp =================================================================== --- libunwind/src/UnwindCursor.hpp +++ libunwind/src/UnwindCursor.hpp @@ -11,6 +11,7 @@ #ifndef __UNWINDCURSOR_HPP__ #define __UNWINDCURSOR_HPP__ +#include "cet_unwind.h" #include #include #include @@ -449,6 +450,12 @@ #ifdef __arm__ virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); } #endif + +#if defined(_LIBUNWIND_CET_ENABLED) + virtual void *get_registers() { + _LIBUNWIND_ABORT("cet_jumpto not implemented"); + } +#endif }; #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32) @@ -901,6 +908,9 @@ virtual void saveVFPAsX(); #endif +#if defined(_LIBUNWIND_CET_ENABLED) + virtual void *get_registers() { return &_registers; } +#endif // libunwind does not and should not depend on C++ library which means that we // need our own defition of inline placement new. static void *operator new(size_t, UnwindCursor *p) { return p; } 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,40 @@ #ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND +// When CET is enabled, each "call" instruction will push return address to +// CET shadow stack, each "ret" instruction will pop current CET shadow stack +// top and compare it with target address which program will return. +// In exception handing, some stack frames will be skipped before jumping to +// landing pad and we must adjust CET shadow stack accordingly. +// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we +// directly jump to __libunwind_Registerts_x86/x86_64_jumpto instead of using +// a regular function call to avoid pushing to CET shadow stack again. +#if !defined(_LIBUNWIND_CET_ENABLED) +#define __unw_phase2_resume(cursor, fn) __unw_resume((cursor)) +#elif defined(_LIBUNWIND_TARGET_I386) +#define __unw_phase2_resume(cursor, fn) \ + do { \ + _LIBUNWIND_POP_CET_SSP(1 + (fn)); \ + void *cetRegContext = __unw_cet_get_registers((cursor)); \ + void *cetJumpAddress = __libunwind_get_cet_jump_target(); \ + __asm__ volatile("push %%edi\n\t" \ + "sub $4, %%esp\n\t" \ + "jmp *%%edx\n\t" ::"D"(cetRegContext), \ + "d"(cetJumpAddress) \ + :); \ + } while (0) +#elif defined(_LIBUNWIND_TARGET_X86_64) +#define __unw_phase2_resume(cursor, fn) \ + do { \ + _LIBUNWIND_POP_CET_SSP(1 + (fn)); \ + void *cetRegContext = __unw_cet_get_registers((cursor)); \ + void *cetJumpAddress = __libunwind_get_cet_jump_target(); \ + __asm__ volatile("jmpq *%%rdx\n\t" ::"D"(cetRegContext), \ + "d"(cetJumpAddress) \ + :); \ + } while (0) +#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 +170,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 +220,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 +260,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 +284,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 +329,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 +353,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,40 @@ +//===--------------------------- 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" +#include +#include + +// Currently, CET is implemented in x86 Linux platform. +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(__CET__) && \ + defined(__SHSTK__) && \ + (defined(_LIBUNWIND_TARGET_I386) || defined(_LIBUNWIND_TARGET_X86_64)) +#define _LIBUNWIND_CET_ENABLED 1 +#endif + +#if defined(_LIBUNWIND_CET_ENABLED) +#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_get_cet_jump_target(); +#endif Index: libunwind/src/libunwind.cpp =================================================================== --- libunwind/src/libunwind.cpp +++ libunwind/src/libunwind.cpp @@ -11,8 +11,9 @@ #include -#include "libunwind_ext.h" +#include "cet_unwind.h" #include "config.h" +#include "libunwind_ext.h" #include @@ -203,6 +204,15 @@ } _LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume) +#if defined(_LIBUNWIND_CET_ENABLED) +_LIBUNWIND_HIDDEN void *__unw_cet_get_registers(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_cet_get_registers(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->get_registers(); +} +#endif + /// Get name of function at cursor position in stack frame. _LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf, size_t bufLen, unw_word_t *offset) { Index: libunwind/src/libunwind_ext.h =================================================================== --- libunwind/src/libunwind_ext.h +++ libunwind/src/libunwind_ext.h @@ -12,6 +12,7 @@ #ifndef __LIBUNWIND_EXT__ #define __LIBUNWIND_EXT__ +#include "cet_unwind.h" #include "config.h" #include #include @@ -32,6 +33,10 @@ extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t); extern int __unw_resume(unw_cursor_t *); +#if defined(_LIBUNWIND_CET_ENABLED) +extern void *__unw_cet_get_registers(unw_cursor_t *); +#endif + #ifdef __arm__ /* Save VFP registers in FSTMX format (instead of FSTMD). */ extern void __unw_save_vfp_as_X(unw_cursor_t *); Index: libunwind/test/CMakeLists.txt =================================================================== --- libunwind/test/CMakeLists.txt +++ libunwind/test/CMakeLists.txt @@ -12,6 +12,7 @@ endif() pythonize_bool(LIBUNWIND_BUILD_32_BITS) +pythonize_bool(LIBUNWIND_ENABLE_CET) pythonize_bool(LIBCXX_ENABLE_SHARED) pythonize_bool(LIBUNWIND_ENABLE_SHARED) pythonize_bool(LIBUNWIND_ENABLE_THREADS) 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.enable_cet = @LIBUNWIND_ENABLE_CET@ site.addsitedir(os.path.join(config.libunwind_src_root, 'test')) site.addsitedir(os.path.join(config.libcxx_src_root, 'utils'))