-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[libc] add checksum for jmpbuf #101110
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?
[libc] add checksum for jmpbuf #101110
Changes from 2 commits
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,67 @@ | ||
| //===-- 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/hash.h" | ||
| #include "src/__support/macros/attributes.h" | ||
| #include "src/__support/macros/config.h" | ||
| #include "src/setjmp/setjmp_impl.h" | ||
| #include "src/stdlib/abort.h" | ||
| #include "src/unistd/write.h" | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| namespace jmpbuf { | ||
| using HashState = internal::HashState; | ||
| // Initial values generated by | ||
| // https://www.random.org/cgi-bin/randbyte?nbytes=48&format=h | ||
| // These values are only used for overlay targets. | ||
| LIBC_INLINE_VAR uint64_t register_mangle_cookie = 0xdf8a883867040cbc; | ||
| LIBC_INLINE_VAR uint64_t checksum_mangle_cookie = 0x9ed4fe406ebe9cf9; | ||
| LIBC_INLINE_VAR uint64_t randomness[4] = { | ||
| 0x83b9df7dddf5ab3d, | ||
| 0x06c931cca75e15c6, | ||
| 0x08280ec9e9a778bf, | ||
| 0x111f67f4aafc9276, | ||
| }; | ||
|
|
||
| LIBC_INLINE int update_checksum(__jmp_buf *buf) { | ||
| HashState state{ | ||
| randomness[0], | ||
| randomness[1], | ||
| randomness[2], | ||
| randomness[3], | ||
| }; | ||
| state.update(buf, offsetof(__jmp_buf, __chksum)); | ||
| buf->__chksum = state.finish() ^ checksum_mangle_cookie; | ||
| return 0; | ||
| } | ||
|
|
||
| LIBC_INLINE void verify(const __jmp_buf *buf) { | ||
| HashState state{ | ||
| randomness[0], | ||
| randomness[1], | ||
| randomness[2], | ||
| randomness[3], | ||
| }; | ||
| state.update(buf, offsetof(__jmp_buf, __chksum)); | ||
| auto chksum = state.finish() ^ checksum_mangle_cookie; | ||
| if (chksum != buf->__chksum) { | ||
| constexpr char MSG[] = "jump buffer corrupted\n"; | ||
| LIBC_NAMESPACE::write(2, MSG, sizeof(MSG) - 1); | ||
| LIBC_NAMESPACE::abort(); | ||
| } | ||
| } | ||
|
|
||
| } // namespace jmpbuf | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL | ||
|
|
||
| #endif // LLVM_LIBC_SRC_SETJMP_CHECKSUM_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,39 +7,63 @@ | |
| //===----------------------------------------------------------------------===// | ||
|
|
||
| #include "src/setjmp/longjmp.h" | ||
| #include "include/llvm-libc-types/jmp_buf.h" | ||
| #include "src/__support/common.h" | ||
| #include "src/__support/macros/config.h" | ||
| #include "src/setjmp/checksum.h" | ||
|
|
||
| #if !defined(LIBC_TARGET_ARCH_IS_X86_64) | ||
| #error "Invalid file include" | ||
| #endif | ||
|
|
||
| namespace LIBC_NAMESPACE_DECL { | ||
|
|
||
| [[gnu::naked]] | ||
| LLVM_LIBC_FUNCTION(void, longjmp, (__jmp_buf * buf, int val)) { | ||
| register __UINT64_TYPE__ rbx __asm__("rbx"); | ||
| register __UINT64_TYPE__ rbp __asm__("rbp"); | ||
| register __UINT64_TYPE__ r12 __asm__("r12"); | ||
| register __UINT64_TYPE__ r13 __asm__("r13"); | ||
| register __UINT64_TYPE__ r14 __asm__("r14"); | ||
| register __UINT64_TYPE__ r15 __asm__("r15"); | ||
| register __UINT64_TYPE__ rsp __asm__("rsp"); | ||
| register __UINT64_TYPE__ rax __asm__("rax"); | ||
|
|
||
| // ABI requires that the return value should be stored in rax. So, we store | ||
| // |val| in rax. Note that this has to happen before we restore the registers | ||
| // from values in |buf|. Otherwise, once rsp and rbp are updated, we cannot | ||
| // read |val|. | ||
| val = val == 0 ? 1 : val; | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rax) : "m"(val) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbx) : "m"(buf->rbx) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rbp) : "m"(buf->rbp) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r12) : "m"(buf->r12) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r13) : "m"(buf->r13) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r14) : "m"(buf->r14) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(r15) : "m"(buf->r15) :); | ||
| LIBC_INLINE_ASM("mov %1, %0\n\t" : "=r"(rsp) : "m"(buf->rsp) :); | ||
| LIBC_INLINE_ASM("jmp *%0\n\t" : : "m"(buf->rip)); | ||
| asm(R"( | ||
| pushq %%rbp | ||
| pushq %%rbx | ||
| mov %%rdi, %%rbp | ||
| mov %%esi, %%ebx | ||
| subq $8, %%rsp | ||
| call %P0 | ||
| addq $8, %%rsp | ||
| mov %%ebx, %%esi | ||
| mov %%rbp, %%rdi | ||
| popq %%rbx | ||
| popq %%rbp | ||
| )" ::"i"(jmpbuf::verify) | ||
| : "rax", "rcx", "rdx", "r8", "r9", "r10", "r11"); | ||
|
|
||
| register __UINT64_TYPE__ rcx __asm__("rcx"); | ||
| // Load cookie | ||
| asm("mov %1, %0\n\t" : "=r"(rcx) : "m"(jmpbuf::register_mangle_cookie)); | ||
|
|
||
| // load registers from buffer | ||
| // do not pass any invalid values into registers | ||
| #define RECOVER(REG) \ | ||
| asm("mov %c[" #REG "](%%rdi), %%rdx\n\t" \ | ||
| "xor %%rdx, %%rcx\n\t" \ | ||
| "mov %%rdx, %%" #REG "\n\t" ::[REG] "i"(offsetof(__jmp_buf, REG)) \ | ||
| : "rdx"); | ||
|
||
|
|
||
| RECOVER(rbx); | ||
| RECOVER(rbp); | ||
| RECOVER(r12); | ||
| RECOVER(r13); | ||
| RECOVER(r14); | ||
| RECOVER(r15); | ||
| RECOVER(rsp); | ||
|
|
||
| asm(R"( | ||
| xor %%eax,%%eax | ||
| cmp $1,%%esi | ||
| adc %%esi,%%eax | ||
| mov %c[rip](%%rdi),%%rdx | ||
| xor %%rdx, %%rcx | ||
| jmp *%%rdx | ||
| )" ::[rip] "i"(offsetof(__jmp_buf, rip)) | ||
| : "rdx"); | ||
| } | ||
|
|
||
| } // namespace LIBC_NAMESPACE_DECL | ||
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.
Is it standard to use fixed keys in these implementations?
xorencryption is already quite weak, is there a way we can initialize these using some form of entropy on systems that can support that?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.
I plan to populate these keys on full build during startup.
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.
Yeah, that sounds reasonable. For other targets, like baremetal, we may want to think about other means of initialization, but that's out of scope for this patch.
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.
If the initialization fails, we should make that as obvious as possible since a fixed key is as bad as no key at all. I'd recommend making these initial values
0xAAAAAAAAAAAAAAAA,0xBBBBBBBBBBBBBBBB, and so on so that it's clear what has happened.