diff --git a/libc/config/config.json b/libc/config/config.json index 9a5d5c3c68da6..408909ba6be8c 100644 --- a/libc/config/config.json +++ b/libc/config/config.json @@ -103,6 +103,10 @@ "LIBC_CONF_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER": { "value": true, "doc": "Make setjmp save the value of x18, and longjmp restore it. The AArch64 ABI delegates this register to platform ABIs, which can choose whether to make it caller-saved." + }, + "LIBC_CONF_SETJMP_FORTIFICATION": { + "value": false, + "doc": "Protect jmp_buf by masking its contents and storing a simple checksum, to make it harder for an attacker to read meaningful information from a jmp_buf or to modify it. This is only supported on x86-64 Linux." } }, "time": { diff --git a/libc/config/linux/x86_64/config.json b/libc/config/linux/x86_64/config.json new file mode 100644 index 0000000000000..1cad9f17d655e --- /dev/null +++ b/libc/config/linux/x86_64/config.json @@ -0,0 +1,8 @@ +{ + "setjmp": { + "LIBC_CONF_SETJMP_FORTIFICATION": { + "value": true, + "doc": "Protect jmp_buf by masking its contents and storing a simple checksum, to make it harder for an attacker to read meaningful information from a jmp_buf or to modify it. This is only supported on x86-64 Linux." + } + } +} diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst index 3db750b1aed21..269c3ff121844 100644 --- a/libc/docs/configure.rst +++ b/libc/docs/configure.rst @@ -55,6 +55,7 @@ to learn about the defaults for your platform and target. - ``LIBC_CONF_SCANF_DISABLE_INDEX_MODE``: Disable index mode in the scanf format string. * **"setjmp" options** - ``LIBC_CONF_SETJMP_AARCH64_RESTORE_PLATFORM_REGISTER``: Make setjmp save the value of x18, and longjmp restore it. The AArch64 ABI delegates this register to platform ABIs, which can choose whether to make it caller-saved. + - ``LIBC_CONF_SETJMP_FORTIFICATION``: Protect jmp_buf by masking its contents and storing a simple checksum, to make it harder for an attacker to read meaningful information from a jmp_buf or to modify it. This is only supported on x86-64 Linux. * **"string" options** - ``LIBC_CONF_MEMSET_X86_USE_SOFTWARE_PREFETCHING``: Inserts prefetch for write instructions (PREFETCHW) for memset on x86 to recover performance when hardware prefetcher is disabled. - ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen. diff --git a/libc/include/llvm-libc-types/jmp_buf.h b/libc/include/llvm-libc-types/jmp_buf.h index f246e6491cf55..10374ccdc8a4d 100644 --- a/libc/include/llvm-libc-types/jmp_buf.h +++ b/libc/include/llvm-libc-types/jmp_buf.h @@ -50,6 +50,8 @@ typedef struct { #else #error "__jmp_buf not available for your target architecture." #endif + // unused if checksum feature is not enabled. + __UINTPTR_TYPE__ __chksum; } __jmp_buf; typedef __jmp_buf jmp_buf[1]; diff --git a/libc/src/setjmp/CMakeLists.txt b/libc/src/setjmp/CMakeLists.txt index d85c532e8636c..aabd05bedc220 100644 --- a/libc/src/setjmp/CMakeLists.txt +++ b/libc/src/setjmp/CMakeLists.txt @@ -1,3 +1,24 @@ +if (LIBC_CONF_SETJMP_FORTIFICATION) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS}) + endif() + if (TARGET libc.src.setjmp.${LIBC_TARGET_OS}.checksum + AND LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64") + add_object_library( + checksum + ALIAS + DEPENDS + .${LIBC_TARGET_OS}.checksum + ) + set(fortification_deps libc.src.setjmp.checksum) + set(fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=1) + else() + message(WARNING "Jmpbuf fortification is enabled but not supported for target ${LIBC_TARGET_ARCHITECTURE} ${LIBC_TARGET_OS}") + set(fortification_deps) + set(fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=0) + endif() +endif() + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE}) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE}) endif() diff --git a/libc/src/setjmp/checksum.h b/libc/src/setjmp/checksum.h new file mode 100644 index 0000000000000..3acba61263e8a --- /dev/null +++ b/libc/src/setjmp/checksum.h @@ -0,0 +1,34 @@ +//===-- Implementation header for jmpbuf checksum ---------------*- C++ -*-===// +// +// 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 LLVM_LIBC_SRC_SETJMP_CHECKSUM_H +#define LLVM_LIBC_SRC_SETJMP_CHECKSUM_H + +#include "src/__support/common.h" + +namespace LIBC_NAMESPACE_DECL { +namespace jmpbuf { + +extern __UINTPTR_TYPE__ value_mask; +extern __UINTPTR_TYPE__ checksum_cookie; + +// single register update derived from aHash +// https://github.com/tkaitchuck/aHash/blob/master/src/fallback_hash.rs#L95 +// +// checksum = folded_multiple(data ^ checksum, MULTIPLE) +// folded_multiple(x, m) = HIGH(x * m) ^ LOW(x * m) + +// From Knuth's PRNG +LIBC_INLINE constexpr __UINTPTR_TYPE__ MULTIPLE = + static_cast<__UINTPTR_TYPE__>(6364136223846793005ull); +void initialize(); +extern "C" [[gnu::cold, noreturn]] void __libc_jmpbuf_corruption(); +} // namespace jmpbuf +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_SETJMP_CHECKSUM_H diff --git a/libc/src/setjmp/i386/CMakeLists.txt b/libc/src/setjmp/i386/CMakeLists.txt new file mode 100644 index 0000000000000..b5b0d9ba65599 --- /dev/null +++ b/libc/src/setjmp/i386/CMakeLists.txt @@ -0,0 +1,24 @@ +add_entrypoint_object( + setjmp + SRCS + setjmp.cpp + HDRS + ../setjmp_impl.h + DEPENDS + libc.hdr.types.jmp_buf + COMPILE_OPTIONS + -O3 +) + +add_entrypoint_object( + longjmp + SRCS + longjmp.cpp + HDRS + ../longjmp.h + DEPENDS + libc.hdr.types.jmp_buf + COMPILE_OPTIONS + -O3 + -fomit-frame-pointer +) diff --git a/libc/src/setjmp/i386/longjmp.cpp b/libc/src/setjmp/i386/longjmp.cpp new file mode 100644 index 0000000000000..fc60f3a8672a0 --- /dev/null +++ b/libc/src/setjmp/i386/longjmp.cpp @@ -0,0 +1,41 @@ +//===-- Implementation of longjmp (32-bit) --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/setjmp/longjmp.h" +#include "include/llvm-libc-macros/offsetof-macro.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" + +#if !defined(LIBC_TARGET_ARCH_IS_X86_32) +#error "Invalid file include" +#endif + +namespace LIBC_NAMESPACE_DECL { + +[[gnu::naked]] +LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) { + asm(R"( + mov 0x4(%%esp), %%ecx + mov 0x8(%%esp), %%eax + cmpl $0x1, %%eax + adcl $0x0, %%eax + + mov %c[ebx](%%ecx), %%ebx + mov %c[esi](%%ecx), %%esi + mov %c[edi](%%ecx), %%edi + mov %c[ebp](%%ecx), %%ebp + mov %c[esp](%%ecx), %%esp + + jmp *%c[eip](%%ecx) + )" ::[ebx] "i"(offsetof(__jmp_buf, ebx)), + [esi] "i"(offsetof(__jmp_buf, esi)), [edi] "i"(offsetof(__jmp_buf, edi)), + [ebp] "i"(offsetof(__jmp_buf, ebp)), [esp] "i"(offsetof(__jmp_buf, esp)), + [eip] "i"(offsetof(__jmp_buf, eip))); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/setjmp/i386/setjmp.cpp b/libc/src/setjmp/i386/setjmp.cpp new file mode 100644 index 0000000000000..5a26f5b1aed10 --- /dev/null +++ b/libc/src/setjmp/i386/setjmp.cpp @@ -0,0 +1,43 @@ +//===-- Implementation of setjmp (32-bit) ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/llvm-libc-macros/offsetof-macro.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/setjmp/setjmp_impl.h" + +#if !defined(LIBC_TARGET_ARCH_IS_X86_32) +#error "Invalid file include" +#endif + +namespace LIBC_NAMESPACE_DECL { +[[gnu::naked]] +LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) { + asm(R"( + mov 4(%%esp), %%eax + + mov %%ebx, %c[ebx](%%eax) + mov %%esi, %c[esi](%%eax) + mov %%edi, %c[edi](%%eax) + mov %%ebp, %c[ebp](%%eax) + + lea 4(%%esp), %%ecx + mov %%ecx, %c[esp](%%eax) + + mov (%%esp), %%ecx + mov %%ecx, %c[eip](%%eax) + + xorl %%eax, %%eax + retl)" ::[ebx] "i"(offsetof(__jmp_buf, ebx)), + [esi] "i"(offsetof(__jmp_buf, esi)), [edi] "i"(offsetof(__jmp_buf, edi)), + [ebp] "i"(offsetof(__jmp_buf, ebp)), [esp] "i"(offsetof(__jmp_buf, esp)), + [eip] "i"(offsetof(__jmp_buf, eip)) + : "eax", "ecx"); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/setjmp/linux/CMakeLists.txt b/libc/src/setjmp/linux/CMakeLists.txt new file mode 100644 index 0000000000000..81275cbd3340e --- /dev/null +++ b/libc/src/setjmp/linux/CMakeLists.txt @@ -0,0 +1,11 @@ +add_object_library( + checksum + SRCS + checksum.cpp + HDRS + ../checksum.h + DEPENDS + libc.src.__support.common + libc.src.__support.OSUtil.osutil + libc.src.stdlib.abort +) diff --git a/libc/src/setjmp/linux/checksum.cpp b/libc/src/setjmp/linux/checksum.cpp new file mode 100644 index 0000000000000..cb3f6f89a6bab --- /dev/null +++ b/libc/src/setjmp/linux/checksum.cpp @@ -0,0 +1,38 @@ +//===-- Implementation for jmpbuf checksum ----------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "src/setjmp/checksum.h" +#include "src/__support/OSUtil/io.h" +#include "src/stdlib/abort.h" +#include + +namespace LIBC_NAMESPACE_DECL { +namespace jmpbuf { +// random bytes from https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h +// the cookie should not be zero otherwise it will be a bad seed as a multiplier +__UINTPTR_TYPE__ value_mask = + static_cast<__UINTPTR_TYPE__>(0x3899'f0d3'5005'd953ull); +__UINTPTR_TYPE__ checksum_cookie = + static_cast<__UINTPTR_TYPE__>(0xc7d9'd341'6afc'33f2ull); + +// initialize the checksum state +void initialize() { + __UINTPTR_TYPE__ entropy[2]; + syscall_impl(SYS_getrandom, entropy, sizeof(entropy), 0); + // add in additional entropy + jmpbuf::value_mask ^= entropy[0]; + jmpbuf::checksum_cookie ^= entropy[0]; +} + +extern "C" [[gnu::cold, noreturn]] void __libc_jmpbuf_corruption() { + write_to_stderr("invalid checksum detected in longjmp\n"); + abort(); +} + +} // namespace jmpbuf +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/setjmp/x86_64/CMakeLists.txt b/libc/src/setjmp/x86_64/CMakeLists.txt index b5b0d9ba65599..df3d352696a05 100644 --- a/libc/src/setjmp/x86_64/CMakeLists.txt +++ b/libc/src/setjmp/x86_64/CMakeLists.txt @@ -1,24 +1,36 @@ +if(LIBC_CONF_SETJMP_FORTIFICATION) + set (setjmp_src setjmp_fortified.cpp) + set (longjmp_src longjmp_fortified.cpp) +else() + set (setjmp_src setjmp.cpp) + set (longjmp_src longjmp.cpp) +endif() + add_entrypoint_object( setjmp SRCS - setjmp.cpp + ${setjmp_src} HDRS ../setjmp_impl.h DEPENDS libc.hdr.types.jmp_buf + ${fortification_deps} COMPILE_OPTIONS -O3 + ${fortification_defs} ) add_entrypoint_object( longjmp SRCS - longjmp.cpp + ${longjmp_src} HDRS ../longjmp.h DEPENDS libc.hdr.types.jmp_buf + ${fortification_deps} COMPILE_OPTIONS -O3 -fomit-frame-pointer + ${fortification_defs} ) diff --git a/libc/src/setjmp/x86_64/longjmp.cpp b/libc/src/setjmp/x86_64/longjmp.cpp index 143c9deb11e9a..f30c752d2e71a 100644 --- a/libc/src/setjmp/x86_64/longjmp.cpp +++ b/libc/src/setjmp/x86_64/longjmp.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of longjmp -----------------------------------------===// +//===-- Implementation of longjmp (64-bit) --------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,34 +11,12 @@ #include "src/__support/common.h" #include "src/__support/macros/config.h" -#if !defined(LIBC_TARGET_ARCH_IS_X86) +#if !defined(LIBC_TARGET_ARCH_IS_X86_64) || LIBC_COPT_SETJMP_FORTIFICATION #error "Invalid file include" #endif namespace LIBC_NAMESPACE_DECL { -#ifdef __i386__ -[[gnu::naked]] -LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) { - asm(R"( - mov 0x4(%%esp), %%ecx - mov 0x8(%%esp), %%eax - cmpl $0x1, %%eax - adcl $0x0, %%eax - - mov %c[ebx](%%ecx), %%ebx - mov %c[esi](%%ecx), %%esi - mov %c[edi](%%ecx), %%edi - mov %c[ebp](%%ecx), %%ebp - mov %c[esp](%%ecx), %%esp - - jmp *%c[eip](%%ecx) - )" ::[ebx] "i"(offsetof(__jmp_buf, ebx)), - [esi] "i"(offsetof(__jmp_buf, esi)), [edi] "i"(offsetof(__jmp_buf, edi)), - [ebp] "i"(offsetof(__jmp_buf, ebp)), [esp] "i"(offsetof(__jmp_buf, esp)), - [eip] "i"(offsetof(__jmp_buf, eip))); -} -#else [[gnu::naked]] LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) { asm(R"( @@ -60,6 +38,5 @@ LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) { [r15] "i"(offsetof(__jmp_buf, r15)), [rsp] "i"(offsetof(__jmp_buf, rsp)), [rip] "i"(offsetof(__jmp_buf, rip))); } -#endif } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/setjmp/x86_64/longjmp_fortified.cpp b/libc/src/setjmp/x86_64/longjmp_fortified.cpp new file mode 100644 index 0000000000000..27c8d9a9e6179 --- /dev/null +++ b/libc/src/setjmp/x86_64/longjmp_fortified.cpp @@ -0,0 +1,95 @@ +//===-- Implementation of longjmp (64-bit) --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/llvm-libc-macros/offsetof-macro.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/setjmp/longjmp.h" + +#if !defined(LIBC_TARGET_ARCH_IS_X86_64) || !LIBC_COPT_SETJMP_FORTIFICATION +#error "Invalid file include" +#endif + +#include "src/setjmp/checksum.h" + +namespace LIBC_NAMESPACE_DECL { + +LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) { + asm(R"( + mov %c[rbx](%%rdi), %%rbx + xor %%rbx, %[cookie] + xor %[mask], %%rbx + mul %[multiple] + xor %%rdx, %[cookie] + + mov %c[rbp](%%rdi), %%rbp + xor %%rbp, %[cookie] + xor %[mask], %%rbp + mul %[multiple] + xor %%rdx, %[cookie] + + mov %c[r12](%%rdi), %%r12 + xor %%r12, %[cookie] + xor %[mask], %%r12 + mul %[multiple] + xor %%rdx, %[cookie] + + mov %c[r13](%%rdi), %%r13 + xor %%r13, %[cookie] + xor %[mask], %%r13 + mul %[multiple] + xor %%rdx, %[cookie] + + mov %c[r14](%%rdi), %%r14 + xor %%r14, %[cookie] + xor %[mask], %%r14 + mul %[multiple] + xor %%rdx, %[cookie] + + mov %c[r15](%%rdi), %%r15 + xor %%r15, %[cookie] + xor %[mask], %%r15 + mul %[multiple] + xor %%rdx, %[cookie] + + mov %c[rsp](%%rdi), %%rsp + xor %%rsp, %[cookie] + xor %[mask], %%rsp + mul %[multiple] + xor %%rdx, %[cookie] + + # move multiplication factor (which should be in rcx) to rdx + # free up rcx for PC recovery + mov %[multiple], %%rdx + mov %c[rip](%%rdi), %%rcx + xor %%rcx, %[cookie] + xor %[mask], %%rcx + mul %%rdx + xor %%rdx, %[cookie] + + cmp %c[chksum](%%rdi), %[cookie] + jne __libc_jmpbuf_corruption + + # from this point, rax does not stand for accumulator but for return value + cmpl $0x1, %%esi + adcl $0x0, %%esi + movq %%rsi, %%rax + + jmpq *%%rcx + )" ::[rbx] "i"(offsetof(__jmp_buf, rbx)), + [rbp] "i"(offsetof(__jmp_buf, rbp)), [r12] "i"(offsetof(__jmp_buf, r12)), + [r13] "i"(offsetof(__jmp_buf, r13)), [r14] "i"(offsetof(__jmp_buf, r14)), + [r15] "i"(offsetof(__jmp_buf, r15)), [rsp] "i"(offsetof(__jmp_buf, rsp)), + [rip] "i"(offsetof(__jmp_buf, rip)), + [chksum] "i"(offsetof(__jmp_buf, __chksum)), + [multiple] "c"(jmpbuf::MULTIPLE), [cookie] "a"(jmpbuf::checksum_cookie), + [mask] "m"(jmpbuf::value_mask) + :); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/setjmp/x86_64/setjmp.cpp b/libc/src/setjmp/x86_64/setjmp.cpp index 5ac10fa87b39a..82ff509a678f0 100644 --- a/libc/src/setjmp/x86_64/setjmp.cpp +++ b/libc/src/setjmp/x86_64/setjmp.cpp @@ -1,4 +1,4 @@ -//===-- Implementation of setjmp ------------------------------------------===// +//===-- Implementation of setjmp (64-bit) ---------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. @@ -11,37 +11,12 @@ #include "src/__support/macros/config.h" #include "src/setjmp/setjmp_impl.h" -#if !defined(LIBC_TARGET_ARCH_IS_X86) +#if !defined(LIBC_TARGET_ARCH_IS_X86_64) || LIBC_COPT_SETJMP_FORTIFICATION #error "Invalid file include" #endif namespace LIBC_NAMESPACE_DECL { -#ifdef __i386__ -[[gnu::naked]] -LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) { - asm(R"( - mov 4(%%esp), %%eax - - mov %%ebx, %c[ebx](%%eax) - mov %%esi, %c[esi](%%eax) - mov %%edi, %c[edi](%%eax) - mov %%ebp, %c[ebp](%%eax) - - lea 4(%%esp), %%ecx - mov %%ecx, %c[esp](%%eax) - - mov (%%esp), %%ecx - mov %%ecx, %c[eip](%%eax) - - xorl %%eax, %%eax - retl)" ::[ebx] "i"(offsetof(__jmp_buf, ebx)), - [esi] "i"(offsetof(__jmp_buf, esi)), [edi] "i"(offsetof(__jmp_buf, edi)), - [ebp] "i"(offsetof(__jmp_buf, ebp)), [esp] "i"(offsetof(__jmp_buf, esp)), - [eip] "i"(offsetof(__jmp_buf, eip)) - : "eax", "ecx"); -} -#else [[gnu::naked]] LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) { asm(R"( @@ -66,6 +41,4 @@ LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) { [rip] "i"(offsetof(__jmp_buf, rip)) : "rax"); } -#endif - } // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/setjmp/x86_64/setjmp_fortified.cpp b/libc/src/setjmp/x86_64/setjmp_fortified.cpp new file mode 100644 index 0000000000000..155b836413368 --- /dev/null +++ b/libc/src/setjmp/x86_64/setjmp_fortified.cpp @@ -0,0 +1,96 @@ +//===-- Implementation of setjmp (64-bit) ---------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "include/llvm-libc-macros/offsetof-macro.h" +#include "src/__support/common.h" +#include "src/__support/macros/config.h" +#include "src/setjmp/setjmp_impl.h" + +#if !defined(LIBC_TARGET_ARCH_IS_X86_64) || !LIBC_COPT_SETJMP_FORTIFICATION +#error "Invalid file include" +#endif + +#include "src/setjmp/checksum.h" + +namespace LIBC_NAMESPACE_DECL { + +[[gnu::naked]] +LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) { + asm(R"( + mov %%rbx, %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[rbx](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov %%rbp, %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[rbp](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov %%r12, %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[r12](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov %%r13, %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[r13](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov %%r14, %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[r14](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov %%r15, %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[r15](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + lea 8(%%rsp), %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[rsp](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov (%%rsp), %%rdx + xor %[mask], %%rdx + mov %%rdx, %c[rip](%%rdi) + xor %%rdx, %[cookie] + mul %[multiple] + xor %%rdx, %[cookie] + + mov %[cookie], %c[chksum](%%rdi) + + # from this point, rax does not stand for accumulator but for return value + xor %%rax, %%rax + ret)" ::[rbx] "i"(offsetof(__jmp_buf, rbx)), + [rbp] "i"(offsetof(__jmp_buf, rbp)), [r12] "i"(offsetof(__jmp_buf, r12)), + [r13] "i"(offsetof(__jmp_buf, r13)), [r14] "i"(offsetof(__jmp_buf, r14)), + [r15] "i"(offsetof(__jmp_buf, r15)), [rsp] "i"(offsetof(__jmp_buf, rsp)), + [rip] "i"(offsetof(__jmp_buf, rip)), + [chksum] "i"(offsetof(__jmp_buf, __chksum)), + [multiple] "c"(jmpbuf::MULTIPLE), [cookie] "a"(jmpbuf::checksum_cookie), + [mask] "S"(jmpbuf::value_mask) + :); +} + +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/startup/linux/CMakeLists.txt b/libc/startup/linux/CMakeLists.txt index eaa724e41f168..245dfca5b3ed3 100644 --- a/libc/startup/linux/CMakeLists.txt +++ b/libc/startup/linux/CMakeLists.txt @@ -88,6 +88,14 @@ endif() add_subdirectory(${LIBC_TARGET_ARCHITECTURE}) +if (LIBC_CONF_SETJMP_FORTIFICATION AND LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64") + set(jmpbuf_fortification_deps libc.src.setjmp.checksum) + set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=1) +else() + set(jmpbuf_fortification_deps) + set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=0) +endif() + add_object_library( do_start SRCS @@ -104,10 +112,12 @@ add_object_library( libc.src.stdlib.exit libc.src.stdlib.atexit libc.src.unistd.environ + ${jmpbuf_fortification_deps} COMPILE_OPTIONS -ffreestanding # To avoid compiler warnings about calling the main function. -fno-builtin # avoid emit unexpected calls -fno-stack-protector # stack protect canary is not available yet. + ${jmpbuf_fortification_defs} ) # TODO: factor out crt1 into multiple objects diff --git a/libc/startup/linux/do_start.cpp b/libc/startup/linux/do_start.cpp index ff104c7f0d1d2..ded94768f0b92 100644 --- a/libc/startup/linux/do_start.cpp +++ b/libc/startup/linux/do_start.cpp @@ -21,6 +21,10 @@ #include #include +#if LIBC_COPT_SETJMP_FORTIFICATION +#include "src/setjmp/checksum.h" +#endif + extern "C" int main(int argc, char **argv, char **envp); extern "C" { @@ -131,6 +135,10 @@ void teardown_main_tls() { cleanup_tls(tls.addr, tls.size); } if (tls.size != 0 && !set_thread_ptr(tls.tp)) syscall_impl(SYS_exit, 1); +#if LIBC_COPT_SETJMP_FORTIFICATION + jmpbuf::initialize(); +#endif + self.attrib = &main_thread_attrib; main_thread_attrib.atexit_callback_mgr = internal::get_thread_atexit_callback_mgr();