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 Original line 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
  causes KCSAN to not report data races due to conflicts where the only plain
  accesses are aligned writes up to word size.
  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
DebugFS interface
~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~


kernel/kcsan/atomic.h

deleted100644 → 0
+0 −23
Original line number Original line 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 Original line Diff line number Diff line
@@ -20,9 +20,9 @@
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/uaccess.h>


#include "atomic.h"
#include "encoding.h"
#include "encoding.h"
#include "kcsan.h"
#include "kcsan.h"
#include "permissive.h"


static bool kcsan_early_enable = IS_ENABLED(CONFIG_KCSAN_EARLY_ENABLE);
static bool kcsan_early_enable = IS_ENABLED(CONFIG_KCSAN_EARLY_ENABLE);
unsigned int kcsan_udelay_task = CONFIG_KCSAN_UDELAY_TASK;
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);
	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. */
/* 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,
					    atomic_long_t *watchpoint,
					    long encoded_watchpoint)
					    long encoded_watchpoint)
{
{
	const bool is_assert = (type & KCSAN_ACCESS_ASSERT) != 0;
	struct kcsan_ctx *ctx = get_ctx();
	unsigned long flags;
	unsigned long flags;
	bool consumed;
	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;
		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
	 * 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.
	 * reader has access_mask!=0, we have to ignore the found watchpoint.
	 */
	 */
	if (get_ctx()->access_mask != 0)
	if (ctx->access_mask)
		return;
		return;


	/*
	/*
	 * Consume the watchpoint as soon as possible, to minimize the chances
	 * If the other thread does not want to ignore the access, and there was
	 * of !consumed. Consuming the watchpoint must always be guarded by
	 * a value change as a result of this thread's operation, we will still
	 * kcsan_is_enabled() check, as otherwise we might erroneously
	 * generate a report of unknown origin.
	 * triggering reports when disabled.
	 *
	 * 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);
	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]);
		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]);
		atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_ASSERT_FAILURES]);
	else
	else
		atomic_long_inc(&kcsan_counters[KCSAN_COUNTER_DATA_RACES]);
		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;
	unsigned long access_mask;
	enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE;
	enum kcsan_value_change value_change = KCSAN_VALUE_CHANGE_MAYBE;
	unsigned long ua_flags = user_access_save();
	unsigned long ua_flags = user_access_save();
	struct kcsan_ctx *ctx = get_ctx();
	unsigned long irq_flags = 0;
	unsigned long irq_flags = 0;


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


	if (!kcsan_is_enabled())
	if (!kcsan_is_enabled(ctx))
		goto out;
		goto out;


	/*
	/*
	 * Special atomic rules: unlikely to be true, so we check them here in
	 * Check to-ignore addresses after kcsan_is_enabled(), as we may access
	 * the slow-path, and not in the fast-path in is_atomic(). Call after
	 * memory that is not yet initialized during early boot.
	 * 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;
		goto out;


	if (!check_encodable((unsigned long)ptr, size)) {
	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 */
		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
	 * Delay this thread, to increase probability of observing a racy
	 * conflicting access.
	 * 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
	 * Re-read value, and check if it is as expected; if not, we infer a
	 * racy access.
	 * racy access.
	 */
	 */
	access_mask = get_ctx()->access_mask;
	access_mask = ctx->access_mask;
	new = 0;
	new = 0;
	switch (size) {
	switch (size) {
	case 1:
	case 1:
@@ -521,8 +527,14 @@ kcsan_setup_watchpoint(const volatile void *ptr, size_t size, int type)
	if (access_mask)
	if (access_mask)
		diff &= 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;
		value_change = KCSAN_VALUE_CHANGE_TRUE;


	/* Check if this access raced with another. */
	/* Check if this access raced with another. */
@@ -644,6 +656,15 @@ void __init kcsan_init(void)
		pr_info("enabled early\n");
		pr_info("enabled early\n");
		WRITE_ONCE(kcsan_enabled, true);
		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 =================================================== */
/* === Exported interface =================================================== */
+32 −0
Original line number Original line Diff line number Diff line
@@ -414,6 +414,14 @@ static noinline void test_kernel_atomic_builtins(void)
	__atomic_load_n(&test_var, __ATOMIC_RELAXED);
	__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 ===== */
/* ===== Test cases ===== */


/* Simple test with normal data race. */
/* Simple test with normal data race. */
@@ -952,6 +960,29 @@ static void test_atomic_builtins(struct kunit *test)
	KUNIT_EXPECT_FALSE(test, match_never);
	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
 * Generate thread counts for all test cases. Values generated are in interval
 * [2, 5] followed by exponentially increasing thread counts from 8 to 32.
 * [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_jiffies_noreport),
	KCSAN_KUNIT_CASE(test_seqlock_noreport),
	KCSAN_KUNIT_CASE(test_seqlock_noreport),
	KCSAN_KUNIT_CASE(test_atomic_builtins),
	KCSAN_KUNIT_CASE(test_atomic_builtins),
	KCSAN_KUNIT_CASE(test_1bit_value_change),
	{},
	{},
};
};


+94 −0
Original line number Original line 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