Commit 7f3b4579 authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'kcsan' of...

Merge branch 'kcsan' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu

 into locking/debug

Pull KCSAN updates from Paul E. McKenney:

 - improve comments
 - introduce CONFIG_KCSAN_STRICT (which RCU uses)
 - optimize use of get_ctx() by kcsan_found_watchpoint()
 - rework atomic.h into permissive.h
 - add the ability to ignore writes that change only one bit of a given data-racy variable.

Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 9ae6ab27 e0493804
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -127,6 +127,18 @@ 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.

DebugFS interface
~~~~~~~~~~~~~~~~~

kernel/kcsan/atomic.h

deleted100644 → 0
+0 −23
Original line number Diff line number Diff line
/* 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 */
+49 −28
Original line number Diff line number Diff line
@@ -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;
@@ -301,9 +301,9 @@ static inline void reset_kcsan_skip(void)
	this_cpu_write(kcsan_skip, skip_count);
}

static __always_inline bool kcsan_is_enabled(void)
static __always_inline bool kcsan_is_enabled(struct kcsan_ctx *ctx)
{
	return READ_ONCE(kcsan_enabled) && get_ctx()->disable_count == 0;
	return READ_ONCE(kcsan_enabled) && !ctx->disable_count;
}

/* Introduce delay depending on context and configuration. */
@@ -353,10 +353,18 @@ 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;

	if (!kcsan_is_enabled())
	/*
	 * 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;

	/*
@@ -364,14 +372,22 @@ static noinline void kcsan_found_watchpoint(const volatile void *ptr,
	 * 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.
	 */
	if (get_ctx()->access_mask != 0)
	if (ctx->access_mask)
		return;

	/*
	 * Consume the watchpoint as soon as possible, to minimize the chances
	 * of !consumed. Consuming the watchpoint must always be guarded by
	 * kcsan_is_enabled() check, as otherwise we might erroneously
	 * triggering reports when disabled.
	 * 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);

@@ -391,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]);
@@ -409,6 +425,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
	unsigned long access_mask;
	enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE;
	unsigned long ua_flags = user_access_save();
	struct kcsan_ctx *ctx = get_ctx();
	unsigned long irq_flags = 0;

	/*
@@ -417,16 +434,14 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
	 */
	reset_kcsan_skip();

	if (!kcsan_is_enabled())
	if (!kcsan_is_enabled(ctx))
		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)) {
@@ -479,15 +494,6 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
		break; /* ignore; we do not diff the values */
	}

	if (IS_ENABLED(CONFIG_KCSAN_DEBUG)) {
		kcsan_disable_current();
		pr_err("watching %s, size: %zu, addr: %px [slot: %d, encoded: %lx]\n",
		       is_write ? "write" : "read", size, ptr,
		       watchpoint_slot((unsigned long)ptr),
		       encode_watchpoint((unsigned long)ptr, size, is_write));
		kcsan_enable_current();
	}

	/*
	 * Delay this thread, to increase probability of observing a racy
	 * conflicting access.
@@ -498,7 +504,7 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
	 * Re-read value, and check if it is as expected; if not, we infer a
	 * racy access.
	 */
	access_mask = get_ctx()->access_mask;
	access_mask = ctx->access_mask;
	new = 0;
	switch (size) {
	case 1:
@@ -521,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. */
@@ -644,6 +656,15 @@ void __init kcsan_init(void)
		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 =================================================== */
+32 −0
Original line number Diff line number Diff line
@@ -414,6 +414,14 @@ static noinline void test_kernel_atomic_builtins(void)
	__atomic_load_n(&test_var, __ATOMIC_RELAXED);
}

static noinline void test_kernel_xor_1bit(void)
{
	/* Do not report data races between the read-writes. */
	kcsan_nestable_atomic_begin();
	test_var ^= 0x10000;
	kcsan_nestable_atomic_end();
}

/* ===== Test cases ===== */

/* Simple test with normal data race. */
@@ -952,6 +960,29 @@ static void test_atomic_builtins(struct kunit *test)
	KUNIT_EXPECT_FALSE(test, match_never);
}

__no_kcsan
static void test_1bit_value_change(struct kunit *test)
{
	const struct expect_report expect = {
		.access = {
			{ test_kernel_read, &test_var, sizeof(test_var), 0 },
			{ test_kernel_xor_1bit, &test_var, sizeof(test_var), __KCSAN_ACCESS_RW(KCSAN_ACCESS_WRITE) },
		},
	};
	bool match = false;

	begin_test_checks(test_kernel_read, test_kernel_xor_1bit);
	do {
		match = IS_ENABLED(CONFIG_KCSAN_PERMISSIVE)
				? report_available()
				: report_matches(&expect);
	} while (!end_test_checks(match));
	if (IS_ENABLED(CONFIG_KCSAN_PERMISSIVE))
		KUNIT_EXPECT_FALSE(test, match);
	else
		KUNIT_EXPECT_TRUE(test, match);
}

/*
 * Generate thread counts for all test cases. Values generated are in interval
 * [2, 5] followed by exponentially increasing thread counts from 8 to 32.
@@ -1024,6 +1055,7 @@ static struct kunit_case kcsan_test_cases[] = {
	KCSAN_KUNIT_CASE(test_jiffies_noreport),
	KCSAN_KUNIT_CASE(test_seqlock_noreport),
	KCSAN_KUNIT_CASE(test_atomic_builtins),
	KCSAN_KUNIT_CASE(test_1bit_value_change),
	{},
};

+94 −0
Original line number Diff line number Diff line
/* 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/bitops.h>
#include <linux/sched.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;

	/*
	 * Data-racy bitops on current->flags are too common, ignore completely
	 * for now.
	 */
	return ptr == &current->flags;
}

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

	/*
	 * A common pattern is checking/setting just 1 bit in a variable; for
	 * example:
	 *
	 *	if (flags & SOME_FLAG) { ... }
	 *
	 * and elsewhere flags is updated concurrently:
	 *
	 *	flags |= SOME_OTHER_FLAG; // just 1 bit
	 *
	 * While it is still recommended that such accesses be marked
	 * appropriately, in many cases these types of data races are so common
	 * that marking them all is often unrealistic and left to maintainer
	 * preference.
	 *
	 * The assumption in all cases is that with all known compiler
	 * optimizations (including those that tear accesses), because no more
	 * than 1 bit changed, the plain accesses are safe despite the presence
	 * of data races.
	 *
	 * The rules here will ignore the data races if we observe no more than
	 * 1 bit changed.
	 *
	 * Of course many operations can effecively change just 1 bit, but the
	 * general assuption that data races involving 1-bit changes can be
	 * tolerated still applies.
	 *
	 * And in case a true bug is missed, the bug likely manifests as a
	 * reportable data race elsewhere.
	 */
	if (hweight64(diff) == 1) {
		/*
		 * Exception: Report data races where the values look like
		 * ordinary booleans (one of them was 0 and the 0th bit was
		 * changed) More often than not, they come with interesting
		 * memory ordering requirements, so let's report them.
		 */
		if (!((!old || !new) && diff == 1))
			return true;
	}

	return false;
}

#endif /* _KERNEL_KCSAN_PERMISSIVE_H */
Loading