root/mm/kmsan/report.c
// 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();
}