xref: /optee_os/lib/libutils/ext/ubsan.c (revision fa1190c619ea2b2312238d91a7256a94b5966d72)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  */
5 
6 #include <atomic.h>
7 #include <compiler.h>
8 #include <string.h>
9 #include <trace.h>
10 #include <types_ext.h>
11 #include <util.h>
12 
13 #if defined(__KERNEL__)
14 # include <kernel/panic.h>
15 #elif defined(__LDELF__)
16 # include <ldelf_syscalls.h>
17 #else
18 # include <utee_syscalls.h>
19 #endif
20 
21 #define UBSAN_LOC_REPORTED BIT32(31)
22 
23 struct source_location {
24 	const char *file_name;
25 	uint32_t line;
26 	uint32_t column;
27 };
28 
ubsan_panic(void)29 static void __noreturn ubsan_panic(void)
30 {
31 #if defined(__KERNEL__)
32 	panic();
33 #elif defined(__LDELF__)
34 	_ldelf_panic(2);
35 #else
36 	_utee_panic(TEE_ERROR_GENERIC);
37 #endif
38 	/*
39 	 * _ldelf_panic and _utee_panic are not marked as noreturn,
40 	 * however they should be. To prevent "‘noreturn’ function
41 	 * does return" warning the while loop is used.
42 	 */
43 	while (1)
44 		;
45 }
46 
was_already_reported(struct source_location * loc)47 static bool was_already_reported(struct source_location *loc)
48 {
49 	uint32_t column = loc->column;
50 
51 	if (column & UBSAN_LOC_REPORTED)
52 		return true;
53 
54 	return !atomic_cas_u32(&loc->column, &column,
55 			       column | UBSAN_LOC_REPORTED);
56 }
57 
58 struct type_descriptor {
59 	uint16_t type_kind;
60 	uint16_t type_info;
61 	char type_name[1];
62 };
63 
64 struct type_mismatch_data {
65 	struct source_location loc;
66 	struct type_descriptor *type;
67 	unsigned long alignment;
68 	unsigned char type_check_kind;
69 };
70 
71 struct overflow_data {
72 	struct source_location loc;
73 	struct type_descriptor *type;
74 };
75 
76 struct shift_out_of_bounds_data {
77 	struct source_location loc;
78 	struct type_descriptor *lhs_type;
79 	struct type_descriptor *rhs_type;
80 };
81 
82 struct out_of_bounds_data {
83 	struct source_location loc;
84 	struct type_descriptor *array_type;
85 	struct type_descriptor *index_type;
86 };
87 
88 struct unreachable_data {
89 	struct source_location loc;
90 };
91 
92 struct vla_bound_data {
93 	struct source_location loc;
94 	struct type_descriptor *type;
95 };
96 
97 struct invalid_value_data {
98 	struct source_location loc;
99 	struct type_descriptor *type;
100 };
101 
102 struct nonnull_arg_data {
103 	struct source_location loc;
104 };
105 
106 struct invalid_builtin_data {
107 	struct source_location loc;
108 	unsigned char kind;
109 };
110 
111 /*
112  * When compiling with -fsanitize=undefined the compiler expects functions
113  * with the following signatures. The functions are never called directly,
114  * only when undefined behavior is detected in instrumented code.
115  */
116 void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
117 				  unsigned long ptr);
118 void __ubsan_handle_type_mismatch_v1(void *data_, void *ptr);
119 void __ubsan_handle_add_overflow(void *data_, void *lhs, void *rhs);
120 void __ubsan_handle_sub_overflow(void *data_, void *lhs, void *rhs);
121 void __ubsan_handle_mul_overflow(void *data_, void *lhs, void *rhs);
122 void __ubsan_handle_negate_overflow(void *data_, void *old_val);
123 void __ubsan_handle_divrem_overflow(void *data_, void *lhs, void *rhs);
124 void __ubsan_handle_pointer_overflow(void *data_, void *lhs, void *rhs);
125 void __ubsan_handle_shift_out_of_bounds(void *data_, void *lhs, void *rhs);
126 void __ubsan_handle_out_of_bounds(void *data_, void *idx);
127 void __ubsan_handle_builtin_unreachable(void *data_);
128 void __ubsan_handle_missing_return(void *data_);
129 void __ubsan_handle_vla_bound_not_positive(void *data_, void *bound);
130 void __ubsan_handle_load_invalid_value(void *data_, void *val);
131 void __ubsan_handle_nonnull_arg(void *data_
132 #if __GCC_VERSION < 60000
133 				, size_t arg_no
134 #endif
135 			       );
136 void __ubsan_handle_invalid_builtin(void *data_);
137 
138 static bool should_panic = true;
139 
ubsan_handle_error(const char * func,struct source_location * loc,bool panic_flag)140 static void ubsan_handle_error(const char *func, struct source_location *loc,
141 			       bool panic_flag)
142 {
143 	const char *f = func;
144 	const char func_prefix[] = "__ubsan_handle";
145 
146 	if (was_already_reported(loc))
147 		return;
148 
149 	if (!memcmp(f, func_prefix, sizeof(func_prefix) - 1))
150 		f += sizeof(func_prefix);
151 
152 	EMSG_RAW("Undefined behavior %s at %s:%" PRIu32 " col %" PRIu32,
153 		 f, loc->file_name, loc->line, loc->column & ~UBSAN_LOC_REPORTED);
154 
155 	if (panic_flag)
156 		ubsan_panic();
157 }
158 
__ubsan_handle_type_mismatch(struct type_mismatch_data * data,unsigned long ptr __unused)159 void __ubsan_handle_type_mismatch(struct type_mismatch_data *data,
160 				  unsigned long ptr __unused)
161 {
162 	ubsan_handle_error(__func__, &data->loc, should_panic);
163 }
164 
__ubsan_handle_type_mismatch_v1(void * data_,void * ptr __unused)165 void __ubsan_handle_type_mismatch_v1(void *data_, void *ptr __unused)
166 {
167 	struct type_mismatch_data *data = data_;
168 
169 	ubsan_handle_error(__func__, &data->loc, should_panic);
170 }
171 
__ubsan_handle_add_overflow(void * data_,void * lhs __unused,void * rhs __unused)172 void __ubsan_handle_add_overflow(void *data_, void *lhs __unused,
173 				 void *rhs __unused)
174 {
175 	struct overflow_data *data = data_;
176 
177 	ubsan_handle_error(__func__, &data->loc, should_panic);
178 }
179 
__ubsan_handle_sub_overflow(void * data_,void * lhs __unused,void * rhs __unused)180 void __ubsan_handle_sub_overflow(void *data_, void *lhs __unused,
181 				 void *rhs __unused)
182 {
183 	struct overflow_data *data = data_;
184 
185 	ubsan_handle_error(__func__, &data->loc, should_panic);
186 }
187 
__ubsan_handle_mul_overflow(void * data_,void * lhs __unused,void * rhs __unused)188 void __ubsan_handle_mul_overflow(void *data_, void *lhs __unused,
189 				 void *rhs __unused)
190 {
191 	struct overflow_data *data = data_;
192 
193 	ubsan_handle_error(__func__, &data->loc, should_panic);
194 }
195 
__ubsan_handle_negate_overflow(void * data_,void * old_val __unused)196 void __ubsan_handle_negate_overflow(void *data_, void *old_val __unused)
197 {
198 	struct overflow_data *data = data_;
199 
200 	ubsan_handle_error(__func__, &data->loc, should_panic);
201 }
202 
__ubsan_handle_divrem_overflow(void * data_,void * lhs __unused,void * rhs __unused)203 void __ubsan_handle_divrem_overflow(void *data_, void *lhs __unused,
204 				    void *rhs __unused)
205 {
206 	struct overflow_data *data = data_;
207 
208 	ubsan_handle_error(__func__, &data->loc, should_panic);
209 }
210 
__ubsan_handle_pointer_overflow(void * data_,void * lhs __unused,void * rhs __unused)211 void __ubsan_handle_pointer_overflow(void *data_, void *lhs __unused,
212 				     void *rhs __unused)
213 {
214 	struct overflow_data *data = data_;
215 
216 	ubsan_handle_error(__func__, &data->loc, should_panic);
217 }
218 
__ubsan_handle_shift_out_of_bounds(void * data_,void * lhs __unused,void * rhs __unused)219 void __ubsan_handle_shift_out_of_bounds(void *data_, void *lhs __unused,
220 					void *rhs __unused)
221 {
222 	struct shift_out_of_bounds_data *data = data_;
223 
224 	ubsan_handle_error(__func__, &data->loc, should_panic);
225 }
226 
__ubsan_handle_out_of_bounds(void * data_,void * idx __unused)227 void __ubsan_handle_out_of_bounds(void *data_, void *idx __unused)
228 {
229 	struct out_of_bounds_data *data = data_;
230 
231 	ubsan_handle_error(__func__, &data->loc, should_panic);
232 }
233 
__ubsan_handle_builtin_unreachable(void * data_)234 void __noreturn __ubsan_handle_builtin_unreachable(void *data_)
235 {
236 	struct unreachable_data *data = data_;
237 
238 	ubsan_handle_error(__func__, &data->loc, false);
239 	ubsan_panic();
240 }
241 
__ubsan_handle_missing_return(void * data_)242 void __noreturn __ubsan_handle_missing_return(void *data_)
243 {
244 	struct unreachable_data *data = data_;
245 
246 	ubsan_handle_error(__func__, &data->loc, false);
247 	ubsan_panic();
248 }
249 
__ubsan_handle_vla_bound_not_positive(void * data_,void * bound __unused)250 void __ubsan_handle_vla_bound_not_positive(void *data_, void *bound __unused)
251 {
252 	struct vla_bound_data *data = data_;
253 
254 	ubsan_handle_error(__func__, &data->loc, should_panic);
255 }
256 
__ubsan_handle_load_invalid_value(void * data_,void * val __unused)257 void __ubsan_handle_load_invalid_value(void *data_, void *val __unused)
258 {
259 	struct invalid_value_data *data = data_;
260 
261 	ubsan_handle_error(__func__, &data->loc, should_panic);
262 }
263 
__ubsan_handle_nonnull_arg(void * data_,size_t arg_no __unused)264 void __ubsan_handle_nonnull_arg(void *data_
265 #if __GCC_VERSION < 60000
266 				, size_t arg_no __unused
267 #endif
268 			       )
269 {
270 	struct nonnull_arg_data *data = data_;
271 
272 	ubsan_handle_error(__func__, &data->loc, should_panic);
273 }
274 
__ubsan_handle_invalid_builtin(void * data_)275 void __ubsan_handle_invalid_builtin(void *data_)
276 {
277 	struct invalid_builtin_data *data = data_;
278 
279 	ubsan_handle_error(__func__, &data->loc, should_panic);
280 }
281