[libc-commits] [libc] [libc] fortify jmp buffer for x86-64 (PR #112769)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Thu Oct 17 12:53:48 PDT 2024


https://github.com/SchrodingerZhu created https://github.com/llvm/llvm-project/pull/112769

supersedes #https://github.com/llvm/llvm-project/pull/101110

>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] [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();



More information about the libc-commits mailing list