blob: 1037b1b17ac0b2ebf83c8454fac90f2fb2710ba7 [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
Angel Pons7d6dae62020-07-27 21:08:38 +020071void __ubsan_handle_type_mismatch_v1(void *data_raw, void *pointer_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070072{
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
Angel Pons7d6dae62020-07-27 21:08:38 +020084ABORT_VARIANT_VP_VP(type_mismatch_v1);
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070085
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
Angel Pons7d6dae62020-07-27 21:08:38 +0200159struct ubsan_pointer_overflow_data {
160 struct ubsan_source_location location;
161};
162
163void __ubsan_handle_pointer_overflow(void *data_raw, void *base_raw, void *result_raw)
164{
165 const struct ubsan_pointer_overflow_data *data =
166 (struct ubsan_pointer_overflow_data *)data_raw;
167 ubsan_value_handle_t base = (ubsan_value_handle_t)base_raw;
168 ubsan_value_handle_t result = (ubsan_value_handle_t)result_raw;
169 (void)base;
170 (void)result;
171 ubsan_abort(&data->location, "pointer overflow");
172}
173
174ABORT_VARIANT_VP_VP_VP(pointer_overflow);
175
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700176struct ubsan_shift_out_of_bounds_data {
177 struct ubsan_source_location location;
178 struct ubsan_type_descriptor *lhs_type;
179 struct ubsan_type_descriptor *rhs_type;
180};
181
182void __ubsan_handle_shift_out_of_bounds(void *data_raw, void *lhs_raw,
183 void *rhs_raw)
184{
185 const struct ubsan_shift_out_of_bounds_data *data =
186 (struct ubsan_shift_out_of_bounds_data *)data_raw;
187 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
188 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
189 (void)lhs;
190 (void)rhs;
191 ubsan_abort(&data->location, "shift out of bounds");
192}
193
194ABORT_VARIANT_VP_VP_VP(shift_out_of_bounds);
195
196struct ubsan_out_of_bounds_data {
197 struct ubsan_source_location location;
198 struct ubsan_type_descriptor *array_type;
199 struct ubsan_type_descriptor *index_type;
200};
201
202void __ubsan_handle_out_of_bounds(void *data_raw, void *index_raw)
203{
204 const struct ubsan_out_of_bounds_data *data =
205 (struct ubsan_out_of_bounds_data *)data_raw;
206 ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
207 (void)index;
208 ubsan_abort(&data->location, "out of bounds");
209}
210
211ABORT_VARIANT_VP_VP(out_of_bounds);
212
213struct ubsan_unreachable_data {
214 struct ubsan_source_location location;
215};
216
Aaron Durbin0370bcf2018-09-05 09:37:11 -0600217void __noreturn __ubsan_handle_builtin_unreachable(void *data_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700218{
219 struct ubsan_unreachable_data *data =
220 (struct ubsan_unreachable_data *)data_raw;
221 ubsan_abort(&data->location, "reached unreachable");
222}
223
Aaron Durbin0370bcf2018-09-05 09:37:11 -0600224void __noreturn __ubsan_handle_missing_return(void *data_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700225{
226 const struct ubsan_unreachable_data *data =
227 (struct ubsan_unreachable_data *)data_raw;
228 ubsan_abort(&data->location, "missing return");
229}
230
231struct ubsan_vla_bound_data {
232 struct ubsan_source_location location;
233 struct ubsan_type_descriptor *type;
234};
235
236void __ubsan_handle_vla_bound_not_positive(void *data_raw, void *bound_raw)
237{
238 const struct ubsan_vla_bound_data *data
239 = (struct ubsan_vla_bound_data *)data_raw;
240 ubsan_value_handle_t bound = (ubsan_value_handle_t)bound_raw;
241 (void)bound;
242 ubsan_abort(&data->location, "negative variable array length");
243}
244
245ABORT_VARIANT_VP_VP(vla_bound_not_positive);
246
247struct ubsan_float_cast_overflow_data {
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700248 struct ubsan_source_location location;
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700249 struct ubsan_type_descriptor *from_type;
250 struct ubsan_type_descriptor *to_type;
251};
252
253void __ubsan_handle_float_cast_overflow(void *data_raw, void *from_raw)
254{
255 struct ubsan_float_cast_overflow_data *data =
256 (struct ubsan_float_cast_overflow_data *)data_raw;
257 ubsan_value_handle_t from = (ubsan_value_handle_t)from_raw;
258 (void) from;
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700259 ubsan_abort(&data->location, "float cast overflow");
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700260}
261
262ABORT_VARIANT_VP_VP(float_cast_overflow);
263
264struct ubsan_invalid_value_data {
265 struct ubsan_source_location location;
266 struct ubsan_type_descriptor *type;
267};
268
269void __ubsan_handle_load_invalid_value(void *data_raw, void *value_raw)
270{
271 const struct ubsan_invalid_value_data *data =
272 (struct ubsan_invalid_value_data *)data_raw;
273 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
274 (void)value;
275 ubsan_abort(&data->location, "invalid value load");
276}
277
278ABORT_VARIANT_VP_VP(load_invalid_value);
279
280struct ubsan_function_type_mismatch_data {
281 struct ubsan_source_location location;
282 struct ubsan_type_descriptor *type;
283};
284
285void __ubsan_handle_function_type_mismatch(void *data_raw, void *value_raw)
286{
287 const struct ubsan_function_type_mismatch_data *data =
288 (struct ubsan_function_type_mismatch_data *)data_raw;
289 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
290 (void)value;
291 ubsan_abort(&data->location, "function type mismatch");
292}
293
294ABORT_VARIANT_VP_VP(function_type_mismatch);
295
296struct ubsan_nonnull_return_data {
297 struct ubsan_source_location location;
298 struct ubsan_source_location attr_location;
299};
300
301void __ubsan_handle_nonnull_return(void *data_raw)
302{
303 const struct ubsan_nonnull_return_data *data =
304 (struct ubsan_nonnull_return_data *)data_raw;
305 ubsan_abort(&data->location, "null return");
306}
307
308ABORT_VARIANT_VP(nonnull_return);
309
310struct ubsan_nonnull_arg_data {
311 struct ubsan_source_location location;
312 struct ubsan_source_location attr_location;
313};
314
315/*
316* TODO: GCC's libubsan does not have the second parameter, but its builtin
317* somehow has it and conflict if we don't match it.
318*/
319void __ubsan_handle_nonnull_arg(void *data_raw, intptr_t index_raw)
320{
321 const struct ubsan_nonnull_arg_data *data =
322 (struct ubsan_nonnull_arg_data *)data_raw;
323 ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
324 (void)index;
325 ubsan_abort(&data->location, "null argument");
326}
327
328ABORT_VARIANT_VP_IP(nonnull_arg);
329
330struct ubsan_cfi_bad_icall_data {
331 struct ubsan_source_location location;
332 struct ubsan_type_descriptor *type;
333};
334
335void __ubsan_handle_cfi_bad_icall(void *data_raw, void *value_raw)
336{
337 static const char *abort_text
338 = "cfi: integrity failure during indirect call.";
339 const struct ubsan_cfi_bad_icall_data *data =
340 (struct ubsan_cfi_bad_icall_data *)data_raw;
341 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
342 (void)value;
343 ubsan_abort(&data->location, abort_text);
344}
345
346ABORT_VARIANT_VP_VP(cfi_bad_icall);