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
23int __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
77static void SigprofHandler(int signo, siginfo_t *si, void *c) {
78 if (__ubsan_is_trap_loop(c)) {
79 __builtin_trap();
80 }
81}
82
83void __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
97int __ubsan_is_trap_loop(void *c) { return false; }
98void __ubsan_install_trap_loop_detection(void) {}
99
100#endif
101