blob: 8c1f64d427e8f748dda2e498e862dbee9d99fc19 [file] [log] [blame]
Patrick Georgi593124d2020-05-10 19:44:08 +02001/* SPDX-License-Identifier: ISC */
Patrick Georgi16849bb2020-05-10 17:52:40 +02002/* ubsan/ubsan.c
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -07003 * Undefined behavior sanitizer runtime support.
4 *
5 * Adapted from:
6 * https://gitlab.com/sortix/sortix/raw/master/libc/ubsan/ubsan.c
7 */
8
9#include <stdint.h>
10#include <console/console.h>
11
12struct ubsan_source_location {
13 const char *filename;
14 uint32_t line;
15 uint32_t column;
16};
17
18struct ubsan_type_descriptor {
19 uint16_t type_kind;
20 uint16_t type_info;
21 char type_name[];
22};
23
24typedef uintptr_t ubsan_value_handle_t;
25
26/*
27* Keep the compiler happy -- it wants prototypes but nobody
28* except the compiler should be touching these functions.
29*/
30#pragma GCC diagnostic ignored "-Wmissing-prototypes"
31
Aaron Durbin0370bcf2018-09-05 09:37:11 -060032static void __noreturn ubsan_abort(const struct ubsan_source_location *location,
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070033 const char *violation) {
34 static const struct ubsan_source_location unknown_location = {
35 "<unknown file>",
36 0,
37 0,
38 };
39
40 if (!location || !location->filename)
41 location = &unknown_location;
42 printk(BIOS_ERR, "%s %s:%lu:%lu\n", violation, location->filename,
43 (unsigned long int)location->line,
44 (unsigned long int)location->column);
45 die("ubsan: unrecoverable error.\n");
46}
47
48#define ABORT_VARIANT(name, params, call) \
Aaron Durbin0370bcf2018-09-05 09:37:11 -060049 __noreturn void __ubsan_handle_##name##_abort params; \
50 __noreturn void __ubsan_handle_##name##_abort params { \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020051 __ubsan_handle_##name call; \
52 __builtin_unreachable(); \
53 }
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070054
55#define ABORT_VARIANT_VP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020056 ABORT_VARIANT(name, (void *a), (a))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070057#define ABORT_VARIANT_VP_VP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020058 ABORT_VARIANT(name, (void *a, void *b), (a, b))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070059#define ABORT_VARIANT_VP_IP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020060 ABORT_VARIANT(name, (void *a, intptr_t b), (a, b))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070061#define ABORT_VARIANT_VP_VP_VP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020062 ABORT_VARIANT(name, (void *a, void *b, void *c), (a, b, c))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070063
64struct ubsan_type_mismatch_data {
65 struct ubsan_source_location location;
66 struct ubsan_type_descriptor *type;
67 uintptr_t alignment;
68 unsigned char type_check_kind;
69};
70
71void __ubsan_handle_type_mismatch(void *data_raw, void *pointer_raw)
72{
73 const struct ubsan_type_mismatch_data *data =
74 (struct ubsan_type_mismatch_data *)data_raw;
75 ubsan_value_handle_t pointer = (ubsan_value_handle_t)pointer_raw;
76 const char *violation = "type mismatch";
77 if (!pointer)
78 violation = "null pointer access";
79 else if (data->alignment && (pointer & (data->alignment - 1)))
80 violation = "unaligned access";
81 ubsan_abort(&data->location, violation);
82}
83
84ABORT_VARIANT_VP_VP(type_mismatch);
85
86struct ubsan_overflow_data {
87 struct ubsan_source_location location;
88 struct ubsan_type_descriptor *type;
89};
90
91void __ubsan_handle_add_overflow(void *data_raw, void *lhs_raw,
92 void *rhs_raw)
93{
94 const struct ubsan_overflow_data *data
95 = (struct ubsan_overflow_data *)data_raw;
96 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
97 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
98 (void)lhs;
99 (void)rhs;
100 ubsan_abort(&data->location, "addition overflow");
101}
102
103ABORT_VARIANT_VP_VP_VP(add_overflow);
104
105void __ubsan_handle_sub_overflow(void *data_raw, void *lhs_raw,
106 void *rhs_raw)
107{
108 const struct ubsan_overflow_data *data
109 = (struct ubsan_overflow_data *)data_raw;
110 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
111 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
112 (void)lhs;
113 (void)rhs;
114 ubsan_abort(&data->location, "subtraction overflow");
115}
116
117ABORT_VARIANT_VP_VP_VP(sub_overflow);
118
119void __ubsan_handle_mul_overflow(void *data_raw, void *lhs_raw,
120 void *rhs_raw)
121{
122 const struct ubsan_overflow_data *data
123 = (struct ubsan_overflow_data *)data_raw;
124 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
125 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
126 (void)lhs;
127 (void)rhs;
128 ubsan_abort(&data->location, "multiplication overflow");
129}
130
131ABORT_VARIANT_VP_VP_VP(mul_overflow);
132
133void __ubsan_handle_negate_overflow(void *data_raw, void *old_value_raw)
134{
135 const struct ubsan_overflow_data *data
136 = (struct ubsan_overflow_data *)data_raw;
137 ubsan_value_handle_t old_value
138 = (ubsan_value_handle_t)old_value_raw;
139 (void) old_value;
140 ubsan_abort(&data->location, "negation overflow");
141}
142
143ABORT_VARIANT_VP_VP(negate_overflow);
144
145void __ubsan_handle_divrem_overflow(void *data_raw, void *lhs_raw,
146 void *rhs_raw)
147{
148 const struct ubsan_overflow_data *data
149 = (struct ubsan_overflow_data *)data_raw;
150 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
151 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
152 (void)lhs;
153 (void)rhs;
154 ubsan_abort(&data->location, "division remainder overflow");
155}
156
157ABORT_VARIANT_VP_VP_VP(divrem_overflow);
158
159struct ubsan_shift_out_of_bounds_data {
160 struct ubsan_source_location location;
161 struct ubsan_type_descriptor *lhs_type;
162 struct ubsan_type_descriptor *rhs_type;
163};
164
165void __ubsan_handle_shift_out_of_bounds(void *data_raw, void *lhs_raw,
166 void *rhs_raw)
167{
168 const struct ubsan_shift_out_of_bounds_data *data =
169 (struct ubsan_shift_out_of_bounds_data *)data_raw;
170 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
171 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
172 (void)lhs;
173 (void)rhs;
174 ubsan_abort(&data->location, "shift out of bounds");
175}
176
177ABORT_VARIANT_VP_VP_VP(shift_out_of_bounds);
178
179struct ubsan_out_of_bounds_data {
180 struct ubsan_source_location location;
181 struct ubsan_type_descriptor *array_type;
182 struct ubsan_type_descriptor *index_type;
183};
184
185void __ubsan_handle_out_of_bounds(void *data_raw, void *index_raw)
186{
187 const struct ubsan_out_of_bounds_data *data =
188 (struct ubsan_out_of_bounds_data *)data_raw;
189 ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
190 (void)index;
191 ubsan_abort(&data->location, "out of bounds");
192}
193
194ABORT_VARIANT_VP_VP(out_of_bounds);
195
196struct ubsan_unreachable_data {
197 struct ubsan_source_location location;
198};
199
Aaron Durbin0370bcf2018-09-05 09:37:11 -0600200void __noreturn __ubsan_handle_builtin_unreachable(void *data_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700201{
202 struct ubsan_unreachable_data *data =
203 (struct ubsan_unreachable_data *)data_raw;
204 ubsan_abort(&data->location, "reached unreachable");
205}
206
Aaron Durbin0370bcf2018-09-05 09:37:11 -0600207void __noreturn __ubsan_handle_missing_return(void *data_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700208{
209 const struct ubsan_unreachable_data *data =
210 (struct ubsan_unreachable_data *)data_raw;
211 ubsan_abort(&data->location, "missing return");
212}
213
214struct ubsan_vla_bound_data {
215 struct ubsan_source_location location;
216 struct ubsan_type_descriptor *type;
217};
218
219void __ubsan_handle_vla_bound_not_positive(void *data_raw, void *bound_raw)
220{
221 const struct ubsan_vla_bound_data *data
222 = (struct ubsan_vla_bound_data *)data_raw;
223 ubsan_value_handle_t bound = (ubsan_value_handle_t)bound_raw;
224 (void)bound;
225 ubsan_abort(&data->location, "negative variable array length");
226}
227
228ABORT_VARIANT_VP_VP(vla_bound_not_positive);
229
230struct ubsan_float_cast_overflow_data {
231/*
232* TODO: Remove this GCC 5.x compatibility after switching to GCC 6.x. The
233* GCC developers accidentally forgot the source location. Their
234* libubsan probes to see if it looks like a path, but we don't need
235* to maintain compatibility with multiple gcc releases. See below.
236*/
237#if !(defined(__GNUC__) && __GNUC__ < 6)
238 struct ubsan_source_location location;
239#endif
240 struct ubsan_type_descriptor *from_type;
241 struct ubsan_type_descriptor *to_type;
242};
243
244void __ubsan_handle_float_cast_overflow(void *data_raw, void *from_raw)
245{
246 struct ubsan_float_cast_overflow_data *data =
247 (struct ubsan_float_cast_overflow_data *)data_raw;
248 ubsan_value_handle_t from = (ubsan_value_handle_t)from_raw;
249 (void) from;
250#if !(defined(__GNUC__) && __GNUC__ < 6)
251 ubsan_abort(&data->location, "float cast overflow");
252#else
253 ubsan_abort(((void) data, &unknown_location), "float cast overflow");
254#endif
255}
256
257ABORT_VARIANT_VP_VP(float_cast_overflow);
258
259struct ubsan_invalid_value_data {
260 struct ubsan_source_location location;
261 struct ubsan_type_descriptor *type;
262};
263
264void __ubsan_handle_load_invalid_value(void *data_raw, void *value_raw)
265{
266 const struct ubsan_invalid_value_data *data =
267 (struct ubsan_invalid_value_data *)data_raw;
268 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
269 (void)value;
270 ubsan_abort(&data->location, "invalid value load");
271}
272
273ABORT_VARIANT_VP_VP(load_invalid_value);
274
275struct ubsan_function_type_mismatch_data {
276 struct ubsan_source_location location;
277 struct ubsan_type_descriptor *type;
278};
279
280void __ubsan_handle_function_type_mismatch(void *data_raw, void *value_raw)
281{
282 const struct ubsan_function_type_mismatch_data *data =
283 (struct ubsan_function_type_mismatch_data *)data_raw;
284 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
285 (void)value;
286 ubsan_abort(&data->location, "function type mismatch");
287}
288
289ABORT_VARIANT_VP_VP(function_type_mismatch);
290
291struct ubsan_nonnull_return_data {
292 struct ubsan_source_location location;
293 struct ubsan_source_location attr_location;
294};
295
296void __ubsan_handle_nonnull_return(void *data_raw)
297{
298 const struct ubsan_nonnull_return_data *data =
299 (struct ubsan_nonnull_return_data *)data_raw;
300 ubsan_abort(&data->location, "null return");
301}
302
303ABORT_VARIANT_VP(nonnull_return);
304
305struct ubsan_nonnull_arg_data {
306 struct ubsan_source_location location;
307 struct ubsan_source_location attr_location;
308};
309
310/*
311* TODO: GCC's libubsan does not have the second parameter, but its builtin
312* somehow has it and conflict if we don't match it.
313*/
314void __ubsan_handle_nonnull_arg(void *data_raw, intptr_t index_raw)
315{
316 const struct ubsan_nonnull_arg_data *data =
317 (struct ubsan_nonnull_arg_data *)data_raw;
318 ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
319 (void)index;
320 ubsan_abort(&data->location, "null argument");
321}
322
323ABORT_VARIANT_VP_IP(nonnull_arg);
324
325struct ubsan_cfi_bad_icall_data {
326 struct ubsan_source_location location;
327 struct ubsan_type_descriptor *type;
328};
329
330void __ubsan_handle_cfi_bad_icall(void *data_raw, void *value_raw)
331{
332 static const char *abort_text
333 = "cfi: integrity failure during indirect call.";
334 const struct ubsan_cfi_bad_icall_data *data =
335 (struct ubsan_cfi_bad_icall_data *)data_raw;
336 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
337 (void)value;
338 ubsan_abort(&data->location, abort_text);
339}
340
341ABORT_VARIANT_VP_VP(cfi_bad_icall);