aboutsummaryrefslogtreecommitdiff
path: root/compiler-rt/lib/ubsan/ubsan_loop_detect.cpp
blob: 75e9724a06f35fceb714af7c6978fe32e57ec7f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
//===-- ubsan_loop_detect.cpp ---------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Runtime support for -fsanitize-trap-loop.
//
//===----------------------------------------------------------------------===//

#include <sanitizer/ubsan_interface.h>

#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))

#include <asm/processor-flags.h>
#include <signal.h>
#include <stdint.h>
#include <sys/time.h>
#include <sys/ucontext.h>

int __ubsan_is_trap_loop(void *c) {
  auto *uc = reinterpret_cast<ucontext_t *>(c);
#if defined(__x86_64__)
  auto *ip = reinterpret_cast<const uint8_t *>(uc->uc_mcontext.gregs[REG_RIP]);
#else
  auto *ip = reinterpret_cast<const uint8_t *>(uc->uc_mcontext.gregs[REG_EIP]);
#endif
  // Test whether IP is at a conditional branch to self instruction.
  if ((ip[0] & 0xf0) != 0x70 || ip[1] != 0xfe)
    return false;

  // If so, test whether the condition is satisfied, in case we happened to
  // receive the signal at a not-taken branch to self.
  uint64_t eflags = uc->uc_mcontext.gregs[REG_EFL];
  switch (ip[0]) {
  case 0x70: // JO
    return eflags & X86_EFLAGS_OF;
  case 0x71: // JNO
    return !(eflags & X86_EFLAGS_OF);
  case 0x72: // JB
    return eflags & X86_EFLAGS_CF;
  case 0x73: // JAE
    return !(eflags & X86_EFLAGS_CF);
  case 0x74: // JE
    return eflags & X86_EFLAGS_ZF;
  case 0x75: // JNE
    return !(eflags & X86_EFLAGS_ZF);
  case 0x76: // JBE
    return (eflags & X86_EFLAGS_CF) || (eflags & X86_EFLAGS_ZF);
  case 0x77: // JA
    return !(eflags & X86_EFLAGS_CF) && !(eflags & X86_EFLAGS_ZF);
  case 0x78: // JS
    return eflags & X86_EFLAGS_SF;
  case 0x79: // JNS
    return !(eflags & X86_EFLAGS_SF);
  case 0x7A: // JP
    return eflags & X86_EFLAGS_PF;
  case 0x7B: // JNP
    return !(eflags & X86_EFLAGS_PF);
  case 0x7C: // JL
    return !!(eflags & X86_EFLAGS_SF) != !!(eflags & X86_EFLAGS_OF);
  case 0x7D: // JGE
    return !!(eflags & X86_EFLAGS_SF) == !!(eflags & X86_EFLAGS_OF);
  case 0x7E: // JLE
    return (eflags & X86_EFLAGS_ZF) ||
           !!(eflags & X86_EFLAGS_SF) != !!(eflags & X86_EFLAGS_OF);
  case 0x7F: // JG
    return !(eflags & X86_EFLAGS_ZF) &&
           !!(eflags & X86_EFLAGS_SF) == !!(eflags & X86_EFLAGS_OF);
  default:
    return false;
  }
}

static void SigprofHandler(int signo, siginfo_t *si, void *c) {
  if (__ubsan_is_trap_loop(c)) {
    __builtin_trap();
  }
}

void __ubsan_install_trap_loop_detection(void) {
  struct sigaction sa;
  sa.sa_sigaction = SigprofHandler;
  sigaction(SIGPROF, &sa, nullptr);

  struct itimerval timer;
  timer.it_value.tv_sec = 0;
  timer.it_value.tv_usec = 100000;
  timer.it_interval = timer.it_value;
  setitimer(ITIMER_PROF, &timer, NULL);
}

#else

int __ubsan_is_trap_loop(void *c) { return false; }
void __ubsan_install_trap_loop_detection(void) {}

#endif