// 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();
}
Messung V0.5 in Prozent C=98 H=100 G=98
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland