|  | // 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) | 
|  | return; | 
|  | if (current->kmsan_ctx.depth) | 
|  | return; | 
|  | if (!origin) | 
|  | return; | 
|  |  | 
|  | kmsan_disable_current(); | 
|  | 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_enable_current(); | 
|  | } |