blob: 560493cfc21199c8b2a4411b43c1a5c5e0ed3d20 [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
Martin Roth050b9112023-05-19 18:54:04 -06007 *
8 * Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
9 *
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070010 */
11
12#include <stdint.h>
13#include <console/console.h>
14
15struct ubsan_source_location {
16 const char *filename;
17 uint32_t line;
18 uint32_t column;
19};
20
21struct ubsan_type_descriptor {
22 uint16_t type_kind;
23 uint16_t type_info;
24 char type_name[];
25};
26
27typedef uintptr_t ubsan_value_handle_t;
28
29/*
30* Keep the compiler happy -- it wants prototypes but nobody
31* except the compiler should be touching these functions.
32*/
33#pragma GCC diagnostic ignored "-Wmissing-prototypes"
34
Aaron Durbin0370bcf2018-09-05 09:37:11 -060035static void __noreturn ubsan_abort(const struct ubsan_source_location *location,
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070036 const char *violation) {
37 static const struct ubsan_source_location unknown_location = {
38 "<unknown file>",
39 0,
40 0,
41 };
42
43 if (!location || !location->filename)
44 location = &unknown_location;
45 printk(BIOS_ERR, "%s %s:%lu:%lu\n", violation, location->filename,
46 (unsigned long int)location->line,
47 (unsigned long int)location->column);
48 die("ubsan: unrecoverable error.\n");
49}
50
51#define ABORT_VARIANT(name, params, call) \
Aaron Durbin0370bcf2018-09-05 09:37:11 -060052 __noreturn void __ubsan_handle_##name##_abort params; \
53 __noreturn void __ubsan_handle_##name##_abort params { \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020054 __ubsan_handle_##name call; \
55 __builtin_unreachable(); \
56 }
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070057
58#define ABORT_VARIANT_VP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020059 ABORT_VARIANT(name, (void *a), (a))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070060#define ABORT_VARIANT_VP_VP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020061 ABORT_VARIANT(name, (void *a, void *b), (a, b))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070062#define ABORT_VARIANT_VP_IP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020063 ABORT_VARIANT(name, (void *a, intptr_t b), (a, b))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070064#define ABORT_VARIANT_VP_VP_VP(name) \
Jonathan Neuschäferab002fa2017-09-19 15:19:53 +020065 ABORT_VARIANT(name, (void *a, void *b, void *c), (a, b, c))
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070066
67struct ubsan_type_mismatch_data {
68 struct ubsan_source_location location;
69 struct ubsan_type_descriptor *type;
70 uintptr_t alignment;
71 unsigned char type_check_kind;
72};
73
Angel Pons7d6dae62020-07-27 21:08:38 +020074void __ubsan_handle_type_mismatch_v1(void *data_raw, void *pointer_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070075{
76 const struct ubsan_type_mismatch_data *data =
77 (struct ubsan_type_mismatch_data *)data_raw;
78 ubsan_value_handle_t pointer = (ubsan_value_handle_t)pointer_raw;
79 const char *violation = "type mismatch";
80 if (!pointer)
81 violation = "null pointer access";
82 else if (data->alignment && (pointer & (data->alignment - 1)))
83 violation = "unaligned access";
84 ubsan_abort(&data->location, violation);
85}
86
Angel Pons7d6dae62020-07-27 21:08:38 +020087ABORT_VARIANT_VP_VP(type_mismatch_v1);
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -070088
89struct ubsan_overflow_data {
90 struct ubsan_source_location location;
91 struct ubsan_type_descriptor *type;
92};
93
94void __ubsan_handle_add_overflow(void *data_raw, void *lhs_raw,
95 void *rhs_raw)
96{
97 const struct ubsan_overflow_data *data
98 = (struct ubsan_overflow_data *)data_raw;
99 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
100 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
101 (void)lhs;
102 (void)rhs;
103 ubsan_abort(&data->location, "addition overflow");
104}
105
106ABORT_VARIANT_VP_VP_VP(add_overflow);
107
108void __ubsan_handle_sub_overflow(void *data_raw, void *lhs_raw,
109 void *rhs_raw)
110{
111 const struct ubsan_overflow_data *data
112 = (struct ubsan_overflow_data *)data_raw;
113 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
114 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
115 (void)lhs;
116 (void)rhs;
117 ubsan_abort(&data->location, "subtraction overflow");
118}
119
120ABORT_VARIANT_VP_VP_VP(sub_overflow);
121
122void __ubsan_handle_mul_overflow(void *data_raw, void *lhs_raw,
123 void *rhs_raw)
124{
125 const struct ubsan_overflow_data *data
126 = (struct ubsan_overflow_data *)data_raw;
127 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
128 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
129 (void)lhs;
130 (void)rhs;
131 ubsan_abort(&data->location, "multiplication overflow");
132}
133
134ABORT_VARIANT_VP_VP_VP(mul_overflow);
135
136void __ubsan_handle_negate_overflow(void *data_raw, void *old_value_raw)
137{
138 const struct ubsan_overflow_data *data
139 = (struct ubsan_overflow_data *)data_raw;
140 ubsan_value_handle_t old_value
141 = (ubsan_value_handle_t)old_value_raw;
142 (void) old_value;
143 ubsan_abort(&data->location, "negation overflow");
144}
145
146ABORT_VARIANT_VP_VP(negate_overflow);
147
148void __ubsan_handle_divrem_overflow(void *data_raw, void *lhs_raw,
149 void *rhs_raw)
150{
151 const struct ubsan_overflow_data *data
152 = (struct ubsan_overflow_data *)data_raw;
153 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
154 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
155 (void)lhs;
156 (void)rhs;
157 ubsan_abort(&data->location, "division remainder overflow");
158}
159
160ABORT_VARIANT_VP_VP_VP(divrem_overflow);
161
Angel Pons7d6dae62020-07-27 21:08:38 +0200162struct ubsan_pointer_overflow_data {
163 struct ubsan_source_location location;
164};
165
166void __ubsan_handle_pointer_overflow(void *data_raw, void *base_raw, void *result_raw)
167{
168 const struct ubsan_pointer_overflow_data *data =
169 (struct ubsan_pointer_overflow_data *)data_raw;
170 ubsan_value_handle_t base = (ubsan_value_handle_t)base_raw;
171 ubsan_value_handle_t result = (ubsan_value_handle_t)result_raw;
172 (void)base;
173 (void)result;
174 ubsan_abort(&data->location, "pointer overflow");
175}
176
177ABORT_VARIANT_VP_VP_VP(pointer_overflow);
178
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700179struct ubsan_shift_out_of_bounds_data {
180 struct ubsan_source_location location;
181 struct ubsan_type_descriptor *lhs_type;
182 struct ubsan_type_descriptor *rhs_type;
183};
184
185void __ubsan_handle_shift_out_of_bounds(void *data_raw, void *lhs_raw,
186 void *rhs_raw)
187{
188 const struct ubsan_shift_out_of_bounds_data *data =
189 (struct ubsan_shift_out_of_bounds_data *)data_raw;
190 ubsan_value_handle_t lhs = (ubsan_value_handle_t)lhs_raw;
191 ubsan_value_handle_t rhs = (ubsan_value_handle_t)rhs_raw;
192 (void)lhs;
193 (void)rhs;
194 ubsan_abort(&data->location, "shift out of bounds");
195}
196
197ABORT_VARIANT_VP_VP_VP(shift_out_of_bounds);
198
199struct ubsan_out_of_bounds_data {
200 struct ubsan_source_location location;
201 struct ubsan_type_descriptor *array_type;
202 struct ubsan_type_descriptor *index_type;
203};
204
205void __ubsan_handle_out_of_bounds(void *data_raw, void *index_raw)
206{
207 const struct ubsan_out_of_bounds_data *data =
208 (struct ubsan_out_of_bounds_data *)data_raw;
209 ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
210 (void)index;
211 ubsan_abort(&data->location, "out of bounds");
212}
213
214ABORT_VARIANT_VP_VP(out_of_bounds);
215
216struct ubsan_unreachable_data {
217 struct ubsan_source_location location;
218};
219
Aaron Durbin0370bcf2018-09-05 09:37:11 -0600220void __noreturn __ubsan_handle_builtin_unreachable(void *data_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700221{
222 struct ubsan_unreachable_data *data =
223 (struct ubsan_unreachable_data *)data_raw;
224 ubsan_abort(&data->location, "reached unreachable");
225}
226
Aaron Durbin0370bcf2018-09-05 09:37:11 -0600227void __noreturn __ubsan_handle_missing_return(void *data_raw)
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700228{
229 const struct ubsan_unreachable_data *data =
230 (struct ubsan_unreachable_data *)data_raw;
231 ubsan_abort(&data->location, "missing return");
232}
233
234struct ubsan_vla_bound_data {
235 struct ubsan_source_location location;
236 struct ubsan_type_descriptor *type;
237};
238
239void __ubsan_handle_vla_bound_not_positive(void *data_raw, void *bound_raw)
240{
241 const struct ubsan_vla_bound_data *data
242 = (struct ubsan_vla_bound_data *)data_raw;
243 ubsan_value_handle_t bound = (ubsan_value_handle_t)bound_raw;
244 (void)bound;
245 ubsan_abort(&data->location, "negative variable array length");
246}
247
248ABORT_VARIANT_VP_VP(vla_bound_not_positive);
249
250struct ubsan_float_cast_overflow_data {
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700251 struct ubsan_source_location location;
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700252 struct ubsan_type_descriptor *from_type;
253 struct ubsan_type_descriptor *to_type;
254};
255
256void __ubsan_handle_float_cast_overflow(void *data_raw, void *from_raw)
257{
258 struct ubsan_float_cast_overflow_data *data =
259 (struct ubsan_float_cast_overflow_data *)data_raw;
260 ubsan_value_handle_t from = (ubsan_value_handle_t)from_raw;
261 (void) from;
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700262 ubsan_abort(&data->location, "float cast overflow");
Ryan Salsamendiab37e9a2017-06-11 21:07:31 -0700263}
264
265ABORT_VARIANT_VP_VP(float_cast_overflow);
266
267struct ubsan_invalid_value_data {
268 struct ubsan_source_location location;
269 struct ubsan_type_descriptor *type;
270};
271
272void __ubsan_handle_load_invalid_value(void *data_raw, void *value_raw)
273{
274 const struct ubsan_invalid_value_data *data =
275 (struct ubsan_invalid_value_data *)data_raw;
276 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
277 (void)value;
278 ubsan_abort(&data->location, "invalid value load");
279}
280
281ABORT_VARIANT_VP_VP(load_invalid_value);
282
283struct ubsan_function_type_mismatch_data {
284 struct ubsan_source_location location;
285 struct ubsan_type_descriptor *type;
286};
287
288void __ubsan_handle_function_type_mismatch(void *data_raw, void *value_raw)
289{
290 const struct ubsan_function_type_mismatch_data *data =
291 (struct ubsan_function_type_mismatch_data *)data_raw;
292 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
293 (void)value;
294 ubsan_abort(&data->location, "function type mismatch");
295}
296
297ABORT_VARIANT_VP_VP(function_type_mismatch);
298
299struct ubsan_nonnull_return_data {
300 struct ubsan_source_location location;
301 struct ubsan_source_location attr_location;
302};
303
304void __ubsan_handle_nonnull_return(void *data_raw)
305{
306 const struct ubsan_nonnull_return_data *data =
307 (struct ubsan_nonnull_return_data *)data_raw;
308 ubsan_abort(&data->location, "null return");
309}
310
311ABORT_VARIANT_VP(nonnull_return);
312
313struct ubsan_nonnull_arg_data {
314 struct ubsan_source_location location;
315 struct ubsan_source_location attr_location;
316};
317
318/*
319* TODO: GCC's libubsan does not have the second parameter, but its builtin
320* somehow has it and conflict if we don't match it.
321*/
322void __ubsan_handle_nonnull_arg(void *data_raw, intptr_t index_raw)
323{
324 const struct ubsan_nonnull_arg_data *data =
325 (struct ubsan_nonnull_arg_data *)data_raw;
326 ubsan_value_handle_t index = (ubsan_value_handle_t)index_raw;
327 (void)index;
328 ubsan_abort(&data->location, "null argument");
329}
330
331ABORT_VARIANT_VP_IP(nonnull_arg);
332
333struct ubsan_cfi_bad_icall_data {
334 struct ubsan_source_location location;
335 struct ubsan_type_descriptor *type;
336};
337
338void __ubsan_handle_cfi_bad_icall(void *data_raw, void *value_raw)
339{
340 static const char *abort_text
341 = "cfi: integrity failure during indirect call.";
342 const struct ubsan_cfi_bad_icall_data *data =
343 (struct ubsan_cfi_bad_icall_data *)data_raw;
344 ubsan_value_handle_t value = (ubsan_value_handle_t)value_raw;
345 (void)value;
346 ubsan_abort(&data->location, abort_text);
347}
348
349ABORT_VARIANT_VP_VP(cfi_bad_icall);