Skip to content

Commit 09c2766

Browse files
Yifan Zhunickdesaulniers
andcommitted
[libc] add checksum for jmpbuf
Co-authored-by: Nick Desaulniers <[email protected]>
1 parent c99bd3c commit 09c2766

File tree

6 files changed

+164
-63
lines changed

6 files changed

+164
-63
lines changed

libc/include/llvm-libc-types/jmp_buf.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ typedef struct {
3838
#else
3939
#error "__jmp_buf not available for your target architecture."
4040
#endif
41+
__UINT64_TYPE__ __sigmask;
42+
__UINT64_TYPE__ __has_sigmask : 1;
43+
__UINT64_TYPE__ __unused : 63;
44+
__UINT64_TYPE__ __chksum;
4145
} __jmp_buf;
4246

4347
typedef __jmp_buf jmp_buf[1];

libc/src/setjmp/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
22
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_ARCHITECTURE})
33
endif()
44

5+
add_header_library(
6+
checksum
7+
HDRS
8+
checksum.h
9+
DEPENDS
10+
libc.src.__support.hash
11+
libc.src.stdlib.abort
12+
libc.src.unistd.write
13+
)
14+
515
add_entrypoint_object(
616
setjmp
717
ALIAS

libc/src/setjmp/checksum.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//===-- Implementation header for jmpbuf checksum ---------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_SETJMP_CHECKSUM_H
10+
#define LLVM_LIBC_SRC_SETJMP_CHECKSUM_H
11+
12+
#include "src/__support/hash.h"
13+
#include "src/__support/macros/attributes.h"
14+
#include "src/__support/macros/config.h"
15+
#include "src/setjmp/setjmp_impl.h"
16+
#include "src/stdlib/abort.h"
17+
#include "src/unistd/write.h"
18+
19+
namespace LIBC_NAMESPACE_DECL {
20+
21+
namespace jmpbuf {
22+
using HashState = internal::HashState;
23+
// Initial values generated by
24+
// https://www.random.org/cgi-bin/randbyte?nbytes=48&format=h
25+
// These values are only used for overlay targets.
26+
LIBC_INLINE_VAR uint64_t register_mangle_cookie = 0xdf8a883867040cbc;
27+
LIBC_INLINE_VAR uint64_t checksum_mangle_cookie = 0x9ed4fe406ebe9cf9;
28+
LIBC_INLINE_VAR uint64_t randomness[4] = {
29+
0x83b9df7dddf5ab3d,
30+
0x06c931cca75e15c6,
31+
0x08280ec9e9a778bf,
32+
0x111f67f4aafc9276,
33+
};
34+
35+
LIBC_INLINE int update_checksum(__jmp_buf *buf) {
36+
HashState state{
37+
randomness[0],
38+
randomness[1],
39+
randomness[2],
40+
randomness[3],
41+
};
42+
state.update(buf, offsetof(__jmp_buf, __chksum));
43+
buf->__chksum = state.finish() ^ checksum_mangle_cookie;
44+
return 0;
45+
}
46+
47+
LIBC_INLINE void verify(const __jmp_buf *buf) {
48+
HashState state{
49+
randomness[0],
50+
randomness[1],
51+
randomness[2],
52+
randomness[3],
53+
};
54+
state.update(buf, offsetof(__jmp_buf, __chksum));
55+
auto chksum = state.finish() ^ checksum_mangle_cookie;
56+
if (chksum != buf->__chksum) {
57+
constexpr char MSG[] = "jump buffer corrupted\n";
58+
LIBC_NAMESPACE::write(2, MSG, sizeof(MSG) - 1);
59+
LIBC_NAMESPACE::abort();
60+
}
61+
}
62+
63+
} // namespace jmpbuf
64+
65+
} // namespace LIBC_NAMESPACE_DECL
66+
67+
#endif // LLVM_LIBC_SRC_SETJMP_CHECKSUM_H

libc/src/setjmp/x86_64/CMakeLists.txt

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,11 @@ add_entrypoint_object(
66
../setjmp_impl.h
77
DEPENDS
88
libc.include.setjmp
9+
libc.src.setjmp.checksum
910
COMPILE_OPTIONS
1011
-O3
1112
-fno-omit-frame-pointer
12-
# TODO: Remove once one of these lands:
13-
# https://github.com/llvm/llvm-project/pull/87837
14-
# https://github.com/llvm/llvm-project/pull/88054
15-
# https://github.com/llvm/llvm-project/pull/88157
16-
-ftrivial-auto-var-init=uninitialized
13+
-momit-leaf-frame-pointer
1714
)
1815

1916
add_entrypoint_object(
@@ -24,6 +21,7 @@ add_entrypoint_object(
2421
../longjmp.h
2522
DEPENDS
2623
libc.include.setjmp
24+
libc.src.setjmp.checksum
2725
COMPILE_OPTIONS
2826
-O3
2927
-fomit-frame-pointer

libc/src/setjmp/x86_64/longjmp.cpp

Lines changed: 46 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,62 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "src/setjmp/longjmp.h"
10+
#include "include/llvm-libc-types/jmp_buf.h"
1011
#include "src/__support/common.h"
1112
#include "src/__support/macros/config.h"
13+
#include "src/setjmp/checksum.h"
1214

1315
#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
1416
#error "Invalid file include"
1517
#endif
1618

1719
namespace LIBC_NAMESPACE_DECL {
1820

21+
[[gnu::naked]]
1922
LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) {
20-
register __UINT64_TYPE__ rbx __asm__("rbx");
21-
register __UINT64_TYPE__ rbp __asm__("rbp");
22-
register __UINT64_TYPE__ r12 __asm__("r12");
23-
register __UINT64_TYPE__ r13 __asm__("r13");
24-
register __UINT64_TYPE__ r14 __asm__("r14");
25-
register __UINT64_TYPE__ r15 __asm__("r15");
26-
register __UINT64_TYPE__ rsp __asm__("rsp");
27-
register __UINT64_TYPE__ rax __asm__("rax");
28-
29-
// ABI requires that the return value should be stored in rax. So, we store
30-
// |val| in rax. Note that this has to happen before we restore the registers
31-
// from values in |buf|. Otherwise, once rsp and rbp are updated, we cannot
32-
// read |val|.
33-
val = val == 0 ? 1 : val;
34-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rax) : "m"(val) :);
35-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbx) : "m"(buf->rbx) :);
36-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbp) : "m"(buf->rbp) :);
37-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r12) : "m"(buf->r12) :);
38-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r13) : "m"(buf->r13) :);
39-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r14) : "m"(buf->r14) :);
40-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r15) : "m"(buf->r15) :);
41-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rsp) : "m"(buf->rsp) :);
42-
LIBC_INLINE_ASM("jmp *%0\n\t" : : "m"(buf->rip));
23+
asm(R"(
24+
pushq %%rbp
25+
pushq %%rbx
26+
mov %%rdi, %%rbp
27+
mov %%esi, %%ebx
28+
subq $8, %%rsp
29+
call %P0
30+
addq $8, %%rsp
31+
mov %%ebx, %%esi
32+
mov %%rbp, %%rdi
33+
popq %%rbx
34+
popq %%rbp
35+
)" :: "i"(jmpbuf::verify) : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11");
36+
37+
register __UINT64_TYPE__ rcx __asm__("rcx");
38+
// Load cookie
39+
asm("mov %1, %0\n\t" : "=r"(rcx) : "m"(jmpbuf::register_mangle_cookie));
40+
41+
// load registers from buffer
42+
// do not pass any invalid values into registers
43+
#define RECOVER(REG) \
44+
asm("mov %c[" #REG "](%%rdi), %%rdx\n\t" \
45+
"xor %%rdx, %%rcx\n\t" \
46+
"mov %%rdx, %%" #REG "\n\t" ::[REG] "i"(offsetof(__jmp_buf, REG)) \
47+
: "rdx");
48+
49+
RECOVER(rbx);
50+
RECOVER(rbp);
51+
RECOVER(r12);
52+
RECOVER(r13);
53+
RECOVER(r14);
54+
RECOVER(r15);
55+
RECOVER(rsp);
56+
57+
asm(R"(
58+
xor %%eax,%%eax
59+
cmp $1,%%esi
60+
adc %%esi,%%eax
61+
mov %c[rip](%%rdi),%%rdx
62+
xor %%rdx, %%rcx
63+
jmp *%%rdx
64+
)" ::[rip] "i"(offsetof(__jmp_buf, rip))
65+
: "rdx");
4366
}
4467

