xref: /aosp_15_r20/external/coreboot/src/lib/ubsan.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: ISC */
2 /* ubsan/ubsan.c
3  * Undefined behavior sanitizer runtime support.
4  *
5  * Adapted from:
6  * https://gitlab.com/sortix/sortix/raw/master/libc/ubsan/ubsan.c
7  *
8  * Copyright (c) 2014, 2015 Jonas 'Sortie' Termansen.
9  *
10  */
11 
12 #include <stdint.h>
13 #include <console/console.h>
14 
15 struct ubsan_source_location {
16 	const char *filename;
17 	uint32_t line;
18 	uint32_t column;
19 };
20 
21 struct ubsan_type_descriptor {
22 	uint16_t type_kind;
23 	uint16_t type_info;
24 	char type_name[];
25 };
26 
27 typedef 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 
ubsan_abort(const struct ubsan_source_location * location,const char * violation)35 static void __noreturn ubsan_abort(const struct ubsan_source_location *location,
36 			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) \
52 	__noreturn void __ubsan_handle_##name##_abort params; \
53 	__noreturn void __ubsan_handle_##name##_abort params { \
54 		__ubsan_handle_##name call; \
55 		__builtin_unreachable(); \
56 	}
57 
58 #define ABORT_VARIANT_VP(name) \
59 	ABORT_VARIANT(name, (void *a), (a))
60 #define ABORT_VARIANT_VP_VP(name) \
61 	ABORT_VARIANT(name, (void *a, void *b), (a, b))
62 #define ABORT_VARIANT_VP_IP(name) \
63 	ABORT_VARIANT(name, (void *a, intptr_t b), (a, b))
64 #define ABORT_VARIANT_VP_VP_VP(name) \
65 	ABORT_VARIANT(name, (void *a, void *b, void *c), (a, b, c))
66 
67 struct 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 
__ubsan_handle_type_mismatch_v1(void * data_raw,void * pointer_raw)74 void __ubsan_handle_type_mismatch_v1(void *data_raw, void *pointer_raw)
75 {
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 
87 ABORT_VARIANT_VP_VP(type_mismatch_v1);
88 
89 struct ubsan_overflow_data {
90 	struct ubsan_source_location location;
91 	struct ubsan_type_descriptor *type;
92 };
93 
__ubsan_handle_add_overflow(void * data_raw,void * lhs_raw,void * rhs_raw)94 void __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 
106 ABORT_VARIANT_VP_VP_VP(add_overflow);
107 
__ubsan_handle_sub_overflow(void * data_raw,void * lhs_raw,void * rhs_raw)108 void __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 
120 ABORT_VARIANT_VP_VP_VP(sub_overflow);
121 
__ubsan_handle_mul_overflow(void * data_raw,void * lhs_raw,void * rhs_raw)122 void __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 
134 ABORT_VARIANT_VP_VP_VP(mul_overflow);
135 
__ubsan_handle_negate_overflow(void * data_raw,void * old_value_raw)136 void __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 
146 ABORT_VARIANT_VP_VP(negate_overflow);
147 
__ubsan_handle_divrem_overflow(void * data_raw,void * lhs_raw,void * rhs_raw)148 void __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 
160 ABORT_VARIANT_VP_VP_VP(divrem_overflow);
161 
162 struct ubsan_pointer_overflow_data {
163 	struct ubsan_source_location location;
164 };
165 
__ubsan_handle_pointer_overflow(void * data_raw,void * base_raw,void * result_raw)166 void __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 
177 ABORT_VARIANT_VP_VP_VP(pointer_overflow);
178 
179 struct 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 
__ubsan_handle_shift_out_of_bounds(void * data_raw,void * lhs_raw,void * rhs_raw)185 void __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 
197 ABORT_VARIANT_VP_VP_VP(shift_out_of_bounds);
198 
199 struct 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 
__ubsan_handle_out_of_bounds(void * data_raw,void * index_raw)205 void __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 
214 ABORT_VARIANT_VP_VP(out_of_bounds);
215 
216 struct ubsan_unreachable_data {
217 	struct ubsan_source_location location;
218 };
219 
__ubsan_handle_builtin_unreachable(void * data_raw)220 void __noreturn __ubsan_handle_builtin_unreachable(void *data_raw)
221 {
222 	struct ubsan_unreachable_data *data =
223 		(struct ubsan_unreachable_data *)data_raw;
224 	ubsan_abort(&data->location, "reached unreachable");
225 }
226 
__ubsan_handle_missing_return(void * data_raw)227 void __noreturn __ubsan_handle_missing_return(void *data_raw)
228 {
229 	const struct ubsan_unreachable_data *data =
230 		(struct ubsan_unreachable_data *)data_raw;
231 	ubsan_abort(&data->location, "missing return");
232 }
233 
234 struct ubsan_vla_bound_data {
235 	struct ubsan_source_location location;
236 	struct ubsan_type_descriptor *type;
237 };
238 
__ubsan_handle_vla_bound_not_positive(void * data_raw,void * bound_raw)239 void __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 
248 ABORT_VARIANT_VP_VP(vla_bound_not_positive);
249 
250 struct ubsan_float_cast_overflow_data {
251 	struct ubsan_source_location location;
252 	struct ubsan_type_descriptor *from_type;
253 	struct ubsan_type_descriptor *to_type;
254 };
255 
__ubsan_handle_float_cast_overflow(void * data_raw,void * from_raw)256 void __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;
262 	ubsan_abort(&data->location, "float cast overflow");
263 }
264 
265 ABORT_VARIANT_VP_VP(float_cast_overflow);
266 
267 struct ubsan_invalid_value_data {
268 	struct ubsan_source_location location;
269 	struct ubsan_type_descriptor *type;
270 };
271 
__ubsan_handle_load_invalid_value(void * data_raw,void * value_raw)272 void __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 
281 ABORT_VARIANT_VP_VP(load_invalid_value);
282 
283 struct ubsan_function_type_mismatch_data {
284 	struct ubsan_source_location location;
285 	struct ubsan_type_descriptor *type;
286 };
287 
__ubsan_handle_function_type_mismatch(void * data_raw,void * value_raw)288 void __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 
297 ABORT_VARIANT_VP_VP(function_type_mismatch);
298 
299 struct ubsan_nonnull_return_data {
300 	struct ubsan_source_location location;
301 	struct ubsan_source_location attr_location;
302 };
303 
__ubsan_handle_nonnull_return(void * data_raw)304 void __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 
311 ABORT_VARIANT_VP(nonnull_return);
312 
313 struct 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 */
__ubsan_handle_nonnull_arg(void * data_raw,intptr_t index_raw)322 void __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 
331 ABORT_VARIANT_VP_IP(nonnull_arg);
332 
333 struct ubsan_cfi_bad_icall_data {
334 	struct ubsan_source_location location;
335 	struct ubsan_type_descriptor *type;
336 };
337 
__ubsan_handle_cfi_bad_icall(void * data_raw,void * value_raw)338 void __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 
349 ABORT_VARIANT_VP_VP(cfi_bad_icall);
350