| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * KMSAN error reporting routines.
- *
- * Copyright (C) 2019-2022 Google LLC
- * Author: Alexander Potapenko <glider@google.com>
- *
- */
- #include <linux/console.h>
- #include <linux/kmsan.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;
- EXPORT_SYMBOL_GPL(panic_on_kmsan);
- #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 *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 || kmsan_in_runtime())
- return;
- if (current->kmsan_ctx.depth)
- return;
- if (!origin)
- return;
- kmsan_enter_runtime();
- 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);
- kmsan_leave_runtime();
- }
|