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:
Marco Elver 2021-06-07 14:56:51 +02:00 committed by Paul E. McKenney
parent 08cac60494
commit 49f72d5358
5 changed files with 89 additions and 32 deletions

View File

@ -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.

View File

@ -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 */

View File

@ -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
View 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 */

View File

@ -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