kcsan: Rework atomic.h into permissive.h
Rework atomic.h into permissive.h to better reflect its purpose, and introduce kcsan_ignore_address() and kcsan_ignore_data_race(). Introduce CONFIG_KCSAN_PERMISSIVE and update the stub functions in preparation for subsequent changes. As before, developers who choose to use KCSAN in "strict" mode will see all data races and are not affected. Furthermore, by relying on the value-change filter logic for kcsan_ignore_data_race(), even if the permissive rules are enabled, the opt-outs in report.c:skip_report() override them (such as for RCU-related functions by default). The option CONFIG_KCSAN_PERMISSIVE is disabled by default, so that the documented default behaviour of KCSAN does not change. Instead, like CONFIG_KCSAN_IGNORE_ATOMICS, the option needs to be explicitly opted in. Signed-off-by: Marco Elver <elver@google.com> Acked-by: Mark Rutland <mark.rutland@arm.com> Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
This commit is contained in:
parent
08cac60494
commit
49f72d5358
@ -127,6 +127,14 @@ Kconfig options:
|
||||
causes KCSAN to not report data races due to conflicts where the only plain
|
||||
accesses are aligned writes up to word size.
|
||||
|
||||
* ``CONFIG_KCSAN_PERMISSIVE``: Enable additional permissive rules to ignore
|
||||
certain classes of common data races. Unlike the above, the rules are more
|
||||
complex involving value-change patterns, access type, and address. This
|
||||
option depends on ``CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=y``. For details
|
||||
please see the ``kernel/kcsan/permissive.h``. Testers and maintainers that
|
||||
only focus on reports from specific subsystems and not the whole kernel are
|
||||
recommended to disable this option.
|
||||
|
||||
To use the strictest possible rules, select ``CONFIG_KCSAN_STRICT=y``, which
|
||||
configures KCSAN to follow the Linux-kernel memory consistency model (LKMM) as
|
||||
closely as possible.
|
||||
|
@ -1,23 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Rules for implicitly atomic memory accesses.
|
||||
*
|
||||
* Copyright (C) 2019, Google LLC.
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_KCSAN_ATOMIC_H
|
||||
#define _KERNEL_KCSAN_ATOMIC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Special rules for certain memory where concurrent conflicting accesses are
|
||||
* common, however, the current convention is to not mark them; returns true if
|
||||
* access to @ptr should be considered atomic. Called from slow-path.
|
||||
*/
|
||||
static bool kcsan_is_atomic_special(const volatile void *ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* _KERNEL_KCSAN_ATOMIC_H */
|
@ -20,9 +20,9 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "atomic.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;
|
||||
@ -353,6 +353,7 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
|
||||
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;
|
||||
@ -374,6 +375,16 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
|
||||
if (ctx->access_mask)
|
||||
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.
|
||||
@ -396,7 +407,7 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
|
||||
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_REPORT_RACES]);
|
||||
}
|
||||
|
||||
if ((type & KCSAN_ACCESS_ASSERT) != 0)
|
||||
if (is_assert)
|
||||
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
|
||||
else
|
||||
atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_DATA_RACES]);
|
||||
@ -427,12 +438,10 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* Special atomic rules: unlikely to be true, so we check them here in
|
||||
* the slow-path, and not in the fast-path in is_atomic(). Call after
|
||||
* kcsan_is_enabled(), as we may access memory that is not yet
|
||||
* initialized during early boot.
|
||||
* 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_is_atomic_special(ptr))
|
||||
if (!is_assert && kcsan_ignore_address(ptr))
|
||||
goto out;
|
||||
|
||||
if (!check_encodable((unsigned long)ptr, size)) {
|
||||
@ -518,8 +527,14 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
|
||||
if (access_mask)
|
||||
diff &= access_mask;
|
||||
|
||||
/* Were we able to observe a value-change? */
|
||||
if (diff != 0)
|
||||
/*
|
||||
* 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. */
|
||||
|
47
kernel/kcsan/permissive.h
Normal file
47
kernel/kcsan/permissive.h
Normal file
@ -0,0 +1,47 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Special rules for ignoring entire classes of data-racy memory accesses. None
|
||||
* of the rules here imply that such data races are generally safe!
|
||||
*
|
||||
* All rules in this file can be configured via CONFIG_KCSAN_PERMISSIVE. Keep
|
||||
* them separate from core code to make it easier to audit.
|
||||
*
|
||||
* Copyright (C) 2019, Google LLC.
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_KCSAN_PERMISSIVE_H
|
||||
#define _KERNEL_KCSAN_PERMISSIVE_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* Access ignore rules based on address.
|
||||
*/
|
||||
static __always_inline bool kcsan_ignore_address(const volatile void *ptr)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_KCSAN_PERMISSIVE))
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Data race ignore rules based on access type and value change patterns.
|
||||
*/
|
||||
static bool
|
||||
kcsan_ignore_data_race(size_t size, int type, u64 old, u64 new, u64 diff)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_KCSAN_PERMISSIVE))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Rules here are only for plain read accesses, so that we still report
|
||||
* data races between plain read-write accesses.
|
||||
*/
|
||||
if (type || size > sizeof(long))
|
||||
return false;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif /* _KERNEL_KCSAN_PERMISSIVE_H */
|
@ -231,4 +231,14 @@ config KCSAN_IGNORE_ATOMICS
|
||||
due to two conflicting plain writes will be reported (aligned and
|
||||
unaligned, if CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n).
|
||||
|
||||
config KCSAN_PERMISSIVE
|
||||
bool "Enable all additional permissive rules"
|
||||
depends on KCSAN_REPORT_VALUE_CHANGE_ONLY
|
||||
help
|
||||
Enable additional permissive rules to ignore certain classes of data
|
||||
races (also see kernel/kcsan/permissive.h). None of the permissive
|
||||
rules imply that such data races are generally safe, but can be used
|
||||
to further reduce reported data races due to data-racy patterns
|
||||
common across the kernel.
|
||||
|
||||
endif # KCSAN
|
||||
|
Loading…
Reference in New Issue
Block a user