4568
} // namespace LIBC_NAMESPACE_DECL

libc/src/setjmp/x86_64/setjmp.cpp

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "src/__support/common.h"
1010
#include "src/__support/macros/config.h"
11+
#include "src/setjmp/checksum.h"
1112
#include "src/setjmp/setjmp_impl.h"
1213

1314
#if !defined(LIBC_TARGET_ARCH_IS_X86_64)
@@ -16,42 +17,40 @@
1617

1718
namespace LIBC_NAMESPACE_DECL {
1819

20+
namespace jmpbuf {} // namespace jmpbuf
21+
[[gnu::naked]]
1922
LLVM_LIBC_FUNCTION(int, setjmp, (__jmp_buf * buf)) {
20-
register __UINT64_TYPE__ rbx __asm__("rbx");
21-
register __UINT64_TYPE__ r12 __asm__("r12");
22-
register __UINT64_TYPE__ r13 __asm__("r13");
23-
register __UINT64_TYPE__ r14 __asm__("r14");
24-
register __UINT64_TYPE__ r15 __asm__("r15");
25-
26-
// We want to store the register values as is. So, we will suppress the
27-
// compiler warnings about the uninitialized variables declared above.
28-
#pragma GCC diagnostic push
29-
#pragma GCC diagnostic ignored "-Wuninitialized"
30-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->rbx) : "r"(rbx) :);
31-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r12) : "r"(r12) :);
32-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r13) : "r"(r13) :);
33-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r14) : "r"(r14) :);
34-
LIBC_INLINE_ASM("mov %1, %0\n\t" : "=m"(buf->r15) : "r"(r15) :);
35-
#pragma GCC diagnostic pop
36-
37-
// We want the rbp of the caller, which is what __builtin_frame_address(1)
38-
// should return. But, compilers generate a warning that calling
39-
// __builtin_frame_address with non-zero argument is unsafe. So, we use
40-
// the knowledge of the x86_64 ABI to fetch the callers rbp. As per the ABI,
41-
// the rbp of the caller is pushed on to the stack and then new top is saved
42-
// in this function's rbp. So, we fetch it from location at which this
43-
// functions's rbp is pointing.
44-
buf->rbp = *reinterpret_cast<__UINTPTR_TYPE__ *>(__builtin_frame_address(0));
45-
46-
// The callers stack address is exactly 2 pointer widths ahead of the current
47-
// frame pointer - between the current frame pointer and the rsp of the caller
48-
// are the return address (pushed by the x86_64 call instruction) and the
49-
// previous stack pointer as required by the x86_64 ABI.
50-
// The stack pointer is ahead because the stack grows down on x86_64.
51-
buf->rsp = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_frame_address(0)) +
52-
sizeof(__UINTPTR_TYPE__) * 2;
53-
buf->rip = reinterpret_cast<__UINTPTR_TYPE__>(__builtin_return_address(0));
54-
return 0;
23+
register __UINT64_TYPE__ rcx __asm__("rcx");
24+
// Load cookie
25+
asm("mov %1, %0\n\t" : "=r"(rcx) : "m"(jmpbuf::register_mangle_cookie));
26+
// store registers to buffer
27+
// do not pass any invalid values into registers
28+
#define STORE(REG) \
29+
asm("mov %%" #REG ", %%rdx\n\t" \
30+
"xor %%rdx, %%rcx\n\t" \
31+
"mov %%rdx, %c[" #REG \
32+
"](%%rdi)\n\t" ::[REG] "i"(offsetof(__jmp_buf, REG)) \
33+
: "rdx");
34+
35+
STORE(rbx);
36+
STORE(rbp);
37+
STORE(r12);
38+
STORE(r13);
39+
STORE(r14);
40+
STORE(r15);
41+
asm(R"(
42+
lea 8(%%rsp),%%rdx
43+
xor %%rdx, %%rcx
44+
mov %%rdx,%c[rsp](%%rdi)
45+
mov (%%rsp),%%rdx
46+
xor %%rdx, %%rcx
47+
mov %%rdx,%c[rip](%%rdi)
48+
)" ::[rsp] "i"(offsetof(__jmp_buf, rsp)),
49+
[rip] "i"(offsetof(__jmp_buf, rip))
50+
: "rdx");
51+
52+
// tail call to update checksum
53+
asm("jmp %P0" : : "i"(jmpbuf::update_checksum));
5554
}
5655

5756
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)