// 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/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; }