Blame view
lib/ubsan.c
10 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 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 77 78 79 80 81 82 83 84 85 |
#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); } static void print_source_location(const char *prefix, struct source_location *loc) { pr_err("%s %s:%d:%d ", prefix, loc->file_name, loc->line & LINE_MASK, loc->column & COLUMN_MASK); } 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 ... |
86 |
static s_max get_signed_val(struct type_descriptor *type, void *val) |
c6d308534 UBSAN: run-time u... |
87 88 89 |
{ if (is_inline_int(type)) { unsigned extra_bits = sizeof(s_max)*8 - type_bit_width(type); |
f0996bc29 ubsan: Fix nasty ... |
90 91 92 |
unsigned long ulong_val = (unsigned long)val; return ((s_max)ulong_val) << extra_bits >> extra_bits; |
c6d308534 UBSAN: run-time u... |
93 94 95 96 97 98 99 |
} if (type_bit_width(type) == 64) return *(s64 *)val; return *(s_max *)val; } |
f0996bc29 ubsan: Fix nasty ... |
100 |
static bool val_is_negative(struct type_descriptor *type, void *val) |
c6d308534 UBSAN: run-time u... |
101 102 103 |
{ return type_is_signed(type) && get_signed_val(type, val) < 0; } |
f0996bc29 ubsan: Fix nasty ... |
104 |
static u_max get_unsigned_val(struct type_descriptor *type, void *val) |
c6d308534 UBSAN: run-time u... |
105 106 |
{ if (is_inline_int(type)) |
f0996bc29 ubsan: Fix nasty ... |
107 |
return (unsigned long)val; |
c6d308534 UBSAN: run-time u... |
108 109 110 111 112 113 114 115 |
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 ... |
116 |
void *value) |
c6d308534 UBSAN: run-time u... |
117 118 119 |
{ if (type_is_int(type)) { if (type_bit_width(type) == 128) { |
c12d3362a int128: move __ui... |
120 |
#if defined(CONFIG_ARCH_SUPPORTS_INT128) |
c6d308534 UBSAN: run-time u... |
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
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)); } } } |
ce5c31db3 lib/ubsan: don't ... |
140 |
static void ubsan_prologue(struct source_location *location) |
c6d308534 UBSAN: run-time u... |
141 142 |
{ current->in_ubsan++; |
c6d308534 UBSAN: run-time u... |
143 144 145 146 147 148 |
pr_err("========================================" "======================================== "); print_source_location("UBSAN: Undefined behaviour in", location); } |
ce5c31db3 lib/ubsan: don't ... |
149 |
static void ubsan_epilogue(void) |
c6d308534 UBSAN: run-time u... |
150 151 152 153 154 |
{ dump_stack(); pr_err("========================================" "======================================== "); |
ce5c31db3 lib/ubsan: don't ... |
155 |
|
c6d308534 UBSAN: run-time u... |
156 157 |
current->in_ubsan--; } |
f0996bc29 ubsan: Fix nasty ... |
158 159 |
static void handle_overflow(struct overflow_data *data, void *lhs, void *rhs, char op) |
c6d308534 UBSAN: run-time u... |
160 161 162 |
{ struct type_descriptor *type = data->type; |
c6d308534 UBSAN: run-time u... |
163 164 165 166 167 |
char lhs_val_str[VALUE_LENGTH]; char rhs_val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ce5c31db3 lib/ubsan: don't ... |
168 |
ubsan_prologue(&data->location); |
c6d308534 UBSAN: run-time u... |
169 170 171 172 173 174 175 176 177 178 179 180 |
val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs); val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs); pr_err("%s integer overflow: ", type_is_signed(type) ? "signed" : "unsigned"); 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 ... |
181 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
182 183 184 |
} void __ubsan_handle_add_overflow(struct overflow_data *data, |
f0996bc29 ubsan: Fix nasty ... |
185 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
186 187 188 189 190 191 192 |
{ handle_overflow(data, lhs, rhs, '+'); } EXPORT_SYMBOL(__ubsan_handle_add_overflow); void __ubsan_handle_sub_overflow(struct overflow_data *data, |
f0996bc29 ubsan: Fix nasty ... |
193 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
194 195 196 197 198 199 |
{ handle_overflow(data, lhs, rhs, '-'); } EXPORT_SYMBOL(__ubsan_handle_sub_overflow); void __ubsan_handle_mul_overflow(struct overflow_data *data, |
f0996bc29 ubsan: Fix nasty ... |
200 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
201 202 203 204 205 206 |
{ handle_overflow(data, lhs, rhs, '*'); } EXPORT_SYMBOL(__ubsan_handle_mul_overflow); void __ubsan_handle_negate_overflow(struct overflow_data *data, |
f0996bc29 ubsan: Fix nasty ... |
207 |
void *old_val) |
c6d308534 UBSAN: run-time u... |
208 |
{ |
c6d308534 UBSAN: run-time u... |
209 210 211 212 |
char old_val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ce5c31db3 lib/ubsan: don't ... |
213 |
ubsan_prologue(&data->location); |
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 223 224 225 |
} EXPORT_SYMBOL(__ubsan_handle_negate_overflow); void __ubsan_handle_divrem_overflow(struct overflow_data *data, |
f0996bc29 ubsan: Fix nasty ... |
226 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
227 |
{ |
c6d308534 UBSAN: run-time u... |
228 229 230 231 |
char rhs_val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ce5c31db3 lib/ubsan: don't ... |
232 |
ubsan_prologue(&data->location); |
c6d308534 UBSAN: run-time u... |
233 234 235 236 237 238 239 240 241 242 |
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 ... |
243 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
244 245 |
} EXPORT_SYMBOL(__ubsan_handle_divrem_overflow); |
42440c1f9 lib/ubsan: add ty... |
246 |
static void handle_null_ptr_deref(struct type_mismatch_data_common *data) |
c6d308534 UBSAN: run-time u... |
247 |
{ |
42440c1f9 lib/ubsan: add ty... |
248 |
if (suppress_report(data->location)) |
c6d308534 UBSAN: run-time u... |
249 |
return; |
ce5c31db3 lib/ubsan: don't ... |
250 |
ubsan_prologue(data->location); |
c6d308534 UBSAN: run-time u... |
251 252 253 254 255 |
pr_err("%s null pointer of type %s ", type_check_kinds[data->type_check_kind], data->type->type_name); |
ce5c31db3 lib/ubsan: don't ... |
256 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
257 |
} |
42440c1f9 lib/ubsan: add ty... |
258 |
static void handle_misaligned_access(struct type_mismatch_data_common *data, |
c6d308534 UBSAN: run-time u... |
259 260 |
unsigned long ptr) { |
42440c1f9 lib/ubsan: add ty... |
261 |
if (suppress_report(data->location)) |
c6d308534 UBSAN: run-time u... |
262 |
return; |
ce5c31db3 lib/ubsan: don't ... |
263 |
ubsan_prologue(data->location); |
c6d308534 UBSAN: run-time u... |
264 265 266 267 268 269 270 |
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 ... |
271 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
272 |
} |
42440c1f9 lib/ubsan: add ty... |
273 |
static void handle_object_size_mismatch(struct type_mismatch_data_common *data, |
c6d308534 UBSAN: run-time u... |
274 275 |
unsigned long ptr) { |
42440c1f9 lib/ubsan: add ty... |
276 |
if (suppress_report(data->location)) |
c6d308534 UBSAN: run-time u... |
277 |
return; |
ce5c31db3 lib/ubsan: don't ... |
278 |
ubsan_prologue(data->location); |
901d805c3 UBSAN: fix typo i... |
279 280 |
pr_err("%s address %p with insufficient space ", |
c6d308534 UBSAN: run-time u... |
281 282 283 284 |
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 ... |
285 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
286 |
} |
42440c1f9 lib/ubsan: add ty... |
287 |
static void ubsan_type_mismatch_common(struct type_mismatch_data_common *data, |
c6d308534 UBSAN: run-time u... |
288 289 |
unsigned long ptr) { |
d08965a27 x86/uaccess, ubsa... |
290 |
unsigned long flags = user_access_save(); |
c6d308534 UBSAN: run-time u... |
291 292 293 294 |
if (!ptr) handle_null_ptr_deref(data); else if (data->alignment && !IS_ALIGNED(ptr, data->alignment)) |
b8fe1120b lib/ubsan.c: s/mi... |
295 |
handle_misaligned_access(data, ptr); |
c6d308534 UBSAN: run-time u... |
296 297 |
else handle_object_size_mismatch(data, ptr); |
d08965a27 x86/uaccess, ubsa... |
298 299 |
user_access_restore(flags); |
c6d308534 UBSAN: run-time u... |
300 |
} |
42440c1f9 lib/ubsan: add ty... |
301 302 |
void __ubsan_handle_type_mismatch(struct type_mismatch_data *data, |
f0996bc29 ubsan: Fix nasty ... |
303 |
void *ptr) |
42440c1f9 lib/ubsan: add ty... |
304 305 306 307 308 309 310 |
{ 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 ... |
311 |
ubsan_type_mismatch_common(&common_data, (unsigned long)ptr); |
42440c1f9 lib/ubsan: add ty... |
312 |
} |
c6d308534 UBSAN: run-time u... |
313 |
EXPORT_SYMBOL(__ubsan_handle_type_mismatch); |
42440c1f9 lib/ubsan: add ty... |
314 |
void __ubsan_handle_type_mismatch_v1(struct type_mismatch_data_v1 *data, |
f0996bc29 ubsan: Fix nasty ... |
315 |
void *ptr) |
42440c1f9 lib/ubsan: add ty... |
316 317 318 319 320 321 322 323 |
{ 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 ... |
324 |
ubsan_type_mismatch_common(&common_data, (unsigned long)ptr); |
42440c1f9 lib/ubsan: add ty... |
325 326 |
} EXPORT_SYMBOL(__ubsan_handle_type_mismatch_v1); |
f0996bc29 ubsan: Fix nasty ... |
327 |
void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, void *index) |
c6d308534 UBSAN: run-time u... |
328 |
{ |
c6d308534 UBSAN: run-time u... |
329 330 331 332 |
char index_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ce5c31db3 lib/ubsan: don't ... |
333 |
ubsan_prologue(&data->location); |
c6d308534 UBSAN: run-time u... |
334 335 336 337 338 |
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 ... |
339 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
340 341 342 343 |
} EXPORT_SYMBOL(__ubsan_handle_out_of_bounds); void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data, |
f0996bc29 ubsan: Fix nasty ... |
344 |
void *lhs, void *rhs) |
c6d308534 UBSAN: run-time u... |
345 |
{ |
c6d308534 UBSAN: run-time u... |
346 347 348 349 |
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... |
350 |
unsigned long ua_flags = user_access_save(); |
c6d308534 UBSAN: run-time u... |
351 352 |
if (suppress_report(&data->location)) |
9a50dcaf0 ubsan, x86: Annot... |
353 |
goto out; |
c6d308534 UBSAN: run-time u... |
354 |
|
ce5c31db3 lib/ubsan: don't ... |
355 |
ubsan_prologue(&data->location); |
c6d308534 UBSAN: run-time u... |
356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
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 ... |
381 |
ubsan_epilogue(); |
9a50dcaf0 ubsan, x86: Annot... |
382 383 |
out: user_access_restore(ua_flags); |
c6d308534 UBSAN: run-time u... |
384 385 |
} EXPORT_SYMBOL(__ubsan_handle_shift_out_of_bounds); |
1c23b4108 lib/ubsan.c: don'... |
386 |
void __ubsan_handle_builtin_unreachable(struct unreachable_data *data) |
c6d308534 UBSAN: run-time u... |
387 |
{ |
ce5c31db3 lib/ubsan: don't ... |
388 |
ubsan_prologue(&data->location); |
c6d308534 UBSAN: run-time u... |
389 390 |
pr_err("calling __builtin_unreachable() "); |
ce5c31db3 lib/ubsan: don't ... |
391 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
392 393 394 395 396 |
panic("can't return from __builtin_unreachable()"); } EXPORT_SYMBOL(__ubsan_handle_builtin_unreachable); void __ubsan_handle_load_invalid_value(struct invalid_value_data *data, |
f0996bc29 ubsan: Fix nasty ... |
397 |
void *val) |
c6d308534 UBSAN: run-time u... |
398 |
{ |
c6d308534 UBSAN: run-time u... |
399 400 401 402 |
char val_str[VALUE_LENGTH]; if (suppress_report(&data->location)) return; |
ce5c31db3 lib/ubsan: don't ... |
403 |
ubsan_prologue(&data->location); |
c6d308534 UBSAN: run-time u... |
404 405 406 407 408 409 |
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 ... |
410 |
ubsan_epilogue(); |
c6d308534 UBSAN: run-time u... |
411 412 |
} EXPORT_SYMBOL(__ubsan_handle_load_invalid_value); |