[libc-commits] [libc] [libc] fortify jmp buffer for x86-64 (PR #112769)
Schrodinger ZHU Yifan via libc-commits
libc-commits at lists.llvm.org
Fri Oct 18 10:49:24 PDT 2024
https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/112769
>From 3a226f14d697d4619faa82fcaf6028236d5c4eba Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 17 Oct 2024 15:52:21 -0400
Subject: [PATCH 1/6] [libc] fortify jmp buffer
---
libc/config/config.json | 4 ++
libc/docs/configure.rst | 1 +
libc/include/llvm-libc-types/jmp_buf.h | 2 +
libc/src/setjmp/CMakeLists.txt | 18 ++++++
libc/src/setjmp/checksum.h | 46 ++++++++++++++
libc/src/setjmp/x86_64/CMakeLists.txt | 4 ++
libc/src/setjmp/x86_64/longjmp.cpp | 78 ++++++++++++++++++-----
libc/src/setjmp/x86_64/setjmp.cpp | 85 ++++++++++++++++++++------
libc/startup/linux/CMakeLists.txt | 1 +
libc/startup/linux/do_start.cpp | 3 +-
10 files changed, 209 insertions(+), 33 deletions(-)
create mode 100644 libc/src/setjmp/checksum.h
diff --git a/libc/config/config.json b/libc/config/config.json
index 2e4f878778e6e0..88110ab29152c3 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -99,6 +99,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_ENABLE_FORTIFICATION": {
+ "value": true,
+ "doc": "Enable fortification for setjmp and longjmp."
}
},
"time": {
diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 867bb807d10ac9..2704b47f13cb36 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -54,6 +54,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_ENABLE_FORTIFICATION``: Enable fortification for setjmp and longjmp.
* **"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 60e033c6c65a95..c89bfc5afcbd3b 100644
--- a/libc/include/llvm-libc-types/jmp_buf.h
+++ b/libc/include/llvm-libc-types/jmp_buf.h
@@ -43,6 +43,8 @@ typedef struct {
#else
#error "__jmp_buf not available for your target architecture."
#endif
+ // unused if checksum feature is not enabled.
+ __UINT64_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 d85c532e8636c8..a90c3e177fb739 100644
--- a/libc/src/setjmp/CMakeLists.txt
+++ b/libc/src/setjmp/CMakeLists.txt
@@ -2,6 +2,24 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
endif()
+if (LIBC_CONF_SETJMP_ENABLE_FORTIFICATION)
+ set(checksum_flags "-DLIBC_SETJMP_ENABLE_FORTIFICATION=1")
+else()
+ set(checksum_flags "-DLIBC_SETJMP_ENABLE_FORTIFICATION=0")
+endif()
+
+
+add_header_library(
+ checksum
+ HDRS
+ checksum.h
+ DEPENDS
+ libc.src.__support.common
+ libc.src.__support.OSUtil.osutil
+ COMPILE_OPTIONS
+ ${checksum_flags}
+)
+
add_entrypoint_object(
setjmp
ALIAS
diff --git a/libc/src/setjmp/checksum.h b/libc/src/setjmp/checksum.h
new file mode 100644
index 00000000000000..df6a12d185c3ee
--- /dev/null
+++ b/libc/src/setjmp/checksum.h
@@ -0,0 +1,46 @@
+//===-- 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
+
+#ifndef LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#define LIBC_COPT_SETJMP_ENABLE_FORTIFICATION 1
+#endif
+
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#include "src/__support/OSUtil/syscall.h"
+#include "src/__support/macros/config.h"
+#include <sys/syscall.h>
+
+namespace LIBC_NAMESPACE_DECL {
+namespace jmpbuf {
+// random bytes from https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h
+LIBC_INLINE __UINTPTR_TYPE__ value_mask = 0x3899'f0d3'5005'd953;
+LIBC_INLINE __UINT64_TYPE__ checksum_cookie = 0xc7d9'd341'6afc'33f2;
+// abitrary prime number
+LIBC_INLINE constexpr __UINT64_TYPE__ ROTATION = 13;
+// initialize the checksum state
+LIBC_INLINE void initialize() {
+ union {
+ struct {
+ __UINTPTR_TYPE__ entropy0;
+ __UINT64_TYPE__ entropy1;
+ };
+ char buffer[sizeof(__UINTPTR_TYPE__) + sizeof(__UINT64_TYPE__)];
+ };
+ syscall_impl<long>(SYS_getrandom, buffer, sizeof(buffer), 0);
+ // add in additional entropy
+ jmpbuf::value_mask ^= entropy0;
+ jmpbuf::checksum_cookie ^= entropy1;
+}
+} // namespace jmpbuf
+} // namespace LIBC_NAMESPACE_DECL
+#endif // LIBC_SETJMP_ENABLE_FORTIFICATION
+
+#endif // LLVM_LIBC_SRC_SETJMP_CHECKSUM_H
diff --git a/libc/src/setjmp/x86_64/CMakeLists.txt b/libc/src/setjmp/x86_64/CMakeLists.txt
index b5b0d9ba65599c..571f3681887106 100644
--- a/libc/src/setjmp/x86_64/CMakeLists.txt
+++ b/libc/src/setjmp/x86_64/CMakeLists.txt
@@ -6,6 +6,7 @@ add_entrypoint_object(
../setjmp_impl.h
DEPENDS
libc.hdr.types.jmp_buf
+ libc.src.setjmp.checksum
COMPILE_OPTIONS
-O3
)
@@ -18,6 +19,9 @@ add_entrypoint_object(
../longjmp.h
DEPENDS
libc.hdr.types.jmp_buf
+ libc.src.stdlib.abort
+ libc.src.__support.OSUtil.osutil
+ libc.src.setjmp.checksum
COMPILE_OPTIONS
-O3
-fomit-frame-pointer
diff --git a/libc/src/setjmp/x86_64/longjmp.cpp b/libc/src/setjmp/x86_64/longjmp.cpp
index c293c55a6f9fb2..09aa189f2b87bd 100644
--- a/libc/src/setjmp/x86_64/longjmp.cpp
+++ b/libc/src/setjmp/x86_64/longjmp.cpp
@@ -8,8 +8,11 @@
#include "src/setjmp/longjmp.h"
#include "include/llvm-libc-macros/offsetof-macro.h"
+#include "src/__support/OSUtil/io.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/setjmp/checksum.h"
+#include "src/stdlib/abort.h"
#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
#error "Invalid file include"
@@ -17,26 +20,71 @@
namespace LIBC_NAMESPACE_DECL {
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+extern "C" [[gnu::cold, noreturn]] void __libc_jmpbuf_corruption() {
+ write_to_stderr("invalid checksum detected in longjmp\n");
+ abort();
+}
+#define LOAD_CHKSUM_STATE_REGISTERS() \
+ asm("mov %0, %%rcx\n\t" ::"m"(jmpbuf::value_mask) : "rcx"); \
+ asm("mov %0, %%rdx\n\t" ::"m"(jmpbuf::checksum_cookie) : "rdx");
+
+#define RESTORE_REG(DST) \
+ "movq %c[" #DST "](%%rdi), %%rax\n\t" \
+ "movq %%rax, %%" #DST "\n\t" \
+ "xor %%rcx, %%" #DST "\n\t" \
+ "mul %%rdx\n\t" \
+ "xor %%rax, %%rdx\n\t" \
+ "rol $%c[rotation], %%rdx\n\t"
+
+#define RESTORE_RIP() \
+ "movq %c[rip](%%rdi), %%rax\n\t" \
+ "xor %%rax, %%rcx\n\t" \
+ "mul %%rdx\n\t" \
+ "xor %%rax, %%rdx\n\t" \
+ "rol $%c[rotation], %%rdx\n\t" \
+ "cmp %c[chksum](%%rdi), %%rdx\n\t" \
+ "jne __libc_jmpbuf_corruption\n\t" \
+ "cmpl $0x1, %%esi\n\t" \
+ "adcl $0x0, %%esi\n\t" \
+ "movq %%rsi, %%rax\n\t" \
+ "jmp *%%rcx\n\t"
+#else
+#define LOAD_CHKSUM_STATE_REGISTERS()
+#define RESTORE_REG(DST) "movq %c[" #DST "](%%rdi), %%" #DST "\n\t"
+#define RESTORE_RIP() \
+ "cmpl $0x1, %%esi\n\t" \
+ "adcl $0x0, %%esi\n\t" \
+ "movq %%rsi, %%rax\n\t" \
+ "jmpq *%c[rip](%%rdi)\n\t"
+#endif
+
[[gnu::naked]]
LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
- asm(R"(
- cmpl $0x1, %%esi
- adcl $0x0, %%esi
- movq %%rsi, %%rax
-
- movq %c[rbx](%%rdi), %%rbx
- movq %c[rbp](%%rdi), %%rbp
- movq %c[r12](%%rdi), %%r12
- movq %c[r13](%%rdi), %%r13
- movq %c[r14](%%rdi), %%r14
- movq %c[r15](%%rdi), %%r15
- movq %c[rsp](%%rdi), %%rsp
- jmpq *%c[rip](%%rdi)
- )" ::[rbx] "i"(offsetof(__jmp_buf, rbx)),
+ LOAD_CHKSUM_STATE_REGISTERS()
+ asm(
+ // clang-format off
+ RESTORE_REG(rbx)
+ RESTORE_REG(rbp)
+ RESTORE_REG(r12)
+ RESTORE_REG(r13)
+ RESTORE_REG(r14)
+ RESTORE_REG(r15)
+ RESTORE_REG(rsp)
+ RESTORE_RIP()
+ // clang-format on
+ ::[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)));
+ [rip] "i"(offsetof(__jmp_buf, rip))
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+ // clang-format off
+ ,[rotation] "i"(jmpbuf::ROTATION)
+ , [chksum] "i"(offsetof(__jmp_buf, __chksum))
+ // clang-format on
+#endif
+ : "rax", "rdx", "rcx", "rsi");
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/setjmp/x86_64/setjmp.cpp b/libc/src/setjmp/x86_64/setjmp.cpp
index f6e82642edd7da..56e2f55cd3a9ff 100644
--- a/libc/src/setjmp/x86_64/setjmp.cpp
+++ b/libc/src/setjmp/x86_64/setjmp.cpp
@@ -9,37 +9,88 @@
#include "include/llvm-libc-macros/offsetof-macro.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+#include "src/setjmp/checksum.h"
#include "src/setjmp/setjmp_impl.h"
#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
#error "Invalid file include"
#endif
-namespace LIBC_NAMESPACE_DECL {
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#define LOAD_CHKSUM_STATE_REGISTERS() \
+ asm("mov %0, %%rcx\n\t" ::"m"(jmpbuf::value_mask) : "rcx"); \
+ asm("mov %0, %%rdx\n\t" ::"m"(jmpbuf::checksum_cookie) : "rdx");
-[[gnu::naked]]
-LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
- asm(R"(
- mov %%rbx, %c[rbx](%%rdi)
- mov %%rbp, %c[rbp](%%rdi)
- mov %%r12, %c[r12](%%rdi)
- mov %%r13, %c[r13](%%rdi)
- mov %%r14, %c[r14](%%rdi)
- mov %%r15, %c[r15](%%rdi)
+#define STORE_REG(SRC) \
+ "mov %%" #SRC ", %%rax\n\t" \
+ "xor %%rcx, %%rax\n\t" \
+ "mov %%rax, %c[" #SRC "](%%rdi)\n\t" \
+ "mul %%rdx\n\t" \
+ "xor %%rax, %%rdx\n\t" \
+ "rol $%c[rotation], %%rdx\n\t"
+
+#define STORE_RSP() \
+ "lea 8(%%rsp), %%rax\n\t" \
+ "xor %%rcx, %%rax\n\t" \
+ "mov %%rax, %c[rsp](%%rdi)\n\t" \
+ "mul %%rdx\n\t" \
+ "xor %%rax, %%rdx\n\t" \
+ "rolq $%c[rotation], %%rdx\n\t"
- lea 8(%%rsp), %%rax
- mov %%rax, %c[rsp](%%rdi)
+#define STORE_RIP() \
+ "mov (%%rsp), %%rax\n\t" \
+ "xor %%rcx, %%rax\n\t" \
+ "mov %%rax, %c[rip](%%rdi)\n\t" \
+ "mul %%rdx\n\t" \
+ "xor %%rax, %%rdx\n\t" \
+ "rolq $%c[rotation], %%rdx\n\t"
- mov (%%rsp), %%rax
- mov %%rax, %c[rip](%%rdi)
+#define STORE_CHECKSUM() "mov %%rdx, %c[chksum](%%rdi)\n\t"
+#else
+#define LOAD_CHKSUM_STATE_REGISTERS()
+#define STORE_REG(SRC) "mov %%" #SRC ", %c[" #SRC "](%%rdi)\n\t"
+#define STORE_RSP() \
+ "lea 8(%%rsp), %%rax\n\t" \
+ "mov %%rax, %c[rsp](%%rdi)\n\t"
+#define STORE_RIP() \
+ "mov (%%rsp), %%rax\n\t" \
+ "mov %%rax, %c[rip](%%rdi)\n\t"
+#define STORE_CHECKSUM()
+#endif
- xorl %%eax, %%eax
- retq)" ::[rbx] "i"(offsetof(__jmp_buf, rbx)),
+namespace LIBC_NAMESPACE_DECL {
+[[gnu::naked]]
+LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
+ LOAD_CHKSUM_STATE_REGISTERS()
+ asm(
+ // clang-format off
+ STORE_REG(rbx)
+ STORE_REG(rbp)
+ STORE_REG(r12)
+ STORE_REG(r13)
+ STORE_REG(r14)
+ STORE_REG(r15)
+ STORE_RSP()
+ STORE_RIP()
+ STORE_CHECKSUM()
+ // clang-format on
+ ::[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))
- : "rax");
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+ // clang-format off
+ ,[rotation] "i"(jmpbuf::ROTATION)
+ ,[chksum] "i"(offsetof(__jmp_buf, __chksum))
+ // clang-format on
+#endif
+ : "rax", "rdx");
+
+ asm(R"(
+ xorl %eax, %eax
+ retq
+ )");
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/startup/linux/CMakeLists.txt b/libc/startup/linux/CMakeLists.txt
index eaa724e41f1685..34c870f98b1f1c 100644
--- a/libc/startup/linux/CMakeLists.txt
+++ b/libc/startup/linux/CMakeLists.txt
@@ -104,6 +104,7 @@ add_object_library(
libc.src.stdlib.exit
libc.src.stdlib.atexit
libc.src.unistd.environ
+ libc.src.setjmp.checksum
COMPILE_OPTIONS
-ffreestanding # To avoid compiler warnings about calling the main function.
-fno-builtin # avoid emit unexpected calls
diff --git a/libc/startup/linux/do_start.cpp b/libc/startup/linux/do_start.cpp
index ff104c7f0d1d2f..fc40ac0b1b1095 100644
--- a/libc/startup/linux/do_start.cpp
+++ b/libc/startup/linux/do_start.cpp
@@ -11,6 +11,7 @@
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/thread.h"
+#include "src/setjmp/checksum.h"
#include "src/stdlib/atexit.h"
#include "src/stdlib/exit.h"
#include "src/unistd/environ.h"
@@ -130,7 +131,7 @@ void teardown_main_tls() { cleanup_tls(tls.addr, tls.size); }
init_tls(tls);
if (tls.size != 0 && !set_thread_ptr(tls.tp))
syscall_impl<long>(SYS_exit, 1);
-
+ jmpbuf::initialize();
self.attrib = &main_thread_attrib;
main_thread_attrib.atexit_callback_mgr =
internal::get_thread_atexit_callback_mgr();
>From 64f571e82b8ab543ec916a656d5223d380d86f20 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 17 Oct 2024 15:54:19 -0400
Subject: [PATCH 2/6] [libc] fortify jmp buffer
---
libc/src/setjmp/CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libc/src/setjmp/CMakeLists.txt b/libc/src/setjmp/CMakeLists.txt
index a90c3e177fb739..6ceaaa8dd3a3f7 100644
--- a/libc/src/setjmp/CMakeLists.txt
+++ b/libc/src/setjmp/CMakeLists.txt
@@ -3,9 +3,9 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
endif()
if (LIBC_CONF_SETJMP_ENABLE_FORTIFICATION)
- set(checksum_flags "-DLIBC_SETJMP_ENABLE_FORTIFICATION=1")
+ set(checksum_flags "-DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=1")
else()
- set(checksum_flags "-DLIBC_SETJMP_ENABLE_FORTIFICATION=0")
+ set(checksum_flags "-DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=0")
endif()
>From 925b79f81d3b3adafdbbf48c1d1e600e812fc2e5 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Thu, 17 Oct 2024 15:55:57 -0400
Subject: [PATCH 3/6] [libc] fortify jmp buffer
---
libc/src/setjmp/x86_64/longjmp.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libc/src/setjmp/x86_64/longjmp.cpp b/libc/src/setjmp/x86_64/longjmp.cpp
index 09aa189f2b87bd..61bd1f728ade9e 100644
--- a/libc/src/setjmp/x86_64/longjmp.cpp
+++ b/libc/src/setjmp/x86_64/longjmp.cpp
@@ -81,7 +81,7 @@ LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
// clang-format off
,[rotation] "i"(jmpbuf::ROTATION)
- , [chksum] "i"(offsetof(__jmp_buf, __chksum))
+ ,[chksum] "i"(offsetof(__jmp_buf, __chksum))
// clang-format on
#endif
: "rax", "rdx", "rcx", "rsi");
>From 63ce9bd067e7e67495008e7169dac1ec5fc3cb8a Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 18 Oct 2024 10:05:03 -0400
Subject: [PATCH 4/6] [libc] fix startup
---
libc/startup/linux/do_start.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/libc/startup/linux/do_start.cpp b/libc/startup/linux/do_start.cpp
index fc40ac0b1b1095..55d85c90fca44b 100644
--- a/libc/startup/linux/do_start.cpp
+++ b/libc/startup/linux/do_start.cpp
@@ -131,7 +131,11 @@ void teardown_main_tls() { cleanup_tls(tls.addr, tls.size); }
init_tls(tls);
if (tls.size != 0 && !set_thread_ptr(tls.tp))
syscall_impl<long>(SYS_exit, 1);
+
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
jmpbuf::initialize();
+#endif
+
self.attrib = &main_thread_attrib;
main_thread_attrib.atexit_callback_mgr =
internal::get_thread_atexit_callback_mgr();
>From 1ea91ad0e3cd2f037cd9026e4c69b01f3423d757 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 18 Oct 2024 11:24:34 -0400
Subject: [PATCH 5/6] [libc] address CRs
---
libc/config/config.json | 2 +-
libc/docs/configure.rst | 2 +-
libc/src/setjmp/CMakeLists.txt | 36 +++++++------
libc/src/setjmp/checksum.h | 33 +++---------
libc/src/setjmp/linux/CMakeLists.txt | 11 ++++
libc/src/setjmp/linux/checksum.cpp | 42 +++++++++++++++
libc/src/setjmp/x86_64/CMakeLists.txt | 8 +--
libc/src/setjmp/x86_64/longjmp.cpp | 78 +++++++++++++++------------
libc/src/setjmp/x86_64/setjmp.cpp | 63 +++++++++++++---------
libc/startup/linux/CMakeLists.txt | 11 +++-
libc/startup/linux/do_start.cpp | 5 +-
11 files changed, 182 insertions(+), 109 deletions(-)
create mode 100644 libc/src/setjmp/linux/CMakeLists.txt
create mode 100644 libc/src/setjmp/linux/checksum.cpp
diff --git a/libc/config/config.json b/libc/config/config.json
index 88110ab29152c3..941b888f047b8f 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -102,7 +102,7 @@
},
"LIBC_CONF_SETJMP_ENABLE_FORTIFICATION": {
"value": true,
- "doc": "Enable fortification for setjmp and longjmp."
+ "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/docs/configure.rst b/libc/docs/configure.rst
index 2704b47f13cb36..30cf375f32b7db 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -54,7 +54,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_ENABLE_FORTIFICATION``: Enable fortification for setjmp and longjmp.
+ - ``LIBC_CONF_SETJMP_ENABLE_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/src/setjmp/CMakeLists.txt b/libc/src/setjmp/CMakeLists.txt
index 6ceaaa8dd3a3f7..21ac0b6ba52e25 100644
--- a/libc/src/setjmp/CMakeLists.txt
+++ b/libc/src/setjmp/CMakeLists.txt
@@ -1,24 +1,28 @@
-if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
- add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
endif()
if (LIBC_CONF_SETJMP_ENABLE_FORTIFICATION)
- set(checksum_flags "-DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=1")
-else()
- set(checksum_flags "-DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=0")
+ 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_ENABLE_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_ENABLE_FORTIFICATION=0)
+ endif()
endif()
-
-add_header_library(
- checksum
- HDRS
- checksum.h
- DEPENDS
- libc.src.__support.common
- libc.src.__support.OSUtil.osutil
- COMPILE_OPTIONS
- ${checksum_flags}
-)
+if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
+endif()
add_entrypoint_object(
setjmp
diff --git a/libc/src/setjmp/checksum.h b/libc/src/setjmp/checksum.h
index df6a12d185c3ee..1c443629426743 100644
--- a/libc/src/setjmp/checksum.h
+++ b/libc/src/setjmp/checksum.h
@@ -9,38 +9,19 @@
#ifndef LLVM_LIBC_SRC_SETJMP_CHECKSUM_H
#define LLVM_LIBC_SRC_SETJMP_CHECKSUM_H
-#ifndef LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
-#define LIBC_COPT_SETJMP_ENABLE_FORTIFICATION 1
-#endif
-
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
-#include "src/__support/OSUtil/syscall.h"
-#include "src/__support/macros/config.h"
-#include <sys/syscall.h>
+#include "src/__support/common.h"
namespace LIBC_NAMESPACE_DECL {
namespace jmpbuf {
-// random bytes from https://www.random.org/cgi-bin/randbyte?nbytes=8&format=h
-LIBC_INLINE __UINTPTR_TYPE__ value_mask = 0x3899'f0d3'5005'd953;
-LIBC_INLINE __UINT64_TYPE__ checksum_cookie = 0xc7d9'd341'6afc'33f2;
+
+extern __UINTPTR_TYPE__ value_mask;
+extern __UINT64_TYPE__ checksum_cookie;
+
// abitrary prime number
LIBC_INLINE constexpr __UINT64_TYPE__ ROTATION = 13;
-// initialize the checksum state
-LIBC_INLINE void initialize() {
- union {
- struct {
- __UINTPTR_TYPE__ entropy0;
- __UINT64_TYPE__ entropy1;
- };
- char buffer[sizeof(__UINTPTR_TYPE__) + sizeof(__UINT64_TYPE__)];
- };
- syscall_impl<long>(SYS_getrandom, buffer, sizeof(buffer), 0);
- // add in additional entropy
- jmpbuf::value_mask ^= entropy0;
- jmpbuf::checksum_cookie ^= entropy1;
-}
+void initialize();
+extern "C" [[gnu::cold, noreturn]] void __libc_jmpbuf_corruption();
} // namespace jmpbuf
} // namespace LIBC_NAMESPACE_DECL
-#endif // LIBC_SETJMP_ENABLE_FORTIFICATION
#endif // LLVM_LIBC_SRC_SETJMP_CHECKSUM_H
diff --git a/libc/src/setjmp/linux/CMakeLists.txt b/libc/src/setjmp/linux/CMakeLists.txt
new file mode 100644
index 00000000000000..81275cbd3340e4
--- /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 00000000000000..d23c2aca05df38
--- /dev/null
+++ b/libc/src/setjmp/linux/checksum.cpp
@@ -0,0 +1,42 @@
+//===-- 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 <sys/syscall.h>
+
+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 = 0x3899'f0d3'5005'd953;
+__UINT64_TYPE__ checksum_cookie = 0xc7d9'd341'6afc'33f2;
+
+// initialize the checksum state
+void initialize() {
+ union {
+ struct {
+ __UINTPTR_TYPE__ entropy0;
+ __UINT64_TYPE__ entropy1;
+ };
+ char buffer[sizeof(__UINTPTR_TYPE__) + sizeof(__UINT64_TYPE__)];
+ };
+ syscall_impl<long>(SYS_getrandom, buffer, sizeof(buffer), 0);
+ // add in additional entropy
+ jmpbuf::value_mask ^= entropy0;
+ jmpbuf::checksum_cookie ^= entropy1;
+}
+
+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 571f3681887106..06100be5c1b6f8 100644
--- a/libc/src/setjmp/x86_64/CMakeLists.txt
+++ b/libc/src/setjmp/x86_64/CMakeLists.txt
@@ -6,9 +6,10 @@ add_entrypoint_object(
../setjmp_impl.h
DEPENDS
libc.hdr.types.jmp_buf
- libc.src.setjmp.checksum
+ ${fortification_deps}
COMPILE_OPTIONS
-O3
+ ${fortification_defs}
)
add_entrypoint_object(
@@ -19,10 +20,9 @@ add_entrypoint_object(
../longjmp.h
DEPENDS
libc.hdr.types.jmp_buf
- libc.src.stdlib.abort
- libc.src.__support.OSUtil.osutil
- libc.src.setjmp.checksum
+ ${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 61bd1f728ade9e..83e2538bea890a 100644
--- a/libc/src/setjmp/x86_64/longjmp.cpp
+++ b/libc/src/setjmp/x86_64/longjmp.cpp
@@ -8,11 +8,12 @@
#include "src/setjmp/longjmp.h"
#include "include/llvm-libc-macros/offsetof-macro.h"
-#include "src/__support/OSUtil/io.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
+
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
#include "src/setjmp/checksum.h"
-#include "src/stdlib/abort.h"
+#endif
#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
#error "Invalid file include"
@@ -20,49 +21,55 @@
namespace LIBC_NAMESPACE_DECL {
+#define CALCULATE_RETURN_VALUE() \
+ "cmpl $0x1, %%esi\n\t" \
+ "adcl $0x0, %%esi\n\t" \
+ "movq %%rsi, %%rax\n\t"
+
#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
-extern "C" [[gnu::cold, noreturn]] void __libc_jmpbuf_corruption() {
- write_to_stderr("invalid checksum detected in longjmp\n");
- abort();
-}
+#define ACCUMULATE_CHECKSUM() \
+ "mul %[checksum]\n\t" \
+ "xor %%rax, %[checksum]\n\t" \
+ "rol $%c[rotation], %[checksum]\n\t"
+
#define LOAD_CHKSUM_STATE_REGISTERS() \
- asm("mov %0, %%rcx\n\t" ::"m"(jmpbuf::value_mask) : "rcx"); \
- asm("mov %0, %%rdx\n\t" ::"m"(jmpbuf::checksum_cookie) : "rdx");
+ asm("mov %[value_mask], %[mask]\n\t" \
+ "mov %[checksum_cookie], %[checksum]\n\t" \
+ : [mask] "=r"(mask), [checksum] "=r"(checksum) \
+ : [value_mask] "m"(jmpbuf::value_mask), [checksum_cookie] "m"( \
+ jmpbuf::checksum_cookie));
+// clang-format off
#define RESTORE_REG(DST) \
"movq %c[" #DST "](%%rdi), %%rax\n\t" \
"movq %%rax, %%" #DST "\n\t" \
- "xor %%rcx, %%" #DST "\n\t" \
- "mul %%rdx\n\t" \
- "xor %%rax, %%rdx\n\t" \
- "rol $%c[rotation], %%rdx\n\t"
+ "xor %[mask], %%" #DST "\n\t" \
+ ACCUMULATE_CHECKSUM()
#define RESTORE_RIP() \
"movq %c[rip](%%rdi), %%rax\n\t" \
- "xor %%rax, %%rcx\n\t" \
- "mul %%rdx\n\t" \
- "xor %%rax, %%rdx\n\t" \
- "rol $%c[rotation], %%rdx\n\t" \
- "cmp %c[chksum](%%rdi), %%rdx\n\t" \
+ "xor %%rax, %[mask]\n\t" \
+ ACCUMULATE_CHECKSUM() \
+ "cmp %c[__chksum](%%rdi), %%rdx\n\t" \
"jne __libc_jmpbuf_corruption\n\t" \
- "cmpl $0x1, %%esi\n\t" \
- "adcl $0x0, %%esi\n\t" \
- "movq %%rsi, %%rax\n\t" \
- "jmp *%%rcx\n\t"
+ CALCULATE_RETURN_VALUE() \
+ "jmp *%[mask]\n\t"
+// clang-format on
#else
#define LOAD_CHKSUM_STATE_REGISTERS()
#define RESTORE_REG(DST) "movq %c[" #DST "](%%rdi), %%" #DST "\n\t"
#define RESTORE_RIP() \
- "cmpl $0x1, %%esi\n\t" \
- "adcl $0x0, %%esi\n\t" \
- "movq %%rsi, %%rax\n\t" \
+ CALCULATE_RETURN_VALUE() \
"jmpq *%c[rip](%%rdi)\n\t"
#endif
-[[gnu::naked]]
-LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
+[[gnu::naked]] LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
+ // use registers to make sure values propagate correctly across the asm blocks
+ [[maybe_unused]] register __UINTPTR_TYPE__ mask asm("rcx");
+ [[maybe_unused]] register __UINT64_TYPE__ checksum asm("rdx");
+
LOAD_CHKSUM_STATE_REGISTERS()
- asm(
+ asm volatile(
// clang-format off
RESTORE_REG(rbx)
RESTORE_REG(rbp)
@@ -73,18 +80,23 @@ LLVM_LIBC_FUNCTION(void, longjmp, (jmp_buf, int)) {
RESTORE_REG(rsp)
RESTORE_RIP()
// clang-format on
- ::[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)),
+ : /* outputs */
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+ [mask] "+r"(mask), [checksum] "+r"(checksum)
+#endif
+ : /* inputs */
+ [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))
#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
// clang-format off
,[rotation] "i"(jmpbuf::ROTATION)
- ,[chksum] "i"(offsetof(__jmp_buf, __chksum))
+ ,[__chksum] "i"(offsetof(__jmp_buf, __chksum))
// clang-format on
#endif
- : "rax", "rdx", "rcx", "rsi");
+ : "rax", "rsi");
}
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/setjmp/x86_64/setjmp.cpp b/libc/src/setjmp/x86_64/setjmp.cpp
index 56e2f55cd3a9ff..456d23a4f76489 100644
--- a/libc/src/setjmp/x86_64/setjmp.cpp
+++ b/libc/src/setjmp/x86_64/setjmp.cpp
@@ -9,43 +9,45 @@
#include "include/llvm-libc-macros/offsetof-macro.h"
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#include "src/setjmp/checksum.h"
#include "src/setjmp/setjmp_impl.h"
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#include "src/setjmp/checksum.h"
+#endif
+
#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
#error "Invalid file include"
#endif
#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#define ACCUMULATE_CHECKSUM() \
+ "mul %[checksum]\n\t" \
+ "xor %%rax, %[checksum]\n\t" \
+ "rol $%c[rotation], %[checksum]\n\t"
+
#define LOAD_CHKSUM_STATE_REGISTERS() \
- asm("mov %0, %%rcx\n\t" ::"m"(jmpbuf::value_mask) : "rcx"); \
- asm("mov %0, %%rdx\n\t" ::"m"(jmpbuf::checksum_cookie) : "rdx");
+ asm("mov %[value_mask], %[mask]\n\t" \
+ "mov %[checksum_cookie], %[checksum]\n\t" \
+ : [mask] "=r"(mask), [checksum] "=r"(checksum) \
+ : [value_mask] "m"(jmpbuf::value_mask), [checksum_cookie] "m"( \
+ jmpbuf::checksum_cookie));
#define STORE_REG(SRC) \
"mov %%" #SRC ", %%rax\n\t" \
- "xor %%rcx, %%rax\n\t" \
- "mov %%rax, %c[" #SRC "](%%rdi)\n\t" \
- "mul %%rdx\n\t" \
- "xor %%rax, %%rdx\n\t" \
- "rol $%c[rotation], %%rdx\n\t"
+ "xor %[mask], %%rax\n\t" \
+ "mov %%rax, %c[" #SRC "](%%rdi)\n\t" ACCUMULATE_CHECKSUM()
#define STORE_RSP() \
"lea 8(%%rsp), %%rax\n\t" \
- "xor %%rcx, %%rax\n\t" \
- "mov %%rax, %c[rsp](%%rdi)\n\t" \
- "mul %%rdx\n\t" \
- "xor %%rax, %%rdx\n\t" \
- "rolq $%c[rotation], %%rdx\n\t"
+ "xor %[mask], %%rax\n\t" \
+ "mov %%rax, %c[rsp](%%rdi)\n\t" ACCUMULATE_CHECKSUM()
#define STORE_RIP() \
"mov (%%rsp), %%rax\n\t" \
- "xor %%rcx, %%rax\n\t" \
- "mov %%rax, %c[rip](%%rdi)\n\t" \
- "mul %%rdx\n\t" \
- "xor %%rax, %%rdx\n\t" \
- "rolq $%c[rotation], %%rdx\n\t"
+ "xor %[mask], %%rax\n\t" \
+ "mov %%rax, %c[rip](%%rdi)\n\t" ACCUMULATE_CHECKSUM()
-#define STORE_CHECKSUM() "mov %%rdx, %c[chksum](%%rdi)\n\t"
+#define STORE_CHECKSUM() "mov %%rdx, %c[__chksum](%%rdi)\n\t"
#else
#define LOAD_CHKSUM_STATE_REGISTERS()
#define STORE_REG(SRC) "mov %%" #SRC ", %c[" #SRC "](%%rdi)\n\t"
@@ -61,8 +63,11 @@
namespace LIBC_NAMESPACE_DECL {
[[gnu::naked]]
LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
+ // use registers to make sure values propagate correctly across the asm blocks
+ [[maybe_unused]] register __UINTPTR_TYPE__ mask asm("rcx");
+ [[maybe_unused]] register __UINT64_TYPE__ checksum asm("rdx");
LOAD_CHKSUM_STATE_REGISTERS()
- asm(
+ asm volatile(
// clang-format off
STORE_REG(rbx)
STORE_REG(rbp)
@@ -74,18 +79,24 @@ LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
STORE_RIP()
STORE_CHECKSUM()
// clang-format on
- ::[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)),
+ :
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+ [checksum] "+r"(checksum)
+#endif
+ :
+ [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))
#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
// clang-format off
,[rotation] "i"(jmpbuf::ROTATION)
- ,[chksum] "i"(offsetof(__jmp_buf, __chksum))
+ ,[__chksum] "i"(offsetof(__jmp_buf, __chksum))
+ ,[mask] "r"(mask)
// clang-format on
#endif
- : "rax", "rdx");
+ : "rax");
asm(R"(
xorl %eax, %eax
diff --git a/libc/startup/linux/CMakeLists.txt b/libc/startup/linux/CMakeLists.txt
index 34c870f98b1f1c..a185e8c04a5a33 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_ENABLE_FORTIFICATION AND LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64")
+ set(jmpbuf_fortification_deps libc.src.setjmp.checksum)
+ set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=1)
+else()
+ set(jmpbuf_fortification_deps)
+ set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=0)
+endif()
+
add_object_library(
do_start
SRCS
@@ -104,11 +112,12 @@ add_object_library(
libc.src.stdlib.exit
libc.src.stdlib.atexit
libc.src.unistd.environ
- libc.src.setjmp.checksum
+ ${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 55d85c90fca44b..0af216ea01eeed 100644
--- a/libc/startup/linux/do_start.cpp
+++ b/libc/startup/linux/do_start.cpp
@@ -11,7 +11,6 @@
#include "src/__support/OSUtil/syscall.h"
#include "src/__support/macros/config.h"
#include "src/__support/threads/thread.h"
-#include "src/setjmp/checksum.h"
#include "src/stdlib/atexit.h"
#include "src/stdlib/exit.h"
#include "src/unistd/environ.h"
@@ -22,6 +21,10 @@
#include <sys/mman.h>
#include <sys/syscall.h>
+#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#include "src/setjmp/checksum.h"
+#endif
+
extern "C" int main(int argc, char **argv, char **envp);
extern "C" {
>From 95066ec623a6253868345bffe8cb40dd4270bb24 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <yifanzhu at rochester.edu>
Date: Fri, 18 Oct 2024 13:42:37 -0400
Subject: [PATCH 6/6] [libc] address CRs
---
libc/config/config.json | 2 +-
libc/docs/configure.rst | 2 +-
libc/src/setjmp/CMakeLists.txt | 6 ++---
libc/src/setjmp/x86_64/checksum.def | 34 +++++++++++++++++++++++++++++
libc/src/setjmp/x86_64/longjmp.cpp | 20 +++++------------
libc/src/setjmp/x86_64/setjmp.cpp | 20 +++++------------
libc/startup/linux/CMakeLists.txt | 6 ++---
libc/startup/linux/do_start.cpp | 4 ++--
8 files changed, 54 insertions(+), 40 deletions(-)
create mode 100644 libc/src/setjmp/x86_64/checksum.def
diff --git a/libc/config/config.json b/libc/config/config.json
index 941b888f047b8f..b4eedabe2e95b9 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -100,7 +100,7 @@
"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_ENABLE_FORTIFICATION": {
+ "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 30cf375f32b7db..bf9e90aa3e1581 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -54,7 +54,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_ENABLE_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.
+ - ``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/src/setjmp/CMakeLists.txt b/libc/src/setjmp/CMakeLists.txt
index 21ac0b6ba52e25..0073ca1a6930ba 100644
--- a/libc/src/setjmp/CMakeLists.txt
+++ b/libc/src/setjmp/CMakeLists.txt
@@ -2,7 +2,7 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
endif()
-if (LIBC_CONF_SETJMP_ENABLE_FORTIFICATION)
+if (LIBC_CONF_SETJMP_FORTIFICATION)
if (TARGET libc.src.setjmp.${LIBC_TARGET_OS}.checksum
AND LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64")
add_object_library(
@@ -12,11 +12,11 @@ if (LIBC_CONF_SETJMP_ENABLE_FORTIFICATION)
.${LIBC_TARGET_OS}.checksum
)
set(fortification_deps libc.src.setjmp.checksum)
- set(fortification_defs -DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=1)
+ 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_ENABLE_FORTIFICATION=0)
+ set(fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=0)
endif()
endif()
diff --git a/libc/src/setjmp/x86_64/checksum.def b/libc/src/setjmp/x86_64/checksum.def
new file mode 100644
index 00000000000000..b4934ca8224f14
--- /dev/null
+++ b/libc/src/setjmp/x86_64/checksum.def
@@ -0,0 +1,34 @@
+//===-- Common macros 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
+//
+//===----------------------------------------------------------------------===//
+
+// For now, the checksum is computed with a simple multiply-xor-rotation
+// algorithm. The pesudo code is as follows:
+//
+// def checksum(x, acc):
+// masked = x ^ MASK
+// high, low = full_multiply(masked, acc)
+// return rotate(high ^ low, ROTATION)
+//
+// Similar other multiplication-based hashing, zero inputs
+// for the `full_multiply` function may pollute the checksum with zero.
+// However, user inputs are always masked where the initial ACC amd MASK are
+// generated with random entropy and ROTATION is a fixed prime number. It should
+// be of a ultra-low chance for masked or acc being zero given a good quality of
+// system-level entropy.
+
+#define ACCUMULATE_CHECKSUM() \
+ "mul %[checksum]\n\t" \
+ "xor %%rax, %[checksum]\n\t" \
+ "rol $%c[rotation], %[checksum]\n\t"
+
+#define LOAD_CHKSUM_STATE_REGISTERS() \
+ asm("mov %[value_mask], %[mask]\n\t" \
+ "mov %[checksum_cookie], %[checksum]\n\t" \
+ : [mask] "=r"(mask), [checksum] "=r"(checksum) \
+ : [value_mask] "m"(jmpbuf::value_mask), [checksum_cookie] "m"( \
+ jmpbuf::checksum_cookie));
diff --git a/libc/src/setjmp/x86_64/longjmp.cpp b/libc/src/setjmp/x86_64/longjmp.cpp
index 83e2538bea890a..033f4a36413683 100644
--- a/libc/src/setjmp/x86_64/longjmp.cpp
+++ b/libc/src/setjmp/x86_64/longjmp.cpp
@@ -11,7 +11,7 @@
#include "src/__support/common.h"
#include "src/__support/macros/config.h"
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
#include "src/setjmp/checksum.h"
#endif
@@ -26,18 +26,8 @@ namespace LIBC_NAMESPACE_DECL {
"adcl $0x0, %%esi\n\t" \
"movq %%rsi, %%rax\n\t"
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
-#define ACCUMULATE_CHECKSUM() \
- "mul %[checksum]\n\t" \
- "xor %%rax, %[checksum]\n\t" \
- "rol $%c[rotation], %[checksum]\n\t"
-
-#define LOAD_CHKSUM_STATE_REGISTERS() \
- asm("mov %[value_mask], %[mask]\n\t" \
- "mov %[checksum_cookie], %[checksum]\n\t" \
- : [mask] "=r"(mask), [checksum] "=r"(checksum) \
- : [value_mask] "m"(jmpbuf::value_mask), [checksum_cookie] "m"( \
- jmpbuf::checksum_cookie));
+#if LIBC_COPT_SETJMP_FORTIFICATION
+#include "src/setjmp/x86_64/checksum.def"
// clang-format off
#define RESTORE_REG(DST) \
@@ -81,7 +71,7 @@ namespace LIBC_NAMESPACE_DECL {
RESTORE_RIP()
// clang-format on
: /* outputs */
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
[mask] "+r"(mask), [checksum] "+r"(checksum)
#endif
: /* inputs */
@@ -90,7 +80,7 @@ namespace LIBC_NAMESPACE_DECL {
[r14] "i"(offsetof(__jmp_buf, r14)), [r15] "i"(offsetof(__jmp_buf, r15)),
[rsp] "i"(offsetof(__jmp_buf, rsp)),
[rip] "i"(offsetof(__jmp_buf, rip))
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
// clang-format off
,[rotation] "i"(jmpbuf::ROTATION)
,[__chksum] "i"(offsetof(__jmp_buf, __chksum))
diff --git a/libc/src/setjmp/x86_64/setjmp.cpp b/libc/src/setjmp/x86_64/setjmp.cpp
index 456d23a4f76489..aed8f0cd5e2d9c 100644
--- a/libc/src/setjmp/x86_64/setjmp.cpp
+++ b/libc/src/setjmp/x86_64/setjmp.cpp
@@ -11,7 +11,7 @@
#include "src/__support/macros/config.h"
#include "src/setjmp/setjmp_impl.h"
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
#include "src/setjmp/checksum.h"
#endif
@@ -19,18 +19,8 @@
#error "Invalid file include"
#endif
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
-#define ACCUMULATE_CHECKSUM() \
- "mul %[checksum]\n\t" \
- "xor %%rax, %[checksum]\n\t" \
- "rol $%c[rotation], %[checksum]\n\t"
-
-#define LOAD_CHKSUM_STATE_REGISTERS() \
- asm("mov %[value_mask], %[mask]\n\t" \
- "mov %[checksum_cookie], %[checksum]\n\t" \
- : [mask] "=r"(mask), [checksum] "=r"(checksum) \
- : [value_mask] "m"(jmpbuf::value_mask), [checksum_cookie] "m"( \
- jmpbuf::checksum_cookie));
+#if LIBC_COPT_SETJMP_FORTIFICATION
+#include "src/setjmp/x86_64/checksum.def"
#define STORE_REG(SRC) \
"mov %%" #SRC ", %%rax\n\t" \
@@ -80,7 +70,7 @@ LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
STORE_CHECKSUM()
// clang-format on
:
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
[checksum] "+r"(checksum)
#endif
:
@@ -89,7 +79,7 @@ LLVM_LIBC_FUNCTION(int, setjmp, (jmp_buf buf)) {
[r14] "i"(offsetof(__jmp_buf, r14)), [r15] "i"(offsetof(__jmp_buf, r15)),
[rsp] "i"(offsetof(__jmp_buf, rsp)),
[rip] "i"(offsetof(__jmp_buf, rip))
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
// clang-format off
,[rotation] "i"(jmpbuf::ROTATION)
,[__chksum] "i"(offsetof(__jmp_buf, __chksum))
diff --git a/libc/startup/linux/CMakeLists.txt b/libc/startup/linux/CMakeLists.txt
index a185e8c04a5a33..245dfca5b3ed3f 100644
--- a/libc/startup/linux/CMakeLists.txt
+++ b/libc/startup/linux/CMakeLists.txt
@@ -88,12 +88,12 @@ endif()
add_subdirectory(${LIBC_TARGET_ARCHITECTURE})
-if (LIBC_CONF_SETJMP_ENABLE_FORTIFICATION AND LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64")
+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_ENABLE_FORTIFICATION=1)
+ set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=1)
else()
set(jmpbuf_fortification_deps)
- set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_ENABLE_FORTIFICATION=0)
+ set(jmpbuf_fortification_defs -DLIBC_COPT_SETJMP_FORTIFICATION=0)
endif()
add_object_library(
diff --git a/libc/startup/linux/do_start.cpp b/libc/startup/linux/do_start.cpp
index 0af216ea01eeed..ded94768f0b92c 100644
--- a/libc/startup/linux/do_start.cpp
+++ b/libc/startup/linux/do_start.cpp
@@ -21,7 +21,7 @@
#include <sys/mman.h>
#include <sys/syscall.h>
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
#include "src/setjmp/checksum.h"
#endif
@@ -135,7 +135,7 @@ void teardown_main_tls() { cleanup_tls(tls.addr, tls.size); }
if (tls.size != 0 && !set_thread_ptr(tls.tp))
syscall_impl<long>(SYS_exit, 1);
-#if LIBC_COPT_SETJMP_ENABLE_FORTIFICATION
+#if LIBC_COPT_SETJMP_FORTIFICATION
jmpbuf::initialize();
#endif
More information about the libc-commits
mailing list