| 1 | //===-- ubsan_loop_detect.cpp ---------------------------------------------===// |
|---|---|
| 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 | // Runtime support for -fsanitize-trap-loop. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include <sanitizer/ubsan_interface.h> |
| 14 | |
| 15 | #if defined(__linux__) && (defined(__i386__) || defined(__x86_64__)) |
| 16 | |
| 17 | #include <asm/processor-flags.h> |
| 18 | #include <signal.h> |
| 19 | #include <stdint.h> |
| 20 | #include <sys/time.h> |
| 21 | #include <sys/ucontext.h> |
| 22 | |
| 23 | int __ubsan_is_trap_loop(void *c) { |
| 24 | auto *uc = reinterpret_cast<ucontext_t *>(c); |
| 25 | #if defined(__x86_64__) |
| 26 | auto *ip = reinterpret_cast<const uint8_t *>(uc->uc_mcontext.gregs[REG_RIP]); |
| 27 | #else |
| 28 | auto *ip = reinterpret_cast<const uint8_t *>(uc->uc_mcontext.gregs[REG_EIP]); |
| 29 | #endif |
| 30 | // Test whether IP is at a conditional branch to self instruction. |
| 31 | if ((ip[0] & 0xf0) != 0x70 || ip[1] != 0xfe) |
| 32 | return false; |
| 33 | |
| 34 | // If so, test whether the condition is satisfied, in case we happened to |
| 35 | // receive the signal at a not-taken branch to self. |
| 36 | uint64_t eflags = uc->uc_mcontext.gregs[REG_EFL]; |
| 37 | switch (ip[0]) { |
| 38 | case 0x70: // JO |
| 39 | return eflags & X86_EFLAGS_OF; |
| 40 | case 0x71: // JNO |
| 41 | return !(eflags & X86_EFLAGS_OF); |
| 42 | case 0x72: // JB |
| 43 | return eflags & X86_EFLAGS_CF; |
| 44 | case 0x73: // JAE |
| 45 | return !(eflags & X86_EFLAGS_CF); |
| 46 | case 0x74: // JE |
| 47 | return eflags & X86_EFLAGS_ZF; |
| 48 | case 0x75: // JNE |
| 49 | return !(eflags & X86_EFLAGS_ZF); |
| 50 | case 0x76: // JBE |
| 51 | return (eflags & X86_EFLAGS_CF) || (eflags & X86_EFLAGS_ZF); |
| 52 | case 0x77: // JA |
| 53 | return !(eflags & X86_EFLAGS_CF) && !(eflags & X86_EFLAGS_ZF); |
| 54 | case 0x78: // JS |
| 55 | return eflags & X86_EFLAGS_SF; |
| 56 | case 0x79: // JNS |
| 57 | return !(eflags & X86_EFLAGS_SF); |
| 58 | case 0x7A: // JP |
| 59 | return eflags & X86_EFLAGS_PF; |
| 60 | case 0x7B: // JNP |
| 61 | return !(eflags & X86_EFLAGS_PF); |
| 62 | case 0x7C: // JL |
| 63 | return !!(eflags & X86_EFLAGS_SF) != !!(eflags & X86_EFLAGS_OF); |
| 64 | case 0x7D: // JGE |
| 65 | return !!(eflags & X86_EFLAGS_SF) == !!(eflags & X86_EFLAGS_OF); |
| 66 | case 0x7E: // JLE |
| 67 | return (eflags & X86_EFLAGS_ZF) || |
| 68 | !!(eflags & X86_EFLAGS_SF) != !!(eflags & X86_EFLAGS_OF); |
| 69 | case 0x7F: // JG |
| 70 | return !(eflags & X86_EFLAGS_ZF) && |
| 71 | !!(eflags & X86_EFLAGS_SF) == !!(eflags & X86_EFLAGS_OF); |
| 72 | default: |
| 73 | return false; |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | static void SigprofHandler(int signo, siginfo_t *si, void *c) { |
| 78 | if (__ubsan_is_trap_loop(c)) { |
| 79 | __builtin_trap(); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | void __ubsan_install_trap_loop_detection(void) { |
| 84 | struct sigaction sa; |
| 85 | sa.sa_sigaction = SigprofHandler; |
| 86 | sigaction(SIGPROF, act: &sa, oact: nullptr); |
| 87 | |
| 88 | struct itimerval timer; |
| 89 | timer.it_value.tv_sec = 0; |
| 90 | timer.it_value.tv_usec = 100000; |
| 91 | timer.it_interval = timer.it_value; |
| 92 | setitimer(ITIMER_PROF, new: &timer, NULL); |
| 93 | } |
| 94 | |
| 95 | #else |
| 96 | |
| 97 | int __ubsan_is_trap_loop(void *c) { return false; } |
| 98 | void __ubsan_install_trap_loop_detection(void) {} |
| 99 | |
| 100 | #endif |
| 101 |