-
Notifications
You must be signed in to change notification settings - Fork 14.2k
[libc] fortify jmp buffer for x86-64 #112769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3a4cf21
2bb6a32
7901be1
f591ed8
3bdd8a8
e6ce912
ef9ccdb
35a1f08
30e338c
b67f5f1
f18519a
b284930
6c7d964
6e70ac5
70f663d
d5791e1
30b48d3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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." | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps this should be one config. Otherwise, if the user flips one but not the other, then setjmp/longjmp will fail at runtime, right? |
||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -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); | ||||||||||
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
https://llvm.org/docs/CodingStandards.html#use-auto-type-deduction-to-make-code-more-readable |
||||||||||
void initialize(); | ||||||||||
extern "C" [[gnu::cold, noreturn]] void __libc_jmpbuf_corruption(); | ||||||||||
} // namespace jmpbuf | ||||||||||
} // namespace LIBC_NAMESPACE_DECL | ||||||||||
|
||||||||||
#endif // LLVM_LIBC_SRC_SETJMP_CHECKSUM_H |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you want to make these |
||
-fomit-frame-pointer | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
) |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -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 <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 = | ||||||||||||||||||
static_cast<__UINTPTR_TYPE__>(0x3899'f0d3'5005'd953ull); | ||||||||||||||||||
__UINTPTR_TYPE__ checksum_cookie = | ||||||||||||||||||
static_cast<__UINTPTR_TYPE__>(0xc7d9'd341'6afc'33f2ull); | ||||||||||||||||||
Comment on lines
+18
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
// initialize the checksum state | ||||||||||||||||||
void initialize() { | ||||||||||||||||||
__UINTPTR_TYPE__ entropy[2]; | ||||||||||||||||||
syscall_impl<long>(SYS_getrandom, entropy, sizeof(entropy), 0); | ||||||||||||||||||
// add in additional entropy | ||||||||||||||||||
jmpbuf::value_mask ^= entropy[0]; | ||||||||||||||||||
jmpbuf::checksum_cookie ^= entropy[0]; | ||||||||||||||||||
Comment on lines
+28
to
+29
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did a previous version of this PR use |
||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
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 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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} | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will eventually need to support shadow stacks (part of Intel's CET): https://libc-alpha.sourceware.narkive.com/KcCIyBg9/patch-linux-x86-support-shadow-stack-pointer-in-setjmp-longjmp. It's perhaps worth discussing if we'll want to have 2 or 3 configs for fortification.
For instance, I'd imaging we'd want full fortification or no fortification. But I wonder if shadow stacks make checksumming irrelevant? Hmm...I'll need to think about that more.