1 | #include "sanitizer_common/sanitizer_atomic.h" |
2 | |
3 | #include <stdlib.h> |
4 | #include <stdint.h> |
5 | #include <string.h> |
6 | #include <unistd.h> |
7 | |
8 | #ifdef KERNEL_USE |
9 | extern "C" void ubsan_message(const char *msg); |
10 | static void message(const char *msg) { ubsan_message(msg); } |
11 | #else |
12 | static void message(const char *msg) { |
13 | (void)write(fd: 2, buf: msg, n: strlen(s: msg)); |
14 | } |
15 | #endif |
16 | |
17 | static const int kMaxCallerPcs = 20; |
18 | static __sanitizer::atomic_uintptr_t caller_pcs[kMaxCallerPcs]; |
19 | // Number of elements in caller_pcs. A special value of kMaxCallerPcs + 1 means |
20 | // that "too many errors" has already been reported. |
21 | static __sanitizer::atomic_uint32_t caller_pcs_sz; |
22 | |
23 | __attribute__((noinline)) static bool report_this_error(uintptr_t caller) { |
24 | if (caller == 0) |
25 | return false; |
26 | while (true) { |
27 | unsigned sz = __sanitizer::atomic_load_relaxed(a: &caller_pcs_sz); |
28 | if (sz > kMaxCallerPcs) return false; // early exit |
29 | // when sz==kMaxCallerPcs print "too many errors", but only when cmpxchg |
30 | // succeeds in order to not print it multiple times. |
31 | if (sz > 0 && sz < kMaxCallerPcs) { |
32 | uintptr_t p; |
33 | for (unsigned i = 0; i < sz; ++i) { |
34 | p = __sanitizer::atomic_load_relaxed(a: &caller_pcs[i]); |
35 | if (p == 0) break; // Concurrent update. |
36 | if (p == caller) return false; |
37 | } |
38 | if (p == 0) continue; // FIXME: yield? |
39 | } |
40 | |
41 | if (!__sanitizer::atomic_compare_exchange_strong( |
42 | a: &caller_pcs_sz, cmp: &sz, xchg: sz + 1, mo: __sanitizer::memory_order_seq_cst)) |
43 | continue; // Concurrent update! Try again from the start. |
44 | |
45 | if (sz == kMaxCallerPcs) { |
46 | message(msg: "ubsan: too many errors\n" ); |
47 | return false; |
48 | } |
49 | __sanitizer::atomic_store_relaxed(a: &caller_pcs[sz], v: caller); |
50 | return true; |
51 | } |
52 | } |
53 | |
54 | __attribute__((noinline)) static void decorate_msg(char *buf, |
55 | uintptr_t caller) { |
56 | // print the address by nibbles |
57 | for (unsigned shift = sizeof(uintptr_t) * 8; shift;) { |
58 | shift -= 4; |
59 | unsigned nibble = (caller >> shift) & 0xf; |
60 | *(buf++) = nibble < 10 ? nibble + '0' : nibble - 10 + 'a'; |
61 | } |
62 | // finish the message |
63 | buf[0] = '\n'; |
64 | buf[1] = '\0'; |
65 | } |
66 | |
67 | #if defined(__ANDROID__) |
68 | extern "C" __attribute__((weak)) void android_set_abort_message(const char *); |
69 | static void abort_with_message(const char *msg) { |
70 | if (&android_set_abort_message) android_set_abort_message(msg); |
71 | abort(); |
72 | } |
73 | #else |
74 | static void abort_with_message(const char *) { abort(); } |
75 | #endif |
76 | |
77 | #if SANITIZER_DEBUG |
78 | namespace __sanitizer { |
79 | // The DCHECK macro needs this symbol to be defined. |
80 | void NORETURN CheckFailed(const char *file, int, const char *cond, u64, u64) { |
81 | message("Sanitizer CHECK failed: " ); |
82 | message(file); |
83 | message(":?? : " ); // FIXME: Show line number. |
84 | message(cond); |
85 | abort(); |
86 | } |
87 | } // namespace __sanitizer |
88 | #endif |
89 | |
90 | #define INTERFACE extern "C" __attribute__((visibility("default"))) |
91 | |
92 | // How many chars we need to reserve to print an address. |
93 | constexpr unsigned kAddrBuf = SANITIZER_WORDSIZE / 4; |
94 | #define MSG_TMPL(msg) "ubsan: " msg " by 0x" |
95 | #define MSG_TMPL_END(buf, msg) (buf + sizeof(MSG_TMPL(msg)) - 1) |
96 | // Reserve an additional byte for '\n'. |
97 | #define MSG_BUF_LEN(msg) (sizeof(MSG_TMPL(msg)) + kAddrBuf + 1) |
98 | |
99 | #define HANDLER_RECOVER(name, msg) \ |
100 | INTERFACE void __ubsan_handle_##name##_minimal() { \ |
101 | uintptr_t caller = GET_CALLER_PC(); \ |
102 | if (!report_this_error(caller)) return; \ |
103 | char msg_buf[MSG_BUF_LEN(msg)] = MSG_TMPL(msg); \ |
104 | decorate_msg(MSG_TMPL_END(msg_buf, msg), caller); \ |
105 | message(msg_buf); \ |
106 | } |
107 | |
108 | #define HANDLER_NORECOVER(name, msg) \ |
109 | INTERFACE void __ubsan_handle_##name##_minimal_abort() { \ |
110 | char msg_buf[MSG_BUF_LEN(msg)] = MSG_TMPL(msg); \ |
111 | decorate_msg(MSG_TMPL_END(msg_buf, msg), GET_CALLER_PC()); \ |
112 | message(msg_buf); \ |
113 | abort_with_message(msg_buf); \ |
114 | } |
115 | |
116 | #define HANDLER(name, msg) \ |
117 | HANDLER_RECOVER(name, msg) \ |
118 | HANDLER_NORECOVER(name, msg) |
119 | |
120 | HANDLER(type_mismatch, "type-mismatch" ) |
121 | HANDLER(alignment_assumption, "alignment-assumption" ) |
122 | HANDLER(add_overflow, "add-overflow" ) |
123 | HANDLER(sub_overflow, "sub-overflow" ) |
124 | HANDLER(mul_overflow, "mul-overflow" ) |
125 | HANDLER(negate_overflow, "negate-overflow" ) |
126 | HANDLER(divrem_overflow, "divrem-overflow" ) |
127 | HANDLER(shift_out_of_bounds, "shift-out-of-bounds" ) |
128 | HANDLER(out_of_bounds, "out-of-bounds" ) |
129 | HANDLER_RECOVER(builtin_unreachable, "builtin-unreachable" ) |
130 | HANDLER_RECOVER(missing_return, "missing-return" ) |
131 | HANDLER(vla_bound_not_positive, "vla-bound-not-positive" ) |
132 | HANDLER(float_cast_overflow, "float-cast-overflow" ) |
133 | HANDLER(load_invalid_value, "load-invalid-value" ) |
134 | HANDLER(invalid_builtin, "invalid-builtin" ) |
135 | HANDLER(invalid_objc_cast, "invalid-objc-cast" ) |
136 | HANDLER(function_type_mismatch, "function-type-mismatch" ) |
137 | HANDLER(implicit_conversion, "implicit-conversion" ) |
138 | HANDLER(nonnull_arg, "nonnull-arg" ) |
139 | HANDLER(nonnull_return, "nonnull-return" ) |
140 | HANDLER(nullability_arg, "nullability-arg" ) |
141 | HANDLER(nullability_return, "nullability-return" ) |
142 | HANDLER(pointer_overflow, "pointer-overflow" ) |
143 | HANDLER(cfi_check_fail, "cfi-check-fail" ) |
144 | |