// SPDX-License-Identifier: GPL-2.0
/*
* KCSAN core runtime .
*
* Copyright ( C ) 2019 , Google LLC .
*/
#define pr_fmt(fmt) "kcsan: " fmt
#include <linux/atomic.h>
#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/minmax.h>
#include <linux/moduleparam.h>
#include <linux/percpu.h>
#include <linux/preempt.h>
#include <linux/sched.h>
#include <linux/string.h>
#include <linux/uaccess.h>
#include "encoding.h"
#include "kcsan.h"
#include "permissive.h"
static bool kcsan_early_enable = IS_ENABLED(CONFIG_KCSAN_EARLY_ENABLE);
unsigned int kcsan_udelay_task = CONFIG_KCSAN_UDELAY_TASK;
unsigned int kcsan_udelay_interrupt = CONFIG_KCSAN_UDELAY_INTERRUPT;
static long kcsan_skip_watch = CONFIG_KCSAN_SKIP_WATCH;
static bool kcsan_interrupt_watcher = IS_ENABLED(CONFIG_KCSAN_INTERRUPT_WATCHER);
#ifdef MODULE_PARAM_PREFIX
#undef MODULE_PARAM_PREFIX
#endif
#define MODULE_PARAM_PREFIX "kcsan."
module_param_named(early_enable, kcsan_early_enable, bool , 0 );
module_param_named(udelay_task, kcsan_udelay_task, uint, 0644 );
module_param_named(udelay_interrupt, kcsan_udelay_interrupt, uint, 0644 );
module_param_named(skip_watch, kcsan_skip_watch, long , 0644 );
module_param_named(interrupt_watcher, kcsan_interrupt_watcher, bool , 0444 );
#ifdef CONFIG_KCSAN_WEAK_MEMORY
static bool kcsan_weak_memory = true ;
module_param_named(weak_memory, kcsan_weak_memory, bool , 0644 );
#else
#define kcsan_weak_memory false
#endif
bool kcsan_enabled;
/* Per-CPU kcsan_ctx for interrupts */
static DEFINE_PER_CPU(struct kcsan_ctx, kcsan_cpu_ctx) = {
.scoped_accesses = {LIST_POISON1, NULL},
};
/*
* Helper macros to index into adjacent slots , starting from address slot
* itself , followed by the right and left slots .
*
* The purpose is 2 - fold :
*
* 1 . if during insertion the address slot is already occupied , check if
* any adjacent slots are free ;
* 2 . accesses that straddle a slot boundary due to size that exceeds a
* slot ' s range may check adjacent slots if any watchpoint matches .
*
* Note that accesses with very large size may still miss a watchpoint ; however ,
* given this should be rare , this is a reasonable trade - off to make , since this
* will avoid :
*
* 1 . excessive contention between watchpoint checks and setup ;
* 2 . larger number of simultaneous watchpoints without sacrificing
* performance .
*
* Example : SLOT_IDX values for KCSAN_CHECK_ADJACENT = 1 , where i is [ 0 , 1 , 2 ] :
*
* slot = 0 : [ 1 , 2 , 0 ]
* slot = 9 : [ 10 , 11 , 9 ]
* slot = 63 : [ 64 , 65 , 63 ]
*/
#define SLOT_IDX(slot, i) (slot + ((i + KCSAN_CHECK_ADJACENT) % NUM_SLOTS))
/*
* SLOT_IDX_FAST is used in the fast - path . Not first checking the address ' s primary
* slot ( middle ) is fine if we assume that races occur rarely . The set of
* indices { SLOT_IDX ( slot , i ) | i in [ 0 , NUM_SLOTS ) } is equivalent to
* { SLOT_IDX_FAST ( slot , i ) | i in [ 0 , NUM_SLOTS ) } .
*/
#define SLOT_IDX_FAST(slot, i) (slot + i)
/*
* Watchpoints , with each entry encoded as defined in encoding . h : in order to be
* able to safely update and access a watchpoint without introducing locking
* overhead , we encode each watchpoint as a single atomic long . The initial
* zero - initialized state matches INVALID_WATCHPOINT .
*
* Add NUM_SLOTS - 1 entries to account for overflow ; this helps avoid having to
* use more complicated SLOT_IDX_FAST calculation with modulo in the fast - path .
*/
static atomic_long_t watchpoints[CONFIG_KCSAN_NUM_WATCHPOINTS + NUM_SLOTS-1 ];
/*
* Instructions to skip watching counter , used in should_watch ( ) . We use a
* per - CPU counter to avoid excessive contention .
*/
static DEFINE_PER_CPU(long , kcsan_skip);
/* For kcsan_prandom_u32_max(). */
static DEFINE_PER_CPU(u32, kcsan_rand_state);
static __always_inline atomic_long_t *find_watchpoint(unsigned long addr,
size_t size,
bool expect_write,
long *encoded_watchpoint)
{
const int slot = watchpoint_slot(addr);
const unsigned long addr_masked = addr & WATCHPOINT_ADDR_MASK;
atomic_long_t *watchpoint;
unsigned long wp_addr_masked;
size_t wp_size;
bool is_write;
int i;
BUILD_BUG_ON(CONFIG_KCSAN_NUM_WATCHPOINTS < NUM_SLOTS);
for (i = 0 ; i < NUM_SLOTS; ++i) {
watchpoint = &watchpoints[SLOT_IDX_FAST(slot, i)];
*encoded_watchpoint = atomic_long_read(watchpoint);
if (!decode_watchpoint(*encoded_watchpoint, &wp_addr_masked,
&wp_size, &is_write))
continue ;
if (expect_write && !is_write)
continue ;
/* Check if the watchpoint matches the access. */
if (matching_access(wp_addr_masked, wp_size, addr_masked, size))
return watchpoint;
}
return NULL;
}
static inline atomic_long_t *
insert_watchpoint(unsigned long addr, size_t size, bool is_write)
{
const int slot = watchpoint_slot(addr);
const long encoded_watchpoint = encode_watchpoint(addr, size, is_write);
atomic_long_t *watchpoint;
int i;
/* Check slot index logic, ensuring we stay within array bounds. */
BUILD_BUG_ON(SLOT_IDX(0 , 0 ) != KCSAN_CHECK_ADJACENT);
BUILD_BUG_ON(SLOT_IDX(0 , KCSAN_CHECK_ADJACENT+1 ) != 0 );
BUILD_BUG_ON(SLOT_IDX(CONFIG_KCSAN_NUM_WATCHPOINTS-1 , KCSAN_CHECK_ADJACENT) != ARRAY_SIZE(watchpoints)-1 );
BUILD_BUG_ON(SLOT_IDX(CONFIG_KCSAN_NUM_WATCHPOINTS-1 , KCSAN_CHECK_ADJACENT+1 ) != ARRAY_SIZE(watchpoints) - NUM_SLOTS);
for (i = 0 ; i < NUM_SLOTS; ++i) {
long expect_val = INVALID_WATCHPOINT;
/* Try to acquire this slot. */
watchpoint = &watchpoints[SLOT_IDX(slot, i)];
if (atomic_long_try_cmpxchg_relaxed(watchpoint, &expect_val, encoded_watchpoint))
return watchpoint;
}
return NULL;
}
/*
* Return true if watchpoint was successfully consumed , false otherwise .
*
* This may return false if :
*
* 1 . another thread already consumed the watchpoint ;
* 2 . the thread that set up the watchpoint already removed it ;
* 3 . the watchpoint was removed and then re - used .
*/
static __always_inline bool
try_consume_watchpoint(atomic_long_t *watchpoint, long encoded_watchpoint)
{
return atomic_long_try_cmpxchg_relaxed(watchpoint, &encoded_watchpoint, CONSUMED_WATCHPOINT);
}
/* Return true if watchpoint was not touched, false if already consumed. */
static inline bool consume_watchpoint(atomic_long_t *watchpoint)
{
return atomic_long_xchg_relaxed(watchpoint, CONSUMED_WATCHPOINT) != CONSUMED_WATCHPOINT;
}
/* Remove the watchpoint -- its slot may be reused after. */
static inline void remove_watchpoint(atomic_long_t *watchpoint)
{
atomic_long_set(watchpoint, INVALID_WATCHPOINT);
}
static __always_inline struct kcsan_ctx *get_ctx(void )
{
/*
* In interrupts , use raw_cpu_ptr to avoid unnecessary checks , that would
* also result in calls that generate warnings in uaccess regions .
*/
return in_task() ? ¤t->kcsan_ctx : raw_cpu_ptr(&kcsan_cpu_ctx);
}
static __always_inline void
check_access(const volatile void *ptr, size_t size, int type, unsigned long ip);
/* Check scoped accesses; never inline because this is a slow-path! */
static noinline void kcsan_check_scoped_accesses(void )
{
struct kcsan_ctx *ctx = get_ctx();
struct kcsan_scoped_access *scoped_access;
if (ctx->disable_scoped)
return ;
ctx->disable_scoped++;
list_for_each_entry(scoped_access, &ctx->scoped_accesses, list) {
check_access(scoped_access->ptr, scoped_access->size,
scoped_access->type, scoped_access->ip);
}
ctx->disable_scoped--;
}
/* Rules for generic atomic accesses. Called from fast-path. */
static __always_inline bool
is_atomic(struct kcsan_ctx *ctx, const volatile void *ptr, size_t size, int type)
{
if (type & KCSAN_ACCESS_ATOMIC)
return true ;
/*
* Unless explicitly declared atomic , never consider an assertion access
* as atomic . This allows using them also in atomic regions , such as
* seqlocks , without implicitly changing their semantics .
*/
if (type & KCSAN_ACCESS_ASSERT)
return false ;
if (IS_ENABLED(CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC) &&
(type & KCSAN_ACCESS_WRITE) && size <= sizeof (long ) &&
!(type & KCSAN_ACCESS_COMPOUND) && IS_ALIGNED((unsigned long )ptr, size))
return true ; /* Assume aligned writes up to word size are atomic. */
if (ctx->atomic_next > 0 ) {
/*
* Because we do not have separate contexts for nested
* interrupts , in case atomic_next is set , we simply assume that
* the outer interrupt set atomic_next . In the worst case , we
* will conservatively consider operations as atomic . This is a
* reasonable trade - off to make , since this case should be
* extremely rare ; however , even if extremely rare , it could
* lead to false positives otherwise .
*/
if ((hardirq_count() >> HARDIRQ_SHIFT) < 2 )
--ctx->atomic_next; /* in task, or outer interrupt */
return true ;
}
return ctx->atomic_nest_count > 0 || ctx->in_flat_atomic;
}
static __always_inline bool
should_watch(struct kcsan_ctx *ctx, const volatile void *ptr, size_t size, int type)
{
/*
* Never set up watchpoints when memory operations are atomic .
*
* Need to check this first , before kcsan_skip check below : ( 1 ) atomics
* should not count towards skipped instructions , and ( 2 ) to actually
* decrement kcsan_atomic_next for consecutive instruction stream .
*/
if (is_atomic(ctx, ptr, size, type))
return false ;
if (this_cpu_dec_return(kcsan_skip) >= 0 )
return false ;
/*
* NOTE : If we get here , kcsan_skip must always be reset in slow path
* via reset_kcsan_skip ( ) to avoid underflow .
*/
/* this operation should be watched */
return true ;
}
/*
* Returns a pseudo - random number in interval [ 0 , ep_ro ) . Simple linear
* congruential generator , using constants from " Numerical Recipes " .
*/
static u32 kcsan_prandom_u32_max(u32 ep_ro)
{
u32 state = this_cpu_read(kcsan_rand_state);
state = 1664525 * state + 1013904223 ;
this_cpu_write(kcsan_rand_state, state);
return state % ep_ro;
}
static inline void reset_kcsan_skip(void )
{
long skip_count = kcsan_skip_watch -
(IS_ENABLED(CONFIG_KCSAN_SKIP_WATCH_RANDOMIZE) ?
kcsan_prandom_u32_max(kcsan_skip_watch) :
0 );
this_cpu_write(kcsan_skip, skip_count);
}
static __always_inline bool kcsan_is_enabled(struct kcsan_ctx *ctx)
{
return READ_ONCE(kcsan_enabled) && !ctx->disable_count;
}
/* Introduce delay depending on context and configuration. */
static void delay_access(int type)
{
unsigned int delay = in_task() ? kcsan_udelay_task : kcsan_udelay_interrupt;
/* For certain access types, skew the random delay to be longer. */
unsigned int skew_delay_order =
(type & (KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_ASSERT)) ? 1 : 0 ;
delay -= IS_ENABLED(CONFIG_KCSAN_DELAY_RANDOMIZE) ?
kcsan_prandom_u32_max(delay >> skew_delay_order) :
0 ;
udelay(delay);
}
/*
* Reads the instrumented memory for value change detection ; value change
* detection is currently done for accesses up to a size of 8 bytes .
*/
static __always_inline u64 read_instrumented_memory(const volatile void *ptr, size_t size)
{
/*
* In the below we don ' t necessarily need the read of the location to
* be atomic , and we don ' t use READ_ONCE ( ) , since all we need for race
* detection is to observe 2 different values .
*
* Furthermore , on certain architectures ( such as arm64 ) , READ_ONCE ( )
* may turn into more complex instructions than a plain load that cannot
* do unaligned accesses .
*/
switch (size) {
case 1 : return *(const volatile u8 *)ptr;
case 2 : return *(const volatile u16 *)ptr;
case 4 : return *(const volatile u32 *)ptr;
case 8 : return *(const volatile u64 *)ptr;
default : return 0 ; /* Ignore; we do not diff the values. */
}
}
void kcsan_save_irqtrace(struct task_struct *task)
{
#ifdef CONFIG_TRACE_IRQFLAGS
task->kcsan_save_irqtrace = task->irqtrace;
#endif
}
void kcsan_restore_irqtrace(struct task_struct *task)
{
#ifdef CONFIG_TRACE_IRQFLAGS
task->irqtrace = task->kcsan_save_irqtrace;
#endif
}
static __always_inline int get_kcsan_stack_depth(void )
{
#ifdef CONFIG_KCSAN_WEAK_MEMORY
return current->kcsan_stack_depth;
#else
BUILD_BUG();
return 0 ;
#endif
}
static __always_inline void add_kcsan_stack_depth(int val)
{
#ifdef CONFIG_KCSAN_WEAK_MEMORY
current->kcsan_stack_depth += val;
#else
BUILD_BUG();
#endif
}
static __always_inline struct kcsan_scoped_access *get_reorder_access(struct kcsan_ctx *ctx)
{
#ifdef CONFIG_KCSAN_WEAK_MEMORY
return ctx->disable_scoped ? NULL : &ctx->reorder_access;
#else
return NULL;
#endif
}
static __always_inline bool
find_reorder_access(struct kcsan_ctx *ctx, const volatile void *ptr, size_t size,
int type, unsigned long ip)
{
struct kcsan_scoped_access *reorder_access = get_reorder_access(ctx);
if (!reorder_access)
return false ;
/*
* Note : If accesses are repeated while reorder_access is identical ,
* never matches the new access , because ! ( type & KCSAN_ACCESS_SCOPED ) .
*/
return reorder_access->ptr == ptr && reorder_access->size == size &&
reorder_access->type == type && reorder_access->ip == ip;
}
static inline void
set_reorder_access(struct kcsan_ctx *ctx, const volatile void *ptr, size_t size,
int type, unsigned long ip)
{
struct kcsan_scoped_access *reorder_access = get_reorder_access(ctx);
if (!reorder_access || !kcsan_weak_memory)
return ;
/*
* To avoid nested interrupts or scheduler ( which share kcsan_ctx )
* reading an inconsistent reorder_access , ensure that the below has
* exclusive access to reorder_access by disallowing concurrent use .
*/
ctx->disable_scoped++;
barrier();
reorder_access->ptr = ptr;
reorder_access->size = size;
reorder_access->type = type | KCSAN_ACCESS_SCOPED;
reorder_access->ip = ip;
reorder_access->stack_depth = get_kcsan_stack_depth();
barrier();
ctx->disable_scoped--;
}
/*
* Pull everything together : check_access ( ) below contains the performance
* critical operations ; the fast - path ( including check_access ) functions should
* all be inlinable by the instrumentation functions .
*
* The slow - path ( kcsan_found_watchpoint , kcsan_setup_watchpoint ) are
* non - inlinable - - note that , we prefix these with " kcsan_ " to ensure they can
* be filtered from the stacktrace , as well as give them unique names for the
* UACCESS whitelist of objtool . Each function uses user_access_save / restore ( ) ,
* since they do not access any user memory , but instrumentation is still
* emitted in UACCESS regions .
*/
static noinline void kcsan_found_watchpoint(const volatile void *ptr,
size_t size,
int type,
unsigned long ip,
atomic_long_t *watchpoint,
long encoded_watchpoint)
{
const bool is_assert = (type & KCSAN_ACCESS_ASSERT) != 0 ;
struct kcsan_ctx *ctx = get_ctx();
unsigned long flags;
bool consumed;
/*
* We know a watchpoint exists . Let ' s try to keep the race - window
* between here and finally consuming the watchpoint below as small as
* possible - - avoid unneccessarily complex code until consumed .
*/
if (!kcsan_is_enabled(ctx))
return ;
/*
* The access_mask check relies on value - change comparison . To avoid
* reporting a race where e . g . the writer set up the watchpoint , but the
* reader has access_mask ! = 0 , we have to ignore the found watchpoint .
*
* reorder_access is never created from an access with access_mask set .
*/
if (ctx->access_mask && !find_reorder_access(ctx, ptr, size, type, ip))
return ;
/*
* If the other thread does not want to ignore the access , and there was
* a value change as a result of this thread ' s operation , we will still
* generate a report of unknown origin .
*
* Use CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN = n to filter .
*/
if (!is_assert && kcsan_ignore_address(ptr))
return ;
/*
* Consuming the watchpoint must be guarded by kcsan_is_enabled ( ) to
* avoid erroneously triggering reports if the context is disabled .
*/
consumed = try_consume_watchpoint(watchpoint, encoded_watchpoint);
/* keep this after try_consume_watchpoint */
flags = user_access_save();
if (consumed) {
kcsan_save_irqtrace(current);
kcsan_report_set_info(ptr, size, type, ip, watchpoint - watchpoints);
kcsan_restore_irqtrace(current);
} else {
/*
* The other thread may not print any diagnostics , as it has
* already removed the watchpoint , or another thread consumed
* the watchpoint before this thread .
*/
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_REPORT_RACES]);
}
if (is_assert)
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
else
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_DATA_RACES]);
user_access_restore(flags);
}
static noinline void
kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type, unsigned long ip)
{
const bool is_write = (type & KCSAN_ACCESS_WRITE) != 0 ;
const bool is_assert = (type & KCSAN_ACCESS_ASSERT) != 0 ;
atomic_long_t *watchpoint;
u64 old, new , diff;
enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE;
bool interrupt_watcher = kcsan_interrupt_watcher;
unsigned long ua_flags = user_access_save();
struct kcsan_ctx *ctx = get_ctx();
unsigned long access_mask = ctx->access_mask;
unsigned long irq_flags = 0 ;
bool is_reorder_access;
/*
* Always reset kcsan_skip counter in slow - path to avoid underflow ; see
* should_watch ( ) .
*/
reset_kcsan_skip();
if (!kcsan_is_enabled(ctx))
goto out;
/*
* Check to - ignore addresses after kcsan_is_enabled ( ) , as we may access
* memory that is not yet initialized during early boot .
*/
if (!is_assert && kcsan_ignore_address(ptr))
goto out;
if (!check_encodable((unsigned long )ptr, size)) {
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_UNENCODABLE_ACCESSES]);
goto out;
}
/*
* The local CPU cannot observe reordering of its own accesses , and
* therefore we need to take care of 2 cases to avoid false positives :
*
* 1 . Races of the reordered access with interrupts . To avoid , if
* the current access is reorder_access , disable interrupts .
* 2 . Avoid races of scoped accesses from nested interrupts ( below ) .
*/
is_reorder_access = find_reorder_access(ctx, ptr, size, type, ip);
if (is_reorder_access)
interrupt_watcher = false ;
/*
* Avoid races of scoped accesses from nested interrupts ( or scheduler ) .
* Assume setting up a watchpoint for a non - scoped ( normal ) access that
* also conflicts with a current scoped access . In a nested interrupt ,
* which shares the context , it would check a conflicting scoped access .
* To avoid , disable scoped access checking .
*/
ctx->disable_scoped++;
/*
* Save and restore the IRQ state trace touched by KCSAN , since KCSAN ' s
* runtime is entered for every memory access , and potentially useful
* information is lost if dirtied by KCSAN .
*/
kcsan_save_irqtrace(current);
if (!interrupt_watcher)
local_irq_save(irq_flags);
watchpoint = insert_watchpoint((unsigned long )ptr, size, is_write);
if (watchpoint == NULL) {
/*
* Out of capacity : the size of ' watchpoints ' , and the frequency
* with which should_watch ( ) returns true should be tweaked so
* that this case happens very rarely .
*/
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_NO_CAPACITY]);
goto out_unlock;
}
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_SETUP_WATCHPOINTS]);
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_USED_WATCHPOINTS]);
/*
* Read the current value , to later check and infer a race if the data
* was modified via a non - instrumented access , e . g . from a device .
*/
old = is_reorder_access ? 0 : read_instrumented_memory(ptr, size);
/*
* Delay this thread , to increase probability of observing a racy
* conflicting access .
*/
delay_access(type);
/*
* Re - read value , and check if it is as expected ; if not , we infer a
* racy access .
*/
if (!is_reorder_access) {
new = read_instrumented_memory(ptr, size);
} else {
/*
* Reordered accesses cannot be used for value change detection ,
* because the memory location may no longer be accessible and
* could result in a fault .
*/
new = 0 ;
access_mask = 0 ;
}
diff = old ^ new ;
if (access_mask)
diff &= access_mask;
/*
* Check if we observed a value change .
*
* Also check if the data race should be ignored ( the rules depend on
* non - zero diff ) ; if it is to be ignored , the below rules for
* KCSAN_VALUE_CHANGE_MAYBE apply .
*/
if (diff && !kcsan_ignore_data_race(size, type, old, new , diff))
value_change = KCSAN_VALUE_CHANGE_TRUE;
/* Check if this access raced with another. */
if (!consume_watchpoint(watchpoint)) {
/*
* Depending on the access type , map a value_change of MAYBE to
* TRUE ( always report ) or FALSE ( never report ) .
*/
if (value_change == KCSAN_VALUE_CHANGE_MAYBE) {
if (access_mask != 0 ) {
/*
* For access with access_mask , we require a
* value - change , as it is likely that races on
* ~ access_mask bits are expected .
*/
value_change = KCSAN_VALUE_CHANGE_FALSE;
} else if (size > 8 || is_assert) {
/* Always assume a value-change. */
value_change = KCSAN_VALUE_CHANGE_TRUE;
}
}
/*
* No need to increment ' data_races ' counter , as the racing
* thread already did .
*
* Count ' assert_failures ' for each failed ASSERT access ,
* therefore both this thread and the racing thread may
* increment this counter .
*/
if (is_assert && value_change == KCSAN_VALUE_CHANGE_TRUE)
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
kcsan_report_known_origin(ptr, size, type, ip,
value_change, watchpoint - watchpoints,
old, new , access_mask);
} else if (value_change == KCSAN_VALUE_CHANGE_TRUE) {
/* Inferring a race, since the value should not have changed. */
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_RACES_UNKNOWN_ORIGIN]);
if (is_assert)
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
if (IS_ENABLED(CONFIG_KCSAN_REPORT_RACE_UNKNOWN_ORIGIN) || is_assert) {
kcsan_report_unknown_origin(ptr, size, type, ip,
old, new , access_mask);
}
}
/*
* Remove watchpoint ; must be after reporting , since the slot may be
* reused after this point .
*/
remove_watchpoint(watchpoint);
atomic_long_dec(&kcsan_counters[KCSAN_COUNTER_USED_WATCHPOINTS]);
out_unlock:
if (!interrupt_watcher)
local_irq_restore(irq_flags);
kcsan_restore_irqtrace(current);
ctx->disable_scoped--;
/*
* Reordered accesses cannot be used for value change detection ,
* therefore never consider for reordering if access_mask is set .
* ASSERT_EXCLUSIVE are not real accesses , ignore them as well .
*/
if (!access_mask && !is_assert)
set_reorder_access(ctx, ptr, size, type, ip);
out:
user_access_restore(ua_flags);
}
static __always_inline void
check_access(const volatile void *ptr, size_t size, int type, unsigned long ip)
{
atomic_long_t *watchpoint;
long encoded_watchpoint;
/*
* Do nothing for 0 sized check ; this comparison will be optimized out
* for constant sized instrumentation ( _ _ tsan_ { read , write } N ) .
*/
if (unlikely(size == 0 ))
return ;
again:
/*
* Avoid user_access_save in fast - path : find_watchpoint is safe without
* user_access_save , as the address that ptr points to is only used to
* check if a watchpoint exists ; ptr is never dereferenced .
*/
watchpoint = find_watchpoint((unsigned long )ptr, size,
!(type & KCSAN_ACCESS_WRITE),
&encoded_watchpoint);
/*
* It is safe to check kcsan_is_enabled ( ) after find_watchpoint in the
* slow - path , as long as no state changes that cause a race to be
* detected and reported have occurred until kcsan_is_enabled ( ) is
* checked .
*/
if (unlikely(watchpoint != NULL))
kcsan_found_watchpoint(ptr, size, type, ip, watchpoint, encoded_watchpoint);
else {
struct kcsan_ctx *ctx = get_ctx(); /* Call only once in fast-path. */
if (unlikely(should_watch(ctx, ptr, size, type))) {
kcsan_setup_watchpoint(ptr, size, type, ip);
return ;
}
if (!(type & KCSAN_ACCESS_SCOPED)) {
struct kcsan_scoped_access *reorder_access = get_reorder_access(ctx);
if (reorder_access) {
/*
* reorder_access check : simulates reordering of
* the access after subsequent operations .
*/
ptr = reorder_access->ptr;
type = reorder_access->type;
ip = reorder_access->ip;
/*
* Upon a nested interrupt , this context ' s
* reorder_access can be modified ( shared ctx ) .
* We know that upon return , reorder_access is
* always invalidated by setting size to 0 via
* _ _ tsan_func_exit ( ) . Therefore we must read
* and check size after the other fields .
*/
barrier();
size = READ_ONCE(reorder_access->size);
if (size)
goto again;
}
}
/*
* Always checked last , right before returning from runtime ;
* if reorder_access is valid , checked after it was checked .
*/
if (unlikely(ctx->scoped_accesses.prev))
kcsan_check_scoped_accesses();
}
}
/* === Public interface ===================================================== */
void __init kcsan_init(void )
{
int cpu;
BUG_ON(!in_task());
for_each_possible_cpu(cpu)
per_cpu(kcsan_rand_state, cpu) = (u32)get_cycles();
/*
* We are in the init task , and no other tasks should be running ;
* WRITE_ONCE without memory barrier is sufficient .
*/
if (kcsan_early_enable) {
pr_info("enabled early\n" );
WRITE_ONCE(kcsan_enabled, true );
}
if (IS_ENABLED(CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY) ||
IS_ENABLED(CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC) ||
IS_ENABLED(CONFIG_KCSAN_PERMISSIVE) ||
IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) {
pr_warn("non-strict mode configured - use CONFIG_KCSAN_STRICT=y to see all data races\n" );
} else {
pr_info("strict mode configured\n" );
}
}
/* === Exported interface =================================================== */
void kcsan_disable_current(void )
{
++get_ctx()->disable_count;
}
EXPORT_SYMBOL(kcsan_disable_current);
void kcsan_enable_current(void )
{
if (get_ctx()->disable_count-- == 0 ) {
/*
* Warn if kcsan_enable_current ( ) calls are unbalanced with
* kcsan_disable_current ( ) calls , which causes disable_count to
* become negative and should not happen .
*/
kcsan_disable_current(); /* restore to 0, KCSAN still enabled */
kcsan_disable_current(); /* disable to generate warning */
WARN(1 , "Unbalanced %s()" , __func__);
kcsan_enable_current();
}
}
EXPORT_SYMBOL(kcsan_enable_current);
void kcsan_enable_current_nowarn(void )
{
if (get_ctx()->disable_count-- == 0 )
kcsan_disable_current();
}
EXPORT_SYMBOL(kcsan_enable_current_nowarn);
void kcsan_nestable_atomic_begin(void )
{
/*
* Do * not * check and warn if we are in a flat atomic region : nestable
* and flat atomic regions are independent from each other .
* See include / linux / kcsan . h : struct kcsan_ctx comments for more
* comments .
*/
++get_ctx()->atomic_nest_count;
}
EXPORT_SYMBOL(kcsan_nestable_atomic_begin);
void kcsan_nestable_atomic_end(void )
{
if (get_ctx()->atomic_nest_count-- == 0 ) {
/*
* Warn if kcsan_nestable_atomic_end ( ) calls are unbalanced with
* kcsan_nestable_atomic_begin ( ) calls , which causes
* atomic_nest_count to become negative and should not happen .
*/
kcsan_nestable_atomic_begin(); /* restore to 0 */
kcsan_disable_current(); /* disable to generate warning */
WARN(1 , "Unbalanced %s()" , __func__);
kcsan_enable_current();
}
}
EXPORT_SYMBOL(kcsan_nestable_atomic_end);
void kcsan_flat_atomic_begin(void )
{
get_ctx()->in_flat_atomic = true ;
}
EXPORT_SYMBOL(kcsan_flat_atomic_begin);
void kcsan_flat_atomic_end(void )
{
get_ctx()->in_flat_atomic = false ;
}
EXPORT_SYMBOL(kcsan_flat_atomic_end);
void kcsan_atomic_next(int n)
{
get_ctx()->atomic_next = n;
}
EXPORT_SYMBOL(kcsan_atomic_next);
void kcsan_set_access_mask(unsigned long mask)
{
get_ctx()->access_mask = mask;
}
EXPORT_SYMBOL(kcsan_set_access_mask);
struct kcsan_scoped_access *
kcsan_begin_scoped_access(const volatile void *ptr, size_t size, int type,
struct kcsan_scoped_access *sa)
{
struct kcsan_ctx *ctx = get_ctx();
check_access(ptr, size, type, _RET_IP_);
ctx->disable_count++; /* Disable KCSAN, in case list debugging is on. */
INIT_LIST_HEAD(&sa->list);
sa->ptr = ptr;
sa->size = size;
sa->type = type;
sa->ip = _RET_IP_;
if (!ctx->scoped_accesses.prev) /* Lazy initialize list head. */
INIT_LIST_HEAD(&ctx->scoped_accesses);
list_add(&sa->list, &ctx->scoped_accesses);
ctx->disable_count--;
return sa;
}
EXPORT_SYMBOL(kcsan_begin_scoped_access);
void kcsan_end_scoped_access(struct kcsan_scoped_access *sa)
{
struct kcsan_ctx *ctx = get_ctx();
if (WARN(!ctx->scoped_accesses.prev, "Unbalanced %s()?" , __func__))
return ;
ctx->disable_count++; /* Disable KCSAN, in case list debugging is on. */
list_del(&sa->list);
if (list_empty(&ctx->scoped_accesses))
/*
* Ensure we do not enter kcsan_check_scoped_accesses ( )
* slow - path if unnecessary , and avoids requiring list_empty ( )
* in the fast - path ( to avoid a READ_ONCE ( ) and potential
* uaccess warning ) .
*/
ctx->scoped_accesses.prev = NULL;
ctx->disable_count--;
check_access(sa->ptr, sa->size, sa->type, sa->ip);
}
EXPORT_SYMBOL(kcsan_end_scoped_access);
void __kcsan_check_access(const volatile void *ptr, size_t size, int type)
{
check_access(ptr, size, type, _RET_IP_);
}
EXPORT_SYMBOL(__kcsan_check_access);
#define DEFINE_MEMORY_BARRIER(name, order_before_cond) \
void __kcsan_## name(void ) \
{ \
struct kcsan_scoped_access *sa = get_reorder_access(get_ctx()); \
if (!sa) \
return ; \
if (order_before_cond) \
sa->size = 0 ; \
} \
EXPORT_SYMBOL(__kcsan_## name)
DEFINE_MEMORY_BARRIER(mb, true );
DEFINE_MEMORY_BARRIER(wmb, sa->type & (KCSAN_ACCESS_WRITE | KCSAN_ACCESS_COMPOUND));
DEFINE_MEMORY_BARRIER(rmb, !(sa->type & KCSAN_ACCESS_WRITE) || (sa->type & KCSAN_ACCESS_COMPOUND));
DEFINE_MEMORY_BARRIER(release, true );
/*
* KCSAN uses the same instrumentation that is emitted by supported compilers
* for ThreadSanitizer ( TSAN ) .
*
* When enabled , the compiler emits instrumentation calls ( the functions
* prefixed with " _ _ tsan " below ) for all loads and stores that it generated ;
* inline asm is not instrumented .
*
* Note that , not all supported compiler versions distinguish aligned / unaligned
* accesses , but e . g . recent versions of Clang do . We simply alias the unaligned
* version to the generic version , which can handle both .
*/
#define DEFINE_TSAN_READ_WRITE(size) \
void __tsan_read## size(void *ptr); \
void __tsan_read## size(void *ptr) \
{ \
check_access(ptr, size, 0 , _RET_IP_); \
} \
EXPORT_SYMBOL(__tsan_read## size); \
void __tsan_unaligned_read## size(void *ptr) \
__alias(__tsan_read## size); \
EXPORT_SYMBOL(__tsan_unaligned_read## size); \
void __tsan_write## size(void *ptr); \
void __tsan_write## size(void *ptr) \
{ \
check_access(ptr, size, KCSAN_ACCESS_WRITE, _RET_IP_); \
} \
EXPORT_SYMBOL(__tsan_write## size); \
void __tsan_unaligned_write## size(void *ptr) \
__alias(__tsan_write## size); \
EXPORT_SYMBOL(__tsan_unaligned_write## size); \
void __tsan_read_write## size(void *ptr); \
void __tsan_read_write## size(void *ptr) \
{ \
check_access(ptr, size, \
KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE, \
_RET_IP_); \
} \
EXPORT_SYMBOL(__tsan_read_write## size); \
void __tsan_unaligned_read_write## size(void *ptr) \
__alias(__tsan_read_write## size); \
EXPORT_SYMBOL(__tsan_unaligned_read_write## size)
DEFINE_TSAN_READ_WRITE(1 );
DEFINE_TSAN_READ_WRITE(2 );
DEFINE_TSAN_READ_WRITE(4 );
DEFINE_TSAN_READ_WRITE(8 );
DEFINE_TSAN_READ_WRITE(16 );
void __tsan_read_range(void *ptr, size_t size);
void __tsan_read_range(void *ptr, size_t size)
{
check_access(ptr, size, 0 , _RET_IP_);
}
EXPORT_SYMBOL(__tsan_read_range);
void __tsan_write_range(void *ptr, size_t size);
void __tsan_write_range(void *ptr, size_t size)
{
check_access(ptr, size, KCSAN_ACCESS_WRITE, _RET_IP_);
}
EXPORT_SYMBOL(__tsan_write_range);
/*
* Use of explicit volatile is generally disallowed [ 1 ] , however , volatile is
* still used in various concurrent context , whether in low - level
* synchronization primitives or for legacy reasons .
* [ 1 ] https : //lwn.net/Articles/233479/
*
* We only consider volatile accesses atomic if they are aligned and would pass
* the size - check of compiletime_assert_rwonce_type ( ) .
*/
#define DEFINE_TSAN_VOLATILE_READ_WRITE(size) \
void __tsan_volatile_read## size(void *ptr); \
void __tsan_volatile_read## size(void *ptr) \
{ \
const bool is_atomic = size <= sizeof (long long ) && \
IS_ALIGNED((unsigned long )ptr, size); \
if (IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS) && is_atomic) \
return ; \
check_access(ptr, size, is_atomic ? KCSAN_ACCESS_ATOMIC : 0 , \
_RET_IP_); \
} \
EXPORT_SYMBOL(__tsan_volatile_read## size); \
void __tsan_unaligned_volatile_read## size(void *ptr) \
__alias(__tsan_volatile_read## size); \
EXPORT_SYMBOL(__tsan_unaligned_volatile_read## size); \
void __tsan_volatile_write## size(void *ptr); \
void __tsan_volatile_write## size(void *ptr) \
{ \
const bool is_atomic = size <= sizeof (long long ) && \
IS_ALIGNED((unsigned long )ptr, size); \
if (IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS) && is_atomic) \
return ; \
check_access(ptr, size, \
KCSAN_ACCESS_WRITE | \
(is_atomic ? KCSAN_ACCESS_ATOMIC : 0 ), \
_RET_IP_); \
} \
EXPORT_SYMBOL(__tsan_volatile_write## size); \
void __tsan_unaligned_volatile_write## size(void *ptr) \
__alias(__tsan_volatile_write## size); \
EXPORT_SYMBOL(__tsan_unaligned_volatile_write## size)
DEFINE_TSAN_VOLATILE_READ_WRITE(1 );
DEFINE_TSAN_VOLATILE_READ_WRITE(2 );
DEFINE_TSAN_VOLATILE_READ_WRITE(4 );
DEFINE_TSAN_VOLATILE_READ_WRITE(8 );
DEFINE_TSAN_VOLATILE_READ_WRITE(16 );
/*
* Function entry and exit are used to determine the validty of reorder_access .
* Reordering of the access ends at the end of the function scope where the
* access happened . This is done for two reasons :
*
* 1 . Artificially limits the scope where missing barriers are detected .
* This minimizes false positives due to uninstrumented functions that
* contain the required barriers but were missed .
*
* 2 . Simplifies generating the stack trace of the access .
*/
void __tsan_func_entry(void *call_pc);
noinline void __tsan_func_entry(void *call_pc)
{
if (!IS_ENABLED(CONFIG_KCSAN_WEAK_MEMORY))
return ;
add_kcsan_stack_depth(1 );
}
EXPORT_SYMBOL(__tsan_func_entry);
void __tsan_func_exit(void );
noinline void __tsan_func_exit(void )
{
struct kcsan_scoped_access *reorder_access;
if (!IS_ENABLED(CONFIG_KCSAN_WEAK_MEMORY))
return ;
reorder_access = get_reorder_access(get_ctx());
if (!reorder_access)
goto out;
if (get_kcsan_stack_depth() <= reorder_access->stack_depth) {
/*
* Access check to catch cases where write without a barrier
* ( supposed release ) was last access in function : because
* instrumentation is inserted before the real access , a data
* race due to the write giving up a c - s would only be caught if
* we do the conflicting access after .
*/
check_access(reorder_access->ptr, reorder_access->size,
reorder_access->type, reorder_access->ip);
reorder_access->size = 0 ;
reorder_access->stack_depth = INT_MIN;
}
out:
add_kcsan_stack_depth(-1 );
}
EXPORT_SYMBOL(__tsan_func_exit);
void __tsan_init(void );
void __tsan_init(void )
{
}
EXPORT_SYMBOL(__tsan_init);
/*
* Instrumentation for atomic builtins ( _ _ atomic_ * , _ _ sync_ * ) .
*
* Normal kernel code _ should not_ be using them directly , but some
* architectures may implement some or all atomics using the compilers '
* builtins .
*
* Note : If an architecture decides to fully implement atomics using the
* builtins , because they are implicitly instrumented by KCSAN ( and KASAN ,
* etc . ) , implementing the ARCH_ATOMIC interface ( to get instrumentation via
* atomic - instrumented ) is no longer necessary .
*
* TSAN instrumentation replaces atomic accesses with calls to any of the below
* functions , whose job is to also execute the operation itself .
*/
static __always_inline void kcsan_atomic_builtin_memorder(int memorder)
{
if (memorder == __ATOMIC_RELEASE ||
memorder == __ATOMIC_SEQ_CST ||
memorder == __ATOMIC_ACQ_REL)
__kcsan_release();
}
#define DEFINE_TSAN_ATOMIC_LOAD_STORE(bits) \
u## bits __tsan_atomic## bits## _load(const u## bits *ptr, int memorder); \
u## bits __tsan_atomic## bits## _load(const u## bits *ptr, int memorder) \
{ \
kcsan_atomic_builtin_memorder(memorder); \
if (!IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) { \
check_access(ptr, bits / BITS_PER_BYTE, KCSAN_ACCESS_ATOMIC, _RET_IP_); \
} \
return __atomic_load_n(ptr, memorder); \
} \
EXPORT_SYMBOL(__tsan_atomic## bits## _load); \
void __tsan_atomic## bits## _store(u## bits *ptr, u## bits v, int memorder); \
void __tsan_atomic## bits## _store(u## bits *ptr, u## bits v, int memorder) \
{ \
kcsan_atomic_builtin_memorder(memorder); \
if (!IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) { \
check_access(ptr, bits / BITS_PER_BYTE, \
KCSAN_ACCESS_WRITE | KCSAN_ACCESS_ATOMIC, _RET_IP_); \
} \
__atomic_store_n(ptr, v, memorder); \
} \
EXPORT_SYMBOL(__tsan_atomic## bits## _store)
#define DEFINE_TSAN_ATOMIC_RMW(op, bits, suffix) \
u## bits __tsan_atomic## bits## _## op(u## bits *ptr, u## bits v, int memorder); \
u## bits __tsan_atomic## bits## _## op(u## bits *ptr, u## bits v, int memorder) \
{ \
kcsan_atomic_builtin_memorder(memorder); \
if (!IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) { \
check_access(ptr, bits / BITS_PER_BYTE, \
KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE | \
KCSAN_ACCESS_ATOMIC, _RET_IP_); \
} \
return __atomic_## op## suffix(ptr, v, memorder); \
} \
EXPORT_SYMBOL(__tsan_atomic## bits## _## op)
/*
* Note : CAS operations are always classified as write , even in case they
* fail . We cannot perform check_access ( ) after a write , as it might lead to
* false positives , in cases such as :
*
* T0 : _ _ atomic_compare_exchange_n ( & p - > flag , & old , 1 , . . . )
*
* T1 : if ( _ _ atomic_load_n ( & p - > flag , . . . ) ) {
* modify * p ;
* p - > flag = 0 ;
* }
*
* The only downside is that , if there are 3 threads , with one CAS that
* succeeds , another CAS that fails , and an unmarked racing operation , we may
* point at the wrong CAS as the source of the race . However , if we assume that
* all CAS can succeed in some other execution , the data race is still valid .
*/
#define DEFINE_TSAN_ATOMIC_CMPXCHG(bits, strength, weak) \
int __tsan_atomic## bits## _compare_exchange_## strength(u## bits *ptr, u## bits *exp, \
u## bits val, int mo, int fail_mo); \
int __tsan_atomic## bits## _compare_exchange_## strength(u## bits *ptr, u## bits *exp, \
u## bits val, int mo, int fail_mo) \
{ \
kcsan_atomic_builtin_memorder(mo); \
if (!IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) { \
check_access(ptr, bits / BITS_PER_BYTE, \
KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE | \
KCSAN_ACCESS_ATOMIC, _RET_IP_); \
} \
return __atomic_compare_exchange_n(ptr, exp, val, weak, mo, fail_mo); \
} \
EXPORT_SYMBOL(__tsan_atomic## bits## _compare_exchange_## strength)
#define DEFINE_TSAN_ATOMIC_CMPXCHG_VAL(bits) \
u## bits __tsan_atomic## bits## _compare_exchange_val(u## bits *ptr, u## bits exp, u## bits val, \
int mo, int fail_mo); \
u## bits __tsan_atomic## bits## _compare_exchange_val(u## bits *ptr, u## bits exp, u## bits val, \
int mo, int fail_mo) \
{ \
kcsan_atomic_builtin_memorder(mo); \
if (!IS_ENABLED(CONFIG_KCSAN_IGNORE_ATOMICS)) { \
check_access(ptr, bits / BITS_PER_BYTE, \
KCSAN_ACCESS_COMPOUND | KCSAN_ACCESS_WRITE | \
KCSAN_ACCESS_ATOMIC, _RET_IP_); \
} \
__atomic_compare_exchange_n(ptr, &exp, val, 0 , mo, fail_mo); \
return exp; \
} \
EXPORT_SYMBOL(__tsan_atomic## bits## _compare_exchange_val)
#define DEFINE_TSAN_ATOMIC_OPS(bits) \
DEFINE_TSAN_ATOMIC_LOAD_STORE(bits); \
DEFINE_TSAN_ATOMIC_RMW(exchange, bits, _n); \
DEFINE_TSAN_ATOMIC_RMW(fetch_add, bits, ); \
DEFINE_TSAN_ATOMIC_RMW(fetch_sub, bits, ); \
DEFINE_TSAN_ATOMIC_RMW(fetch_and, bits, ); \
DEFINE_TSAN_ATOMIC_RMW(fetch_or, bits, ); \
DEFINE_TSAN_ATOMIC_RMW(fetch_xor, bits, ); \
DEFINE_TSAN_ATOMIC_RMW(fetch_nand, bits, ); \
DEFINE_TSAN_ATOMIC_CMPXCHG(bits, strong, 0 ); \
DEFINE_TSAN_ATOMIC_CMPXCHG(bits, weak, 1 ); \
DEFINE_TSAN_ATOMIC_CMPXCHG_VAL(bits)
DEFINE_TSAN_ATOMIC_OPS(8 );
DEFINE_TSAN_ATOMIC_OPS(16 );
DEFINE_TSAN_ATOMIC_OPS(32 );
#ifdef CONFIG_64BIT
DEFINE_TSAN_ATOMIC_OPS(64 );
#endif
void __tsan_atomic_thread_fence(int memorder);
void __tsan_atomic_thread_fence(int memorder)
{
kcsan_atomic_builtin_memorder(memorder);
__atomic_thread_fence(memorder);
}
EXPORT_SYMBOL(__tsan_atomic_thread_fence);
/*
* In instrumented files , we emit instrumentation for barriers by mapping the
* kernel barriers to an _ _ atomic_signal_fence ( ) , which is interpreted specially
* and otherwise has no relation to a real _ _ atomic_signal_fence ( ) . No known
* kernel code uses _ _ atomic_signal_fence ( ) .
*
* Since fsanitize = thread instrumentation handles _ _ atomic_signal_fence ( ) , which
* are turned into calls to _ _ tsan_atomic_signal_fence ( ) , such instrumentation
* can be disabled via the _ _ no_kcsan function attribute ( vs . an explicit call
* which could not ) . When _ _ no_kcsan is requested , _ _ atomic_signal_fence ( )
* generates no code .
*
* Note : The result of using _ _ atomic_signal_fence ( ) with KCSAN enabled is
* potentially limiting the compiler ' s ability to reorder operations ; however ,
* if barriers were instrumented with explicit calls ( without LTO ) , the compiler
* couldn ' t optimize much anyway . The result of a hypothetical architecture
* using _ _ atomic_signal_fence ( ) in normal code would be KCSAN false negatives .
*/
void __tsan_atomic_signal_fence(int memorder);
noinline void __tsan_atomic_signal_fence(int memorder)
{
switch (memorder) {
case __KCSAN_BARRIER_TO_SIGNAL_FENCE_mb:
__kcsan_mb();
break ;
case __KCSAN_BARRIER_TO_SIGNAL_FENCE_wmb:
__kcsan_wmb();
break ;
case __KCSAN_BARRIER_TO_SIGNAL_FENCE_rmb:
__kcsan_rmb();
break ;
case __KCSAN_BARRIER_TO_SIGNAL_FENCE_release:
__kcsan_release();
break ;
default :
break ;
}
}
EXPORT_SYMBOL(__tsan_atomic_signal_fence);
#ifdef __HAVE_ARCH_MEMSET
void *__tsan_memset(void *s, int c, size_t count);
noinline void *__tsan_memset(void *s, int c, size_t count)
{
/*
* Instead of not setting up watchpoints where accessed size is greater
* than MAX_ENCODABLE_SIZE , truncate checked size to MAX_ENCODABLE_SIZE .
*/
size_t check_len = min_t(size_t, count, MAX_ENCODABLE_SIZE);
check_access(s, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
return memset(s, c, count);
}
#else
void *__tsan_memset(void *s, int c, size_t count) __alias(memset);
#endif
EXPORT_SYMBOL(__tsan_memset);
#ifdef __HAVE_ARCH_MEMMOVE
void *__tsan_memmove(void *dst, const void *src, size_t len);
noinline void *__tsan_memmove(void *dst, const void *src, size_t len)
{
size_t check_len = min_t(size_t, len, MAX_ENCODABLE_SIZE);
check_access(dst, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
check_access(src, check_len, 0 , _RET_IP_);
return memmove(dst, src, len);
}
#else
void *__tsan_memmove(void *dst, const void *src, size_t len) __alias(memmove);
#endif
EXPORT_SYMBOL(__tsan_memmove);
#ifdef __HAVE_ARCH_MEMCPY
void *__tsan_memcpy(void *dst, const void *src, size_t len);
noinline void *__tsan_memcpy(void *dst, const void *src, size_t len)
{
size_t check_len = min_t(size_t, len, MAX_ENCODABLE_SIZE);
check_access(dst, check_len, KCSAN_ACCESS_WRITE, _RET_IP_);
check_access(src, check_len, 0 , _RET_IP_);
return memcpy(dst, src, len);
}
#else
void *__tsan_memcpy(void *dst, const void *src, size_t len) __alias(memcpy);
#endif
EXPORT_SYMBOL(__tsan_memcpy);
Messung V0.5 in Prozent C=94 H=98 G=95
¤ Dauer der Verarbeitung: 0.41 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland