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