mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
5.8 KiB
219 lines
5.8 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* KMSAN error reporting routines. |
|
* |
|
* Copyright (C) 2019-2022 Google LLC |
|
* Author: Alexander Potapenko <[email protected]> |
|
* |
|
*/ |
|
|
|
#include <linux/console.h> |
|
#include <linux/moduleparam.h> |
|
#include <linux/stackdepot.h> |
|
#include <linux/stacktrace.h> |
|
#include <linux/uaccess.h> |
|
|
|
#include "kmsan.h" |
|
|
|
static DEFINE_RAW_SPINLOCK(kmsan_report_lock); |
|
#define DESCR_SIZE 128 |
|
/* Protected by kmsan_report_lock */ |
|
static char report_local_descr[DESCR_SIZE]; |
|
int panic_on_kmsan __read_mostly; |
|
|
|
#ifdef MODULE_PARAM_PREFIX |
|
#undef MODULE_PARAM_PREFIX |
|
#endif |
|
#define MODULE_PARAM_PREFIX "kmsan." |
|
module_param_named(panic, panic_on_kmsan, int, 0); |
|
|
|
/* |
|
* Skip internal KMSAN frames. |
|
*/ |
|
static int get_stack_skipnr(const unsigned long stack_entries[], |
|
int num_entries) |
|
{ |
|
int len, skip; |
|
char buf[64]; |
|
|
|
for (skip = 0; skip < num_entries; ++skip) { |
|
len = scnprintf(buf, sizeof(buf), "%ps", |
|
(void *)stack_entries[skip]); |
|
|
|
/* Never show __msan_* or kmsan_* functions. */ |
|
if ((strnstr(buf, "__msan_", len) == buf) || |
|
(strnstr(buf, "kmsan_", len) == buf)) |
|
continue; |
|
|
|
/* |
|
* No match for runtime functions -- @skip entries to skip to |
|
* get to first frame of interest. |
|
*/ |
|
break; |
|
} |
|
|
|
return skip; |
|
} |
|
|
|
/* |
|
* Currently the descriptions of locals generated by Clang look as follows: |
|
* ----local_name@function_name |
|
* We want to print only the name of the local, as other information in that |
|
* description can be confusing. |
|
* The meaningful part of the description is copied to a global buffer to avoid |
|
* allocating memory. |
|
*/ |
|
static char *pretty_descr(char *descr) |
|
{ |
|
int pos = 0, len = strlen(descr); |
|
|
|
for (int i = 0; i < len; i++) { |
|
if (descr[i] == '@') |
|
break; |
|
if (descr[i] == '-') |
|
continue; |
|
report_local_descr[pos] = descr[i]; |
|
if (pos + 1 == DESCR_SIZE) |
|
break; |
|
pos++; |
|
} |
|
report_local_descr[pos] = 0; |
|
return report_local_descr; |
|
} |
|
|
|
void kmsan_print_origin(depot_stack_handle_t origin) |
|
{ |
|
unsigned long *entries = NULL, *chained_entries = NULL; |
|
unsigned int nr_entries, chained_nr_entries, skipnr; |
|
void *pc1 = NULL, *pc2 = NULL; |
|
depot_stack_handle_t head; |
|
unsigned long magic; |
|
char *descr = NULL; |
|
unsigned int depth; |
|
|
|
if (!origin) |
|
return; |
|
|
|
while (true) { |
|
nr_entries = stack_depot_fetch(origin, &entries); |
|
depth = kmsan_depth_from_eb(stack_depot_get_extra_bits(origin)); |
|
magic = nr_entries ? entries[0] : 0; |
|
if ((nr_entries == 4) && (magic == KMSAN_ALLOCA_MAGIC_ORIGIN)) { |
|
descr = (char *)entries[1]; |
|
pc1 = (void *)entries[2]; |
|
pc2 = (void *)entries[3]; |
|
pr_err("Local variable %s created at:\n", |
|
pretty_descr(descr)); |
|
if (pc1) |
|
pr_err(" %pSb\n", pc1); |
|
if (pc2) |
|
pr_err(" %pSb\n", pc2); |
|
break; |
|
} |
|
if ((nr_entries == 3) && (magic == KMSAN_CHAIN_MAGIC_ORIGIN)) { |
|
/* |
|
* Origin chains deeper than KMSAN_MAX_ORIGIN_DEPTH are |
|
* not stored, so the output may be incomplete. |
|
*/ |
|
if (depth == KMSAN_MAX_ORIGIN_DEPTH) |
|
pr_err("<Zero or more stacks not recorded to save memory>\n\n"); |
|
head = entries[1]; |
|
origin = entries[2]; |
|
pr_err("Uninit was stored to memory at:\n"); |
|
chained_nr_entries = |
|
stack_depot_fetch(head, &chained_entries); |
|
kmsan_internal_unpoison_memory( |
|
chained_entries, |
|
chained_nr_entries * sizeof(*chained_entries), |
|
/*checked*/ false); |
|
skipnr = get_stack_skipnr(chained_entries, |
|
chained_nr_entries); |
|
stack_trace_print(chained_entries + skipnr, |
|
chained_nr_entries - skipnr, 0); |
|
pr_err("\n"); |
|
continue; |
|
} |
|
pr_err("Uninit was created at:\n"); |
|
if (nr_entries) { |
|
skipnr = get_stack_skipnr(entries, nr_entries); |
|
stack_trace_print(entries + skipnr, nr_entries - skipnr, |
|
0); |
|
} else { |
|
pr_err("(stack is not available)\n"); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
void kmsan_report(depot_stack_handle_t origin, void *address, int size, |
|
int off_first, int off_last, const void *user_addr, |
|
enum kmsan_bug_reason reason) |
|
{ |
|
unsigned long stack_entries[KMSAN_STACK_DEPTH]; |
|
int num_stack_entries, skipnr; |
|
char *bug_type = NULL; |
|
unsigned long ua_flags; |
|
bool is_uaf; |
|
|
|
if (!kmsan_enabled) |
|
return; |
|
if (!current->kmsan_ctx.allow_reporting) |
|
return; |
|
if (!origin) |
|
return; |
|
|
|
current->kmsan_ctx.allow_reporting = false; |
|
ua_flags = user_access_save(); |
|
raw_spin_lock(&kmsan_report_lock); |
|
pr_err("=====================================================\n"); |
|
is_uaf = kmsan_uaf_from_eb(stack_depot_get_extra_bits(origin)); |
|
switch (reason) { |
|
case REASON_ANY: |
|
bug_type = is_uaf ? "use-after-free" : "uninit-value"; |
|
break; |
|
case REASON_COPY_TO_USER: |
|
bug_type = is_uaf ? "kernel-infoleak-after-free" : |
|
"kernel-infoleak"; |
|
break; |
|
case REASON_SUBMIT_URB: |
|
bug_type = is_uaf ? "kernel-usb-infoleak-after-free" : |
|
"kernel-usb-infoleak"; |
|
break; |
|
} |
|
|
|
num_stack_entries = |
|
stack_trace_save(stack_entries, KMSAN_STACK_DEPTH, 1); |
|
skipnr = get_stack_skipnr(stack_entries, num_stack_entries); |
|
|
|
pr_err("BUG: KMSAN: %s in %pSb\n", bug_type, |
|
(void *)stack_entries[skipnr]); |
|
stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr, |
|
0); |
|
pr_err("\n"); |
|
|
|
kmsan_print_origin(origin); |
|
|
|
if (size) { |
|
pr_err("\n"); |
|
if (off_first == off_last) |
|
pr_err("Byte %d of %d is uninitialized\n", off_first, |
|
size); |
|
else |
|
pr_err("Bytes %d-%d of %d are uninitialized\n", |
|
off_first, off_last, size); |
|
} |
|
if (address) |
|
pr_err("Memory access of size %d starts at %px\n", size, |
|
address); |
|
if (user_addr && reason == REASON_COPY_TO_USER) |
|
pr_err("Data copied to user address %px\n", user_addr); |
|
pr_err("\n"); |
|
dump_stack_print_info(KERN_ERR); |
|
pr_err("=====================================================\n"); |
|
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE); |
|
raw_spin_unlock(&kmsan_report_lock); |
|
if (panic_on_kmsan) |
|
panic("kmsan.panic set ...\n"); |
|
user_access_restore(ua_flags); |
|
current->kmsan_ctx.allow_reporting = true; |
|
}
|
|
|