Blame view
lib/ubsan.c
10.5 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
c6d308534 UBSAN: run-time u... |
2 3 4 5 6 |
/* * UBSAN error reporting functions * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> |
c6d308534 UBSAN: run-time u... |
7 8 9 10 11 12 13 14 15 |
*/ #include <linux/bitops.h> #include <linux/bug.h> #include <linux/ctype.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/sched.h> |
d08965a27 x86/uaccess, ubsa... |
16 |
#include <linux/uaccess.h> |
c6d308534 UBSAN: run-time u... |
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
#include "ubsan.h" const char *type_check_kinds[] = { "load of", "store to", "reference binding to", "member access within", "member call on", "constructor call on", "downcast of", "downcast of" }; #define REPORTED_BIT 31 #if (BITS_PER_LONG == 64) && defined(__BIG_ENDIAN) #define COLUMN_MASK (~(1U << REPORTED_BIT)) #define LINE_MASK (~0U) #else #define COLUMN_MASK (~0U) #define LINE_MASK (~(1U << REPORTED_BIT)) #endif #define VALUE_LENGTH 40 static bool was_reported(struct source_location *location) { return test_and_set_bit(REPORTED_BIT, &location->reported); } |
c6d308534 UBSAN: run-time u... |
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
static bool suppress_report(struct source_location *loc) { return current->in_ubsan || was_reported(loc); } static bool type_is_int(struct type_descriptor *type) { return type->type_kind == type_kind_int; } static bool type_is_signed(struct type_descriptor *type) { WARN_ON(!type_is_int(type)); return type->type_info & 1; } static unsigned type_bit_width(struct type_descriptor *type) { return 1 << (type->type_info >> 1); } static bool is_inline_int(struct type_descriptor *type) { unsigned inline_bits = sizeof(unsigned long)*8; unsigned bits = type_bit_width(type); WARN_ON(!type_is_int(type)); return bits <= inline_bits; } |
f0996bc29 ubsan: Fix nasty ... |
77 |
static s_max get_signed_val(struct type_descriptor *type, void *val) |
c6d308534 UBSAN: run-time u... |
78 79 80 |
{ if (is_inline_int(type)) { unsigned extra_bits = sizeof(s_max)*8 - type_bit_width(type); |
f0996bc29 ubsan: Fix nasty ... |
81 82 83 |
unsigned long ulong_val = (unsigned long)val; return ((s_max)ulong_val) << extra_bits >> extra_bits; |
c6d308534 UBSAN: run-time u... |
84 85 86 87 88 89 90 |
} if (type_bit_width(type) == 64) return *(s64 *)val; return *(s_max *)val; } |
f0996bc29 ubsan: Fix nasty ... |
91 |
static bool val_is_negative(struct type_descriptor *type, void *val) |
c6d308534 UBSAN: run-time u... |
92 93 94 |
{ return type_is_signed(type) && get_signed_val(type, val) < 0; } |
f0996bc29 ubsan: Fix nasty ... |
95 |
static u_max get_unsigned_val(struct type_descriptor *type, void *val) |
c6d308534 UBSAN: run-time u... |
96 97 |
{ if (is_inline_int(type)) |
f0996bc29 ubsan: Fix nasty ... |
98 |
return (unsigned long)val; |
c6d308534 UBSAN: run-time u... |
99 100 101 102 103 104 105 106 |
if (type_bit_width(type) == 64) return *(u64 *)val; return *(u_max *)val; } static void val_to_string(char *str, size_t size, struct type_descriptor *type, |
f0996bc29 ubsan: Fix nasty ... |
107 |
void *value) |
c6d308534 UBSAN: run-time u... |
108 109 110 |
{ if (type_is_int(type)) { if (type_bit_width(type) == 128) { |
c12d3362a int128: move __ui... |
111 |
#if defined(CONFIG_ARCH_SUPPORTS_INT128) |
c6d308534 UBSAN: run-time u... |
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
u_max val = get_unsigned_val(type, value); scnprintf(str, size, "0x%08x%08x%08x%08x", (u32)(val >> 96), (u32)(val >> 64), (u32)(val >> 32), (u32)(val)); #else WARN_ON(1); #endif } else if (type_is_signed(type)) { scnprintf(str, size, "%lld", (s64)get_signed_val(type, value)); } else { scnprintf(str, size, "%llu", (u64)get_unsigned_val(type, value)); } } } |
ef065653e ubsan: include bu... |
131 |
static void ubsan_prologue(struct source_location *loc, const char *reason) |
c6d308534 UBSAN: run-time u... |
132 133 |
{ current->in_ubsan++; |
c6d308534 UBSAN: run-time u... |
134 135 136 137 |
pr_err("========================================" "======================================== "); |
ef065653e ubsan: include bu... |
138 139 140 |
pr_err("UBSAN: %s in %s:%d:%d ", reason, loc->file_name, loc->line & LINE_MASK, loc->column & COLUMN_MASK); |
c6d308534 UBSAN: run-time u... |
141 |
} |
ce5c31db3 lib/ubsan: don't ... |
142 |
static void ubsan_epilogue(void) |
c6d308534 UBSAN: run-time u... |
143 144 145 146 147 |
{ dump_stack(); pr_err("========================================" "======================================== "); |
ce5c31db3 lib/ubsan: don't ... |
148 |
|
c6d308534 UBSAN: run-time u... |
149 |
current->in_ubsan--; |
1d28c8d6d ubsan: check pani... |
150 151 152 153 154 155 156 157 158 159 160 161 |
if (panic_on_warn) { /* * This thread may hit another WARN() in the panic path. * Resetting this prevents additional WARN() from panicking the * system on this thread. Other threads are blocked by the * panic_mutex in panic(). */ panic_on_warn = 0; panic("panic_on_warn set ... "); } |
c6d308534 UBSAN: run-time u... |
162 |
} |
f0996bc29 ubsan: Fix nasty ... |
163 164 |
static void handle_overflow(struct overflow_data *data, void *lhs, void *rhs, char op) |
c6d308534 UBSAN: run-time u... |
165 166 167 |
{ struct type_descriptor *type = data->type; |
c6d308534 UBSAN: run-time u... |
168 169 170 171 172 |
char lhs_val_str[VALUE_LENGTH]; char rhs_val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ef065653e ubsan: include bu... |
173 174 175 |
ubsan_prologue(&data->location, type_is_signed(type) ? "signed-integer-overflow" : "unsigned-integer-overflow"); |
c6d308534 UBSAN: run-time u... |
176 177 178 |
val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs); val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs); |
c6d308534 UBSAN: run-time u... |
179 180 181 182 183 184 |
pr_err("%s %c %s cannot be represented in type %s ", lhs_val_str, op, rhs_val_str, type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
185 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
186 |
} |
469cbd016 lib/ubsan.c: fix ... |
187 |
void __ubsan_handle_add_overflow(void *data, |
f0996bc29 ubsan: Fix nasty ... |
188 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
189 190 191 192 193 |
{ handle_overflow(data, lhs, rhs, '+'); } EXPORT_SYMBOL(__ubsan_handle_add_overflow); |
469cbd016 lib/ubsan.c: fix ... |
194 |
void __ubsan_handle_sub_overflow(void *data, |
f0996bc29 ubsan: Fix nasty ... |
195 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
196 197 198 199 |
{ handle_overflow(data, lhs, rhs, '-'); } EXPORT_SYMBOL(__ubsan_handle_sub_overflow); |
469cbd016 lib/ubsan.c: fix ... |
200 |
void __ubsan_handle_mul_overflow(void *data, |
f0996bc29 ubsan: Fix nasty ... |
201 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
202 203 204 205 |
{ handle_overflow(data, lhs, rhs, '*'); } EXPORT_SYMBOL(__ubsan_handle_mul_overflow); |
469cbd016 lib/ubsan.c: fix ... |
206 |
void __ubsan_handle_negate_overflow(void *_data, void *old_val) |
c6d308534 UBSAN: run-time u... |
207 |
{ |
469cbd016 lib/ubsan.c: fix ... |
208 |
struct overflow_data *data = _data; |
c6d308534 UBSAN: run-time u... |
209 210 211 212 |
char old_val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ef065653e ubsan: include bu... |
213 |
ubsan_prologue(&data->location, "negation-overflow"); |
c6d308534 UBSAN: run-time u... |
214 215 216 217 218 219 |
val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val); pr_err("negation of %s cannot be represented in type %s: ", old_val_str, data->type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
220 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
221 222 |
} EXPORT_SYMBOL(__ubsan_handle_negate_overflow); |
469cbd016 lib/ubsan.c: fix ... |
223 |
void __ubsan_handle_divrem_overflow(void *_data, void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
224 |
{ |
469cbd016 lib/ubsan.c: fix ... |
225 |
struct overflow_data *data = _data; |
c6d308534 UBSAN: run-time u... |
226 227 228 229 |
char rhs_val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ef065653e ubsan: include bu... |
230 |
ubsan_prologue(&data->location, "division-overflow"); |
c6d308534 UBSAN: run-time u... |
231 232 233 234 235 236 237 238 239 240 |
val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs); if (type_is_signed(data->type) && get_signed_val(data->type, rhs) == -1) pr_err("division of %s by -1 cannot be represented in type %s ", rhs_val_str, data->type->type_name); else pr_err("division by zero "); |
ce5c31db3 lib/ubsan: don't ... |
241 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
242 243 |
} EXPORT_SYMBOL(__ubsan_handle_divrem_overflow); |
42440c1f9 lib/ubsan: add ty... |
244 |
static void handle_null_ptr_deref(struct type_mismatch_data_common *data) |
c6d308534 UBSAN: run-time u... |
245 |
{ |
42440c1f9 lib/ubsan: add ty... |
246 |
if (suppress_report(data->location)) |
c6d308534 UBSAN: run-time u... |
247 |
return; |
ef065653e ubsan: include bu... |
248 |
ubsan_prologue(data->location, "null-ptr-deref"); |
c6d308534 UBSAN: run-time u... |
249 250 251 252 253 |
pr_err("%s null pointer of type %s ", type_check_kinds[data->type_check_kind], data->type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
254 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
255 |
} |
42440c1f9 lib/ubsan: add ty... |
256 |
static void handle_misaligned_access(struct type_mismatch_data_common *data, |
c6d308534 UBSAN: run-time u... |
257 258 |
unsigned long ptr) { |
42440c1f9 lib/ubsan: add ty... |
259 |
if (suppress_report(data->location)) |
c6d308534 UBSAN: run-time u... |
260 |
return; |
ef065653e ubsan: include bu... |
261 |
ubsan_prologue(data->location, "misaligned-access"); |
c6d308534 UBSAN: run-time u... |
262 263 264 265 266 267 268 |
pr_err("%s misaligned address %p for type %s ", type_check_kinds[data->type_check_kind], (void *)ptr, data->type->type_name); pr_err("which requires %ld byte alignment ", data->alignment); |
ce5c31db3 lib/ubsan: don't ... |
269 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
270 |
} |
42440c1f9 lib/ubsan: add ty... |
271 |
static void handle_object_size_mismatch(struct type_mismatch_data_common *data, |
c6d308534 UBSAN: run-time u... |
272 273 |
unsigned long ptr) { |
42440c1f9 lib/ubsan: add ty... |
274 |
if (suppress_report(data->location)) |
c6d308534 UBSAN: run-time u... |
275 |
return; |
ef065653e ubsan: include bu... |
276 |
ubsan_prologue(data->location, "object-size-mismatch"); |
901d805c3 UBSAN: fix typo i... |
277 278 |
pr_err("%s address %p with insufficient space ", |
c6d308534 UBSAN: run-time u... |
279 280 281 282 |
type_check_kinds[data->type_check_kind], (void *) ptr); pr_err("for an object of type %s ", data->type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
283 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
284 |
} |
42440c1f9 lib/ubsan: add ty... |
285 |
static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data, |
c6d308534 UBSAN: run-time u... |
286 287 |
unsigned long ptr) { |
d08965a27 x86/uaccess, ubsa... |
288 |
unsigned long flags = user_access_save(); |
c6d308534 UBSAN: run-time u... |
289 290 291 292 |
if (!ptr) handle_null_ptr_deref(data); else if (data->alignment && !IS_ALIGNED(ptr, data->alignment)) |
b8fe1120b lib/ubsan.c: s/mi... |
293 |
handle_misaligned_access(data, ptr); |
c6d308534 UBSAN: run-time u... |
294 295 |
else handle_object_size_mismatch(data, ptr); |
d08965a27 x86/uaccess, ubsa... |
296 297 |
user_access_restore(flags); |
c6d308534 UBSAN: run-time u... |
298 |
} |
42440c1f9 lib/ubsan: add ty... |
299 300 |
void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, |
f0996bc29 ubsan: Fix nasty ... |
301 |
void *ptr) |
42440c1f9 lib/ubsan: add ty... |
302 303 304 305 306 307 308 |
{ struct type_mismatch_data_common common_data = { .location = &data->location, .type = data->type, .alignment = data->alignment, .type_check_kind = data->type_check_kind }; |
f0996bc29 ubsan: Fix nasty ... |
309 |
ubsan_type_mismatch_common(&common_data, (unsigned long)ptr); |
42440c1f9 lib/ubsan: add ty... |
310 |
} |
c6d308534 UBSAN: run-time u... |
311 |
EXPORT_SYMBOL(__ubsan_handle_type_mismatch); |
469cbd016 lib/ubsan.c: fix ... |
312 |
void __ubsan_handle_type_mismatch_v1(void *_data, void *ptr) |
42440c1f9 lib/ubsan: add ty... |
313 |
{ |
469cbd016 lib/ubsan.c: fix ... |
314 |
struct type_mismatch_data_v1 *data = _data; |
42440c1f9 lib/ubsan: add ty... |
315 316 317 318 319 320 |
struct type_mismatch_data_common common_data = { .location = &data->location, .type = data->type, .alignment = 1UL << data->log_alignment, .type_check_kind = data->type_check_kind }; |
f0996bc29 ubsan: Fix nasty ... |
321 |
ubsan_type_mismatch_common(&common_data, (unsigned long)ptr); |
42440c1f9 lib/ubsan: add ty... |
322 323 |
} EXPORT_SYMBOL(__ubsan_handle_type_mismatch_v1); |
469cbd016 lib/ubsan.c: fix ... |
324 |
void __ubsan_handle_out_of_bounds(void *_data, void *index) |
c6d308534 UBSAN: run-time u... |
325 |
{ |
469cbd016 lib/ubsan.c: fix ... |
326 |
struct out_of_bounds_data *data = _data; |
c6d308534 UBSAN: run-time u... |
327 328 329 330 |
char index_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ef065653e ubsan: include bu... |
331 |
ubsan_prologue(&data->location, "array-index-out-of-bounds"); |
c6d308534 UBSAN: run-time u... |
332 333 334 335 336 |
val_to_string(index_str, sizeof(index_str), data->index_type, index); pr_err("index %s is out of range for type %s ", index_str, data->array_type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
337 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
338 339 |
} EXPORT_SYMBOL(__ubsan_handle_out_of_bounds); |
469cbd016 lib/ubsan.c: fix ... |
340 |
void __ubsan_handle_shift_out_of_bounds(void *_data, void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
341 |
{ |
469cbd016 lib/ubsan.c: fix ... |
342 |
struct shift_out_of_bounds_data *data = _data; |
c6d308534 UBSAN: run-time u... |
343 344 345 346 |
struct type_descriptor *rhs_type = data->rhs_type; struct type_descriptor *lhs_type = data->lhs_type; char rhs_str[VALUE_LENGTH]; char lhs_str[VALUE_LENGTH]; |
9a50dcaf0 ubsan, x86: Annot... |
347 |
unsigned long ua_flags = user_access_save(); |
c6d308534 UBSAN: run-time u... |
348 349 |
if (suppress_report(&data->location)) |
9a50dcaf0 ubsan, x86: Annot... |
350 |
goto out; |
c6d308534 UBSAN: run-time u... |
351 |
|
ef065653e ubsan: include bu... |
352 |
ubsan_prologue(&data->location, "shift-out-of-bounds"); |
c6d308534 UBSAN: run-time u... |
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs); val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs); if (val_is_negative(rhs_type, rhs)) pr_err("shift exponent %s is negative ", rhs_str); else if (get_unsigned_val(rhs_type, rhs) >= type_bit_width(lhs_type)) pr_err("shift exponent %s is too large for %u-bit type %s ", rhs_str, type_bit_width(lhs_type), lhs_type->type_name); else if (val_is_negative(lhs_type, lhs)) pr_err("left shift of negative value %s ", lhs_str); else pr_err("left shift of %s by %s places cannot be" " represented in type %s ", lhs_str, rhs_str, lhs_type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
378 |
ubsan_epilogue(); |
9a50dcaf0 ubsan, x86: Annot... |
379 380 |
out: user_access_restore(ua_flags); |
c6d308534 UBSAN: run-time u... |
381 382 |
} EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds); |
469cbd016 lib/ubsan.c: fix ... |
383 |
void __ubsan_handle_builtin_unreachable(void *_data) |
c6d308534 UBSAN: run-time u... |
384 |
{ |
469cbd016 lib/ubsan.c: fix ... |
385 |
struct unreachable_data *data = _data; |
ef065653e ubsan: include bu... |
386 |
ubsan_prologue(&data->location, "unreachable"); |
c6d308534 UBSAN: run-time u... |
387 388 |
pr_err("calling __builtin_unreachable() "); |
ce5c31db3 lib/ubsan: don't ... |
389 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
390 391 392 |
panic("can't return from __builtin_unreachable()"); } EXPORT_SYMBOL(__ubsan_handle_builtin_unreachable); |
469cbd016 lib/ubsan.c: fix ... |
393 |
void __ubsan_handle_load_invalid_value(void *_data, void *val) |
c6d308534 UBSAN: run-time u... |
394 |
{ |
469cbd016 lib/ubsan.c: fix ... |
395 |
struct invalid_value_data *data = _data; |
c6d308534 UBSAN: run-time u... |
396 397 398 399 |
char val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ef065653e ubsan: include bu... |
400 |
ubsan_prologue(&data->location, "invalid-load"); |
c6d308534 UBSAN: run-time u... |
401 402 403 404 405 406 |
val_to_string(val_str, sizeof(val_str), data->type, val); pr_err("load of value %s is not a valid value for type %s ", val_str, data->type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
407 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
408 409 |
} EXPORT_SYMBOL(__ubsan_handle_load_invalid_value); |