// SPDX-License-Identifier: GPL-2.0
/*
* KMSAN compiler API .
*
* This file implements _ _ msan_XXX hooks that Clang inserts into the code
* compiled with - fsanitize = kernel - memory .
* See Documentation / dev - tools / kmsan . rst for more information on how KMSAN
* instrumentation works .
*
* Copyright ( C ) 2017 - 2022 Google LLC
* Author : Alexander Potapenko < glider @ google . com >
*
*/
#include "kmsan.h"
#include <linux/gfp.h>
#include <linux/kmsan.h>
#include <linux/kmsan_string.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
static inline bool is_bad_asm_addr(void *addr, uintptr_t size, bool is_store)
{
if (IS_ENABLED(CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE) &&
(u64)addr < TASK_SIZE)
return true ;
if (!kmsan_get_metadata(addr, KMSAN_META_SHADOW))
return true ;
return false ;
}
static inline struct shadow_origin_ptr
get_shadow_origin_ptr(void *addr, u64 size, bool store)
{
unsigned long ua_flags = user_access_save();
struct shadow_origin_ptr ret;
ret = kmsan_get_shadow_origin_ptr(addr, size, store);
user_access_restore(ua_flags);
return ret;
}
/*
* KMSAN instrumentation functions follow . They are not declared elsewhere in
* the kernel code , so they are preceded by prototypes , to silence
* - Wmissing - prototypes warnings .
*/
/* Get shadow and origin pointers for a memory load with non-standard size. */
struct shadow_origin_ptr __msan_metadata_ptr_for_load_n(void *addr,
uintptr_t size);
struct shadow_origin_ptr __msan_metadata_ptr_for_load_n(void *addr,
uintptr_t size)
{
return get_shadow_origin_ptr(addr, size, /*store*/ false);
}
EXPORT_SYMBOL(__msan_metadata_ptr_for_load_n);
/* Get shadow and origin pointers for a memory store with non-standard size. */
struct shadow_origin_ptr __msan_metadata_ptr_for_store_n(void *addr,
uintptr_t size);
struct shadow_origin_ptr __msan_metadata_ptr_for_store_n(void *addr,
uintptr_t size)
{
return get_shadow_origin_ptr(addr, size, /*store*/ true);
}
EXPORT_SYMBOL(__msan_metadata_ptr_for_store_n);
/*
* Declare functions that obtain shadow / origin pointers for loads and stores
* with fixed size .
*/
#define DECLARE_METADATA_PTR_GETTER(size) \
struct shadow_origin_ptr __msan_metadata_ptr_for_load_## size( \
void *addr); \
struct shadow_origin_ptr __msan_metadata_ptr_for_load_## size( \
void *addr) \
{ \
return get_shadow_origin_ptr(addr, size, /*store*/ false); \
} \
EXPORT_SYMBOL(__msan_metadata_ptr_for_load_## size); \
struct shadow_origin_ptr __msan_metadata_ptr_for_store_## size( \
void *addr); \
struct shadow_origin_ptr __msan_metadata_ptr_for_store_## size( \
void *addr) \
{ \
return get_shadow_origin_ptr(addr, size, /*store*/ true); \
} \
EXPORT_SYMBOL(__msan_metadata_ptr_for_store_## size)
DECLARE_METADATA_PTR_GETTER(1 );
DECLARE_METADATA_PTR_GETTER(2 );
DECLARE_METADATA_PTR_GETTER(4 );
DECLARE_METADATA_PTR_GETTER(8 );
/*
* Handle a memory store performed by inline assembly . KMSAN conservatively
* attempts to unpoison the outputs of asm ( ) directives to prevent false
* positives caused by missed stores .
*
* _ _ msan_instrument_asm_store ( ) may be called for inline assembly code when
* entering or leaving IRQ . We omit the check for kmsan_in_runtime ( ) to ensure
* the memory written to in these cases is also marked as initialized .
*/
void __msan_instrument_asm_store(void *addr, uintptr_t size);
void __msan_instrument_asm_store(void *addr, uintptr_t size)
{
unsigned long ua_flags;
if (!kmsan_enabled)
return ;
ua_flags = user_access_save();
/*
* Most of the accesses are below 32 bytes . The exceptions so far are
* clwb ( ) ( 64 bytes ) , FPU state ( 512 bytes ) and chsc ( ) ( 4096 bytes ) .
*/
if (size > 4096 ) {
WARN_ONCE(1 , "assembly store size too big: %ld\n" , size);
size = 8 ;
}
if (is_bad_asm_addr(addr, size, /*is_store*/ true)) {
user_access_restore(ua_flags);
return ;
}
/* Unpoisoning the memory on best effort. */
kmsan_internal_unpoison_memory(addr, size, /*checked*/ false);
user_access_restore(ua_flags);
}
EXPORT_SYMBOL(__msan_instrument_asm_store);
/*
* KMSAN instrumentation pass replaces LLVM memcpy , memmove and memset
* intrinsics with calls to respective _ _ msan_ functions . We use
* get_param0_metadata ( ) and set_retval_metadata ( ) to store the shadow / origin
* values for the destination argument of these functions and use them for the
* functions ' return values .
*/
static inline void get_param0_metadata(u64 *shadow,
depot_stack_handle_t *origin)
{
struct kmsan_ctx *ctx = kmsan_get_context();
*shadow = *(u64 *)(ctx->cstate.param_tls);
*origin = ctx->cstate.param_origin_tls[0 ];
}
static inline void set_retval_metadata(u64 shadow, depot_stack_handle_t origin)
{
struct kmsan_ctx *ctx = kmsan_get_context();
*(u64 *)(ctx->cstate.retval_tls) = shadow;
ctx->cstate.retval_origin_tls = origin;
}
/* Handle llvm.memmove intrinsic. */
void *__msan_memmove(void *dst, const void *src, uintptr_t n);
void *__msan_memmove(void *dst, const void *src, uintptr_t n)
{
depot_stack_handle_t origin;
void *result;
u64 shadow;
get_param0_metadata(&shadow, &origin);
result = __memmove(dst, src, n);
if (!n)
/* Some people call memmove() with zero length. */
return result;
if (!kmsan_enabled || kmsan_in_runtime())
return result;
kmsan_enter_runtime();
kmsan_internal_memmove_metadata(dst, (void *)src, n);
kmsan_leave_runtime();
set_retval_metadata(shadow, origin);
return result;
}
EXPORT_SYMBOL(__msan_memmove);
/* Handle llvm.memcpy intrinsic. */
void *__msan_memcpy(void *dst, const void *src, uintptr_t n);
void *__msan_memcpy(void *dst, const void *src, uintptr_t n)
{
depot_stack_handle_t origin;
void *result;
u64 shadow;
get_param0_metadata(&shadow, &origin);
result = __memcpy(dst, src, n);
if (!n)
/* Some people call memcpy() with zero length. */
return result;
if (!kmsan_enabled || kmsan_in_runtime())
return result;
kmsan_enter_runtime();
/* Using memmove instead of memcpy doesn't affect correctness. */
kmsan_internal_memmove_metadata(dst, (void *)src, n);
kmsan_leave_runtime();
set_retval_metadata(shadow, origin);
return result;
}
EXPORT_SYMBOL(__msan_memcpy);
/* Handle llvm.memset intrinsic. */
void *__msan_memset(void *dst, int c, uintptr_t n);
void *__msan_memset(void *dst, int c, uintptr_t n)
{
depot_stack_handle_t origin;
void *result;
u64 shadow;
get_param0_metadata(&shadow, &origin);
result = __memset(dst, c, n);
if (!kmsan_enabled || kmsan_in_runtime())
return result;
kmsan_enter_runtime();
/*
* Clang doesn ' t pass parameter metadata here , so it is impossible to
* use shadow of @ c to set up the shadow for @ dst .
*/
kmsan_internal_unpoison_memory(dst, n, /*checked*/ false);
kmsan_leave_runtime();
set_retval_metadata(shadow, origin);
return result;
}
EXPORT_SYMBOL(__msan_memset);
/*
* Create a new origin from an old one . This is done when storing an
* uninitialized value to memory . When reporting an error , KMSAN unrolls and
* prints the whole chain of stores that preceded the use of this value .
*/
depot_stack_handle_t __msan_chain_origin(depot_stack_handle_t origin);
depot_stack_handle_t __msan_chain_origin(depot_stack_handle_t origin)
{
depot_stack_handle_t ret = 0 ;
unsigned long ua_flags;
if (!kmsan_enabled || kmsan_in_runtime())
return ret;
ua_flags = user_access_save();
/* Creating new origins may allocate memory. */
kmsan_enter_runtime();
ret = kmsan_internal_chain_origin(origin);
kmsan_leave_runtime();
user_access_restore(ua_flags);
return ret;
}
EXPORT_SYMBOL(__msan_chain_origin);
/* Poison a local variable when entering a function. */
void __msan_poison_alloca(void *address, uintptr_t size, char *descr);
void __msan_poison_alloca(void *address, uintptr_t size, char *descr)
{
depot_stack_handle_t handle;
unsigned long entries[4 ];
unsigned long ua_flags;
if (!kmsan_enabled || kmsan_in_runtime())
return ;
ua_flags = user_access_save();
entries[0 ] = KMSAN_ALLOCA_MAGIC_ORIGIN;
entries[1 ] = (u64)descr;
entries[2 ] = (u64)__builtin_return_address(0 );
/*
* With frame pointers enabled , it is possible to quickly fetch the
* second frame of the caller stack without calling the unwinder .
* Without them , simply do not bother .
*/
if (IS_ENABLED(CONFIG_UNWINDER_FRAME_POINTER))
entries[3 ] = (u64)__builtin_return_address(1 );
else
entries[3 ] = 0 ;
/* stack_depot_save() may allocate memory. */
kmsan_enter_runtime();
handle = stack_depot_save(entries, ARRAY_SIZE(entries), __GFP_HIGH);
kmsan_leave_runtime();
kmsan_internal_set_shadow_origin(address, size, -1 , handle,
/*checked*/ true);
user_access_restore(ua_flags);
}
EXPORT_SYMBOL(__msan_poison_alloca);
/* Unpoison a local variable. */
void __msan_unpoison_alloca(void *address, uintptr_t size);
void __msan_unpoison_alloca(void *address, uintptr_t size)
{
if (!kmsan_enabled || kmsan_in_runtime())
return ;
kmsan_enter_runtime();
kmsan_internal_unpoison_memory(address, size, /*checked*/ true);
kmsan_leave_runtime();
}
EXPORT_SYMBOL(__msan_unpoison_alloca);
/*
* Report that an uninitialized value with the given origin was used in a way
* that constituted undefined behavior .
*/
void __msan_warning(u32 origin);
void __msan_warning(u32 origin)
{
kmsan_report(origin, /*address*/ NULL, /*size*/ 0,
/*off_first*/ 0, /*off_last*/ 0, /*user_addr*/ NULL,
REASON_ANY);
}
EXPORT_SYMBOL(__msan_warning);
/*
* At the beginning of an instrumented function , obtain the pointer to
* ` struct kmsan_context_state ` holding the metadata for function parameters .
*/
struct kmsan_context_state *__msan_get_context_state(void );
struct kmsan_context_state *__msan_get_context_state(void )
{
return &kmsan_get_context()->cstate;
}
EXPORT_SYMBOL(__msan_get_context_state);
Messung V0.5 in Prozent C=90 H=100 G=95
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland