// SPDX-License-Identifier: GPL-2.0
/*
* This file contains generic KASAN specific error reporting code .
*
* Copyright ( c ) 2014 Samsung Electronics Co . , Ltd .
* Author : Andrey Ryabinin < ryabinin . a . a @ gmail . com >
*
* Some code borrowed from https : //github.com/xairy/kasan-prototype by
* Andrey Konovalov < andreyknvl @ gmail . com >
*/
#include <linux/bitops.h>
#include <linux/ftrace.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/printk.h>
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/slab.h>
#include <linux/stackdepot.h>
#include <linux/stacktrace.h>
#include <linux/string.h>
#include <linux/types.h>
#include <linux/kasan.h>
#include <linux/module.h>
#include <asm /sections.h>
#include "kasan.h"
#include "../slab.h"
const void *kasan_find_first_bad_addr(const void *addr, size_t size)
{
const void *p = addr;
if (!addr_has_metadata(p))
return p;
while (p < addr + size && !(*(u8 *)kasan_mem_to_shadow(p)))
p += KASAN_GRANULE_SIZE;
return p;
}
size_t kasan_get_alloc_size(void *object, struct kmem_cache *cache)
{
size_t size = 0 ;
u8 *shadow;
/*
* Skip the addr_has_metadata check , as this function only operates on
* slab memory , which must have metadata .
*/
/*
* The loop below returns 0 for freed objects , for which KASAN cannot
* calculate the allocation size based on the metadata .
*/
shadow = (u8 *)kasan_mem_to_shadow(object);
while (size < cache->object_size) {
if (*shadow == 0 )
size += KASAN_GRANULE_SIZE;
else if (*shadow >= 1 && *shadow <= KASAN_GRANULE_SIZE - 1 )
return size + *shadow;
else
return size;
shadow++;
}
return cache->object_size;
}
static const char *get_shadow_bug_type(struct kasan_report_info *info)
{
const char *bug_type = "unknown-crash" ;
u8 *shadow_addr;
shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr);
/*
* If shadow byte value is in [ 0 , KASAN_GRANULE_SIZE ) we can look
* at the next shadow byte to determine the type of the bad access .
*/
if (*shadow_addr > 0 && *shadow_addr <= KASAN_GRANULE_SIZE - 1 )
shadow_addr++;
switch (*shadow_addr) {
case 0 ... KASAN_GRANULE_SIZE - 1 :
/*
* In theory it ' s still possible to see these shadow values
* due to a data race in the kernel code .
*/
bug_type = "out-of-bounds" ;
break ;
case KASAN_PAGE_REDZONE:
case KASAN_SLAB_REDZONE:
bug_type = "slab-out-of-bounds" ;
break ;
case KASAN_GLOBAL_REDZONE:
bug_type = "global-out-of-bounds" ;
break ;
case KASAN_STACK_LEFT:
case KASAN_STACK_MID:
case KASAN_STACK_RIGHT:
case KASAN_STACK_PARTIAL:
bug_type = "stack-out-of-bounds" ;
break ;
case KASAN_PAGE_FREE:
bug_type = "use-after-free" ;
break ;
case KASAN_SLAB_FREE:
case KASAN_SLAB_FREE_META:
bug_type = "slab-use-after-free" ;
break ;
case KASAN_ALLOCA_LEFT:
case KASAN_ALLOCA_RIGHT:
bug_type = "alloca-out-of-bounds" ;
break ;
case KASAN_VMALLOC_INVALID:
bug_type = "vmalloc-out-of-bounds" ;
break ;
}
return bug_type;
}
static const char *get_wild_bug_type(struct kasan_report_info *info)
{
const char *bug_type = "unknown-crash" ;
if ((unsigned long )info->access_addr < PAGE_SIZE)
bug_type = "null-ptr-deref" ;
else if ((unsigned long )info->access_addr < TASK_SIZE)
bug_type = "user-memory-access" ;
else
bug_type = "wild-memory-access" ;
return bug_type;
}
static const char *get_bug_type(struct kasan_report_info *info)
{
/*
* If access_size is a negative number , then it has reason to be
* defined as out - of - bounds bug type .
*
* Casting negative numbers to size_t would indeed turn up as
* a large size_t and its value will be larger than ULONG_MAX / 2 ,
* so that this can qualify as out - of - bounds .
*/
if (info->access_addr + info->access_size < info->access_addr)
return "out-of-bounds" ;
if (addr_has_metadata(info->access_addr))
return get_shadow_bug_type(info);
return get_wild_bug_type(info);
}
void kasan_complete_mode_report_info(struct kasan_report_info *info)
{
struct kasan_alloc_meta *alloc_meta;
struct kasan_free_meta *free_meta;
if (!info->bug_type)
info->bug_type = get_bug_type(info);
if (!info->cache || !info->object)
return ;
alloc_meta = kasan_get_alloc_meta(info->cache, info->object);
if (alloc_meta)
memcpy(&info->alloc_track, &alloc_meta->alloc_track,
sizeof (info->alloc_track));
if (*(u8 *)kasan_mem_to_shadow(info->object) == KASAN_SLAB_FREE_META) {
/* Free meta must be present with KASAN_SLAB_FREE_META. */
free_meta = kasan_get_free_meta(info->cache, info->object);
memcpy(&info->free_track, &free_meta->free_track,
sizeof (info->free_track));
}
}
void kasan_metadata_fetch_row(char *buffer, void *row)
{
memcpy(buffer, kasan_mem_to_shadow(row), META_BYTES_PER_ROW);
}
void kasan_print_aux_stacks(struct kmem_cache *cache, const void *object)
{
struct kasan_alloc_meta *alloc_meta;
alloc_meta = kasan_get_alloc_meta(cache, object);
if (!alloc_meta)
return ;
if (alloc_meta->aux_stack[0 ]) {
pr_err("Last potentially related work creation:\n" );
stack_depot_print(alloc_meta->aux_stack[0 ]);
pr_err("\n" );
}
if (alloc_meta->aux_stack[1 ]) {
pr_err("Second to last potentially related work creation:\n" );
stack_depot_print(alloc_meta->aux_stack[1 ]);
pr_err("\n" );
}
}
#ifdef CONFIG_KASAN_STACK
static bool __must_check tokenize_frame_descr(const char **frame_descr,
char *token, size_t max_tok_len,
unsigned long *value)
{
const char *sep = strchr(*frame_descr, ' ' );
if (sep == NULL)
sep = *frame_descr + strlen(*frame_descr);
if (token != NULL) {
const size_t tok_len = sep - *frame_descr;
if (tok_len + 1 > max_tok_len) {
pr_err("internal error: frame description too long: %s\n" ,
*frame_descr);
return false ;
}
/* Copy token (+ 1 byte for '\0'). */
strscpy(token, *frame_descr, tok_len + 1 );
}
/* Advance frame_descr past separator. */
*frame_descr = sep + 1 ;
if (value != NULL && kstrtoul(token, 10 , value)) {
pr_err("internal error: not a valid number: %s\n" , token);
return false ;
}
return true ;
}
static void print_decoded_frame_descr(const char *frame_descr)
{
/*
* We need to parse the following string :
* " n alloc_1 alloc_2 . . . alloc_n "
* where alloc_i looks like
* " offset size len name "
* or " offset size len name : line " .
*/
char token[64 ];
unsigned long num_objects;
if (!tokenize_frame_descr(&frame_descr, token, sizeof (token),
&num_objects))
return ;
pr_err("\n" );
pr_err("This frame has %lu %s:\n" , num_objects,
num_objects == 1 ? "object" : "objects" );
while (num_objects--) {
unsigned long offset;
unsigned long size;
/* access offset */
if (!tokenize_frame_descr(&frame_descr, token, sizeof (token),
&offset))
return ;
/* access size */
if (!tokenize_frame_descr(&frame_descr, token, sizeof (token),
&size))
return ;
/* name length (unused) */
if (!tokenize_frame_descr(&frame_descr, NULL, 0 , NULL))
return ;
/* object name */
if (!tokenize_frame_descr(&frame_descr, token, sizeof (token),
NULL))
return ;
/* Strip line number; without filename it's not very helpful. */
strreplace(token, ':' , '\0' );
/* Finally, print object information. */
pr_err(" [%lu, %lu) '%s'" , offset, offset + size, token);
}
}
/* Returns true only if the address is on the current task's stack. */
static bool __must_check get_address_stack_frame_info(const void *addr,
unsigned long *offset,
const char **frame_descr,
const void **frame_pc)
{
unsigned long aligned_addr;
unsigned long mem_ptr;
const u8 *shadow_bottom;
const u8 *shadow_ptr;
const unsigned long *frame;
BUILD_BUG_ON(IS_ENABLED(CONFIG_STACK_GROWSUP));
aligned_addr = round_down((unsigned long )addr, sizeof (long ));
mem_ptr = round_down(aligned_addr, KASAN_GRANULE_SIZE);
shadow_ptr = kasan_mem_to_shadow((void *)aligned_addr);
shadow_bottom = kasan_mem_to_shadow(end_of_stack(current));
while (shadow_ptr >= shadow_bottom && *shadow_ptr != KASAN_STACK_LEFT) {
shadow_ptr--;
mem_ptr -= KASAN_GRANULE_SIZE;
}
while (shadow_ptr >= shadow_bottom && *shadow_ptr == KASAN_STACK_LEFT) {
shadow_ptr--;
mem_ptr -= KASAN_GRANULE_SIZE;
}
if (shadow_ptr < shadow_bottom)
return false ;
frame = (const unsigned long *)(mem_ptr + KASAN_GRANULE_SIZE);
if (frame[0 ] != KASAN_CURRENT_STACK_FRAME_MAGIC) {
pr_err("internal error: frame has invalid marker: %lu\n" ,
frame[0 ]);
return false ;
}
*offset = (unsigned long )addr - (unsigned long )frame;
*frame_descr = (const char *)frame[1 ];
*frame_pc = (void *)frame[2 ];
return true ;
}
void kasan_print_address_stack_frame(const void *addr)
{
unsigned long offset;
const char *frame_descr;
const void *frame_pc;
if (WARN_ON(!object_is_on_stack(addr)))
return ;
pr_err("The buggy address belongs to stack of task %s/%d\n" ,
current->comm, task_pid_nr(current));
if (!get_address_stack_frame_info(addr, &offset, &frame_descr,
&frame_pc))
return ;
pr_err(" and is located at offset %lu in frame:\n" , offset);
pr_err(" %pS\n" , frame_pc);
if (!frame_descr)
return ;
print_decoded_frame_descr(frame_descr);
}
#endif /* CONFIG_KASAN_STACK */
#define DEFINE_ASAN_REPORT_LOAD(size) \
void __asan_report_load## size## _noabort(void *addr) \
{ \
kasan_report(addr, size, false , _RET_IP_); \
} \
EXPORT_SYMBOL(__asan_report_load## size## _noabort)
#define DEFINE_ASAN_REPORT_STORE(size) \
void __asan_report_store## size## _noabort(void *addr) \
{ \
kasan_report(addr, size, true , _RET_IP_); \
} \
EXPORT_SYMBOL(__asan_report_store## size## _noabort)
DEFINE_ASAN_REPORT_LOAD(1 );
DEFINE_ASAN_REPORT_LOAD(2 );
DEFINE_ASAN_REPORT_LOAD(4 );
DEFINE_ASAN_REPORT_LOAD(8 );
DEFINE_ASAN_REPORT_LOAD(16 );
DEFINE_ASAN_REPORT_STORE(1 );
DEFINE_ASAN_REPORT_STORE(2 );
DEFINE_ASAN_REPORT_STORE(4 );
DEFINE_ASAN_REPORT_STORE(8 );
DEFINE_ASAN_REPORT_STORE(16 );
void __asan_report_load_n_noabort(void *addr, ssize_t size)
{
kasan_report(addr, size, false , _RET_IP_);
}
EXPORT_SYMBOL(__asan_report_load_n_noabort);
void __asan_report_store_n_noabort(void *addr, ssize_t size)
{
kasan_report(addr, size, true , _RET_IP_);
}
EXPORT_SYMBOL(__asan_report_store_n_noabort);
Messung V0.5 in Prozent C=96 H=95 G=95
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland