1#include "sanitizer_common/sanitizer_atomic.h"
2
3#include <stdint.h>
4#include <stdlib.h>
5#include <string.h>
6
7#if defined(KERNEL_USE)
8extern "C" void ubsan_message(const char *msg);
9static void message(const char *msg) { ubsan_message(msg); }
10#elif SANITIZER_AMDGPU || SANITIZER_NVPTX
11#include <stdio.h>
12template <typename... Args>
13static void message(const char *msg, Args &&...args) {
14 fprintf(stderr, msg, args...);
15}
16#elif SANITIZER_SPIRV
17extern "C" int printf(const char *fmt, ...);
18template <typename... Args>
19static void message(const char *msg, Args &&...args) {
20 printf(msg, args...);
21}
22#else
23#include <unistd.h>
24static void message(const char *msg) { (void)write(fd: 2, buf: msg, n: strlen(s: msg)); }
25#endif
26
27// If for some reason we cannot build the runtime with preserve_all, don't
28// emit any symbol. Programs that need them will fail to link, but that is
29// better than randomly corrupted registers.
30// Some architectures don't support preserve_all (but clang still has the)
31// attribute. For now, only support x86-64 and aarch64.
32#if defined(__clang__) && defined(__has_cpp_attribute) && \
33 (defined(__x86_64__) || defined(__aarch64__))
34#if __has_cpp_attribute(clang::preserve_all)
35#define PRESERVE_HANDLERS true
36#else
37#define PRESERVE_HANDLERS false
38#endif
39#else
40#define PRESERVE_HANDLERS false
41#endif
42
43static const int kMaxCallerPcs = 20;
44static __sanitizer::atomic_uintptr_t caller_pcs[kMaxCallerPcs];
45// Number of elements in caller_pcs. A special value of kMaxCallerPcs + 1 means
46// that "too many errors" has already been reported.
47static __sanitizer::atomic_uint32_t caller_pcs_sz;
48
49static char *append_str(const char *s, char *buf, const char *end) {
50 for (const char *p = s; (buf < end) && (*p != '\0'); ++p, ++buf)
51 *buf = *p;
52 return buf;
53}
54
55static char *append_hex(uintptr_t d, char *buf, const char *end) {
56 // Print the address by nibbles.
57 for (unsigned shift = sizeof(uintptr_t) * 8; shift && buf < end;) {
58 shift -= 4;
59 unsigned nibble = (d >> shift) & 0xf;
60 *(buf++) = nibble < 10 ? nibble + '0' : nibble - 10 + 'a';
61 }
62 return buf;
63}
64
65static void format_msg(const char *kind, uintptr_t caller, char *buf,
66 const char *end) {
67 buf = append_str(s: "ubsan: ", buf, end);
68 buf = append_str(s: kind, buf, end);
69 buf = append_str(s: " by 0x", buf, end);
70 buf = append_hex(d: caller, buf, end);
71 buf = append_str(s: "\n", buf, end);
72 if (buf == end)
73 --buf; // Make sure we don't cause a buffer overflow.
74 *buf = '\0';
75}
76
77static void format(const char *kind, uintptr_t caller) {
78#if SANITIZER_AMDGPU || SANITIZER_NVPTX || SANITIZER_SPIRV
79 (void)format_msg;
80 message("ubsan: %s by %p\n", kind, reinterpret_cast<void *>(caller));
81#else
82 char msg_buf[128];
83 format_msg(kind, caller, buf: msg_buf, end: msg_buf + sizeof(msg_buf));
84 message(msg: msg_buf);
85#endif
86}
87
88[[gnu::cold]] static void report_error(const char *kind, uintptr_t caller) {
89 if (caller == 0)
90 return;
91 while (true) {
92 unsigned sz = __sanitizer::atomic_load_relaxed(a: &caller_pcs_sz);
93 if (sz > kMaxCallerPcs)
94 return; // early exit
95 // when sz==kMaxCallerPcs print "too many errors", but only when cmpxchg
96 // succeeds in order to not print it multiple times.
97 if (sz > 0 && sz < kMaxCallerPcs) {
98 uintptr_t p;
99 for (unsigned i = 0; i < sz; ++i) {
100 p = __sanitizer::atomic_load_relaxed(a: &caller_pcs[i]);
101 if (p == 0)
102 break; // Concurrent update.
103 if (p == caller)
104 return;
105 }
106 if (p == 0)
107 continue; // FIXME: yield?
108 }
109
110 if (!__sanitizer::atomic_compare_exchange_strong(
111 a: &caller_pcs_sz, cmp: &sz, xchg: sz + 1, mo: __sanitizer::memory_order_seq_cst))
112 continue; // Concurrent update! Try again from the start.
113
114 if (sz == kMaxCallerPcs) {
115 message(msg: "ubsan: too many errors\n");
116 return;
117 }
118 __sanitizer::atomic_store_relaxed(a: &caller_pcs[sz], v: caller);
119
120 format(kind, caller);
121 }
122}
123
124SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_report_error, const char *kind,
125 uintptr_t caller) {
126 report_error(kind, caller);
127}
128
129#if PRESERVE_HANDLERS
130SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_report_error_preserve,
131 const char *kind, uintptr_t caller)
132[[clang::preserve_all]] {
133 // Additional indirection so the user can override this with their own
134 // preserve_all function. This would allow, e.g., a function that reports the
135 // first error only, so for all subsequent calls we can skip the register save
136 // / restore.
137 __ubsan_report_error(kind, caller);
138}
139#endif
140
141SANITIZER_INTERFACE_WEAK_DEF(void, __ubsan_report_error_fatal, const char *kind,
142 uintptr_t caller) {
143 // Use another handlers, in case it's already overriden.
144 __ubsan_report_error(kind, caller);
145}
146
147#if defined(__ANDROID__)
148extern "C" __attribute__((weak)) void android_set_abort_message(const char *);
149static void abort_with_message(const char *kind, uintptr_t caller) {
150 char msg_buf[128];
151 format_msg(kind, caller, msg_buf, msg_buf + sizeof(msg_buf));
152 if (&android_set_abort_message)
153 android_set_abort_message(msg_buf);
154 abort();
155}
156#elif SANITIZER_AMDGPU || SANITIZER_NVPTX || SANITIZER_SPIRV
157static void abort_with_message(const char *kind, uintptr_t caller) {
158 __builtin_verbose_trap("ubsan", "unrecoverable error");
159}
160#else
161static void abort_with_message(const char *kind, uintptr_t caller) { abort(); }
162#endif
163
164#if SANITIZER_DEBUG
165namespace __sanitizer {
166// The DCHECK macro needs this symbol to be defined.
167void NORETURN CheckFailed(const char *file, int, const char *cond, u64, u64) {
168 message("Sanitizer CHECK failed: ");
169 message(file);
170 message(":?? : "); // FIXME: Show line number.
171 message(cond);
172 abort();
173}
174} // namespace __sanitizer
175#endif
176
177#define INTERFACE extern "C" __attribute__((visibility("default")))
178
179#if PRESERVE_HANDLERS
180#define HANDLER_PRESERVE(name, kind) \
181 INTERFACE void __ubsan_handle_##name##_minimal_preserve() \
182 [[clang::preserve_all]] { \
183 __ubsan_report_error_preserve(kind, GET_CALLER_PC()); \
184 }
185#else
186#define HANDLER_PRESERVE(name, kind)
187#endif
188
189#define HANDLER_RECOVER(name, kind) \
190 INTERFACE void __ubsan_handle_##name##_minimal() { \
191 __ubsan_report_error(kind, GET_CALLER_PC()); \
192 } \
193 HANDLER_PRESERVE(name, kind)
194
195#define HANDLER_NORECOVER(name, kind) \
196 INTERFACE void __ubsan_handle_##name##_minimal_abort() { \
197 uintptr_t caller = GET_CALLER_PC(); \
198 __ubsan_report_error_fatal(kind, caller); \
199 abort_with_message(kind, caller); \
200 }
201
202#define HANDLER(name, kind) \
203 HANDLER_RECOVER(name, kind) \
204 HANDLER_NORECOVER(name, kind)
205
206HANDLER(type_mismatch, "type-mismatch")
207HANDLER(alignment_assumption, "alignment-assumption")
208HANDLER(add_overflow, "add-overflow")
209HANDLER(sub_overflow, "sub-overflow")
210HANDLER(mul_overflow, "mul-overflow")
211HANDLER(negate_overflow, "negate-overflow")
212HANDLER(divrem_overflow, "divrem-overflow")
213HANDLER(shift_out_of_bounds, "shift-out-of-bounds")
214HANDLER(out_of_bounds, "out-of-bounds")
215HANDLER(local_out_of_bounds, "local-out-of-bounds")
216HANDLER_RECOVER(builtin_unreachable, "builtin-unreachable")
217HANDLER_RECOVER(missing_return, "missing-return")
218HANDLER(vla_bound_not_positive, "vla-bound-not-positive")
219HANDLER(float_cast_overflow, "float-cast-overflow")
220HANDLER(load_invalid_value, "load-invalid-value")
221HANDLER(invalid_builtin, "invalid-builtin")
222HANDLER(invalid_objc_cast, "invalid-objc-cast")
223HANDLER(function_type_mismatch, "function-type-mismatch")
224HANDLER(implicit_conversion, "implicit-conversion")
225HANDLER(nonnull_arg, "nonnull-arg")
226HANDLER(nonnull_return, "nonnull-return")
227HANDLER(nullability_arg, "nullability-arg")
228HANDLER(nullability_return, "nullability-return")
229HANDLER(pointer_overflow, "pointer-overflow")
230HANDLER(cfi_check_fail, "cfi-check-fail")
231