Received: by 2002:a05:6a10:206:0:0:0:0 with SMTP id 6csp4810075pxj; Wed, 12 May 2021 13:52:28 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxjNFo3ccwLqpAHI9xA1FcK+6etEDM3vg5kXsMhHvJiB+bW9K+VxyZRh6zQvXGCEeDuDIAu X-Received: by 2002:a17:906:9bc2:: with SMTP id de2mr8799169ejc.340.1620852748400; Wed, 12 May 2021 13:52:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1620852748; cv=none; d=google.com; s=arc-20160816; b=vtHkR3D6IYe2DV+T30qyR6Vohmq8nN/XpDSX3tmILsqAHeQgPW5FP7w2NixfeQJa20 TmrUlfZO1H3K4uAGdp3JHgt7DZegEhydCl7jKLbb+HVALSa0alGmr0N5OL75YfBArzuX 7FrpfXMicST9Wu+kkOg8CVVvwRVZ+6esnsUfniaNFT5EOE6ETQ/oJb6XKo6WIh3fp0eL kDyC1fZsIidtXoWDLAwGBxeavhHmzEyJZ3rZrmPsfLLiwJsTtoi4+hs1VXB049kHnvQ3 LSZ4/rsu93I84HSDRpF0ASDxOOjfXtjMcNvK+flS47FZE+7LKNDjk52LQLiJgPgBX0UE bu+Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:content-transfer-encoding :content-disposition:mime-version:references:reply-to:message-id :subject:cc:to:from:date:dkim-signature; bh=n/33gzjQQ2oiNHshnqGaSMLel5hXt0k4bAmrmlKlzzc=; b=l+meH7wQlpy0c7ThOQR/hNEfzfybtuo8ah7EQqrnjj/DFMU5UPdudvoPrqkejl4rl2 TEzFltzc/3zt94/UVUbuLKYAQ2nhoBWqMLbg0GLjPSivl8sUH8m4lria9nNYf4qrbipg s6MsOfxHIzM5rpjNgDQiA+qoU2IV3G3Yr3sBas3C9sU8qkZ1HNAOO5KCCOWrPQ8T9U7E s2g6LsDpUpHczrwrGlM9XfGEZr+wcjQg1pK3xlRWMhBlP0EStBUhO4p2b5voSiiYafvZ ZM621cYIr+A2yucdmtKwUFbrspbNvoz3s0MPKIQ8wdvYGej7HO0ExDhn6j+bZGgi9+0i 6/0w== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=KRbWatYw; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [23.128.96.18]) by mx.google.com with ESMTP id ke4si921380ejc.239.2021.05.12.13.51.32; Wed, 12 May 2021 13:52:28 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) client-ip=23.128.96.18; Authentication-Results: mx.google.com; dkim=pass header.i=@kernel.org header.s=k20201202 header.b=KRbWatYw; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 23.128.96.18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1344954AbhELUih (ORCPT + 99 others); Wed, 12 May 2021 16:38:37 -0400 Received: from mail.kernel.org ([198.145.29.99]:33320 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1386046AbhELUSy (ORCPT ); Wed, 12 May 2021 16:18:54 -0400 Received: by mail.kernel.org (Postfix) with ESMTPSA id 65D9F60E09; Wed, 12 May 2021 20:17:44 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1620850664; bh=wdILHCP6dNnk3muwmBMWKYZ+siU/Vf+xoO3elurzkaU=; h=Date:From:To:Cc:Subject:Reply-To:References:In-Reply-To:From; b=KRbWatYwaMQU1Zh5mVS29mE31B7Lc3BmSuUo5KCwPTy60v3peG7YHDNGcABHH8Bf8 YJIEQv4BXaui1aHtgYSUao6sX48pvpuDy5auiJgL1T84xaW25WNmOIrJNhiZCnNkbJ qhoD5gTR5u2L7/P5jxThuAc2VMiZK+U8pxn9mNW188hNWes+hWw36/uiKeyCITM07x sNtUew/Yt87WQYJ3gd51Yx4lnx16T/Vw0IZ2Hfth+t30c8aRJQOSl1cTffwTTPzsZl aABDk2ytzZydVhNXy5roIFJPxXle+CDuCW1G/lp6kAXcfG7QxbfJeCF2V0+NKMDG4I iJ7ygr76h6vmA== Received: by paulmck-ThinkPad-P17-Gen-1.home (Postfix, from userid 1000) id E6E7D5C0379; Wed, 12 May 2021 13:17:43 -0700 (PDT) Date: Wed, 12 May 2021 13:17:43 -0700 From: "Paul E. McKenney" To: Manfred Spraul Cc: kasan-dev , Linux Kernel Mailing List , Davidlohr Bueso , 1vier1@web.de Subject: Re: ipc/sem, ipc/msg, ipc/mqueue.c kcsan questions Message-ID: <20210512201743.GW975577@paulmck-ThinkPad-P17-Gen-1> Reply-To: paulmck@kernel.org References: MIME-Version: 1.0 Content-Type: text/plain; charset=iso-8859-1 Content-Disposition: inline Content-Transfer-Encoding: 8bit In-Reply-To: Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Wed, May 12, 2021 at 09:58:18PM +0200, Manfred Spraul wrote: > Hi, > > I got a report from kcsan for sem_lock()/sem_unlock(), but I'm fairly > certain that this is a false positive: > > > [? 184.344960] BUG: KCSAN: data-race in sem_lock / sem_unlock.part.0 > > [? 184.360437] > > [? 184.375443] write to 0xffff8881022fd6c0 of 4 bytes by task 1128 on > > cpu 0: > > [? 184.391192]? sem_unlock.part.0+0xfa/0x118 > 0000000000001371 : > static inline void sem_unlock(struct sem_array *sma, int locknum) > ??? 1464:?????? eb 0f?????????????????? jmp??? 1475 > > ??????????????? sma->use_global_lock--; > ??? 1466:?????? e8 00 00 00 00????????? callq? 146b > ??????????????????????? 1467: R_X86_64_PLT32??? __tsan_write4-0x4 > ??? 146b:?????? 41 ff cc??????????????? dec??? %r12d > > > [? 184.406693]? do_semtimedop+0x690/0xab3 > > [? 184.422032]? __x64_sys_semop+0x3e/0x43 > > [? 184.437180]? do_syscall_64+0x9e/0xb5 > > [? 184.452125]? entry_SYSCALL_64_after_hwframe+0x44/0xae > > [? 184.467269] > > [? 184.482215] read to 0xffff8881022fd6c0 of 4 bytes by task 1129 on cpu > > 2: > > [? 184.497750]? sem_lock+0x59/0xe0 > 0000000000001bbc : > ??????? if (!sma->use_global_lock) { > ??? 1c0a:?????? 4c 89 ef??????????????? mov??? %r13,%rdi > ??????? idx = array_index_nospec(sops->sem_num, sma->sem_nsems); > ??? 1c0d:?????? 0f b7 db??????????????? movzwl %bx,%ebx > ??????? if (!sma->use_global_lock) { > ??? 1c10:?????? e8 00 00 00 00????????? callq? 1c15 > ??????????????????????? 1c11: R_X86_64_PLT32??? __tsan_read4-0x4 > > > [? 184.513121]? do_semtimedop+0x4f6/0xab3 > > [? 184.528427]? __x64_sys_semop+0x3e/0x43 > > [? 184.543540]? do_syscall_64+0x9e/0xb5 > > [? 184.558473]? entry_SYSCALL_64_after_hwframe+0x44/0xae > > > sma->use_global_lock is evaluated in sem_lock() twice: > > > ?????? /* > > ???????? * Initial check for use_global_lock. Just an optimization, > > ???????? * no locking, no memory barrier. > > ???????? */ > > ??????? if (!sma->use_global_lock) { > Both sides of the if-clause handle possible data races. > > Is > > ??? if (!data_race(sma->use_global_lock)) { > > the correct thing to suppress the warning? Most likely READ_ONCE() rather than data_race(), but please see the end of this message. > > ??????????????? /* > > ???????????????? * It appears that no complex operation is around. > > ???????????????? * Acquire the per-semaphore lock. > > ???????????????? */ > > ??????????????? spin_lock(&sem->lock); > > > > ??????????????? /* see SEM_BARRIER_1 for purpose/pairing */ > > ??????????????? if (!smp_load_acquire(&sma->use_global_lock)) { > Here I would need advise: The code only checks for zero / non-zero. The smp_load_acquire() is just fine. > This pairs with complexmode_tryleave(): > > > ??????? if (sma->use_global_lock == 1) { > > > > ??????????????? /* See SEM_BARRIER_1 for purpose/pairing */ > > ??????????????? smp_store_release(&sma->use_global_lock, 0); > > ??????? } else { > > ??????????????? sma->use_global_lock--; > > ??????? } > > If use_global_lock is reduced from e.g. 6 to 5, it is undefined if a > concurrent reader sees 6 or 5. But it doesn't matter, as both values are > non-zero. > > The change to 0 is protected. Again, most likely a READ_ONCE() for sma->use_global_lock, but again please see the end of this message. The key point is that adding (or avoiding) markings is not a mechanical process. > What is the right way to prevent false positives from kcsan? > > As 2nd question: > > net/netfilter/nf_conntrack_core.c, nf_conntrack_all_lock(): > > Is a data_race() needed around "nf_conntrack_locks_all = true;"? Interesting code. The nf_conntrack_all_lock() function acquires nf_conntrack_locks_all_lock, except that the smp_load_acquire() of nf_conntrack_locks_all in nf_conntrack_lock() might be protected by any of a number of locks. In contrast, it appears that the smp_store_release() in nf_conntrack_all_unlock() is always protected by nf_conntrack_locks_all_lock. Is the fact that nf_conntrack_all_lock()'s store can run concurrently with nf_conntrack_lock() smp_load_acquire() intentional? If not, then KCSAN is letting you know of a bug. Otherwise, WRITE_ONCE() might be helpful, but I don't know this code, so that is just a guess. Does tools/memory-model/Documentation/access-marking.txt, shown below, help? Thanx, Paul ------------------------------------------------------------------------ MARKING SHARED-MEMORY ACCESSES ============================== This document provides guidelines for marking intentionally concurrent normal accesses to shared memory, that is "normal" as in accesses that do not use read-modify-write atomic operations. It also describes how to document these accesses, both with comments and with special assertions processed by the Kernel Concurrency Sanitizer (KCSAN). This discussion builds on an earlier LWN article [1]. ACCESS-MARKING OPTIONS ====================== The Linux kernel provides the following access-marking options: 1. Plain C-language accesses (unmarked), for example, "a = b;" 2. Data-race marking, for example, "data_race(a = b);" 3. READ_ONCE(), for example, "a = READ_ONCE(b);" The various forms of atomic_read() also fit in here. 4. WRITE_ONCE(), for example, "WRITE_ONCE(a, b);" The various forms of atomic_set() also fit in here. These may be used in combination, as shown in this admittedly improbable example: WRITE_ONCE(a, b + data_race(c + d) + READ_ONCE(e)); Neither plain C-language accesses nor data_race() (#1 and #2 above) place any sort of constraint on the compiler's choice of optimizations [2]. In contrast, READ_ONCE() and WRITE_ONCE() (#3 and #4 above) restrict the compiler's use of code-motion and common-subexpression optimizations. Therefore, if a given access is involved in an intentional data race, using READ_ONCE() for loads and WRITE_ONCE() for stores is usually preferable to data_race(), which in turn is usually preferable to plain C-language accesses. KCSAN will complain about many types of data races involving plain C-language accesses, but marking all accesses involved in a given data race with one of data_race(), READ_ONCE(), or WRITE_ONCE(), will prevent KCSAN from complaining. Of course, lack of KCSAN complaints does not imply correct code. Therefore, please take a thoughtful approach when responding to KCSAN complaints. Churning the code base with ill-considered additions of data_race(), READ_ONCE(), and WRITE_ONCE() is unhelpful. In fact, the following sections describe situations where use of data_race() and even plain C-language accesses is preferable to READ_ONCE() and WRITE_ONCE(). Use of the data_race() Macro ---------------------------- Here are some situations where data_race() should be used instead of READ_ONCE() and WRITE_ONCE(): 1. Data-racy loads from shared variables whose values are used only for diagnostic purposes. 2. Data-racy reads whose values are checked against marked reload. 3. Reads whose values feed into error-tolerant heuristics. 4. Writes setting values that feed into error-tolerant heuristics. Data-Racy Reads for Approximate Diagnostics Approximate diagnostics include lockdep reports, monitoring/statistics (including /proc and /sys output), WARN*()/BUG*() checks whose return values are ignored, and other situations where reads from shared variables are not an integral part of the core concurrency design. In fact, use of data_race() instead READ_ONCE() for these diagnostic reads can enable better checking of the remaining accesses implementing the core concurrency design. For example, suppose that the core design prevents any non-diagnostic reads from shared variable x from running concurrently with updates to x. Then using plain C-language writes to x allows KCSAN to detect reads from x from within regions of code that fail to exclude the updates. In this case, it is important to use data_race() for the diagnostic reads because otherwise KCSAN would give false-positive warnings about these diagnostic reads. In theory, plain C-language loads can also be used for this use case. However, in practice this will have the disadvantage of causing KCSAN to generate false positives because KCSAN will have no way of knowing that the resulting data race was intentional. Data-Racy Reads That Are Checked Against Marked Reload The values from some reads are not implicitly trusted. They are instead fed into some operation that checks the full value against a later marked load from memory, which means that the occasional arbitrarily bogus value is not a problem. For example, if a bogus value is fed into cmpxchg(), all that happens is that this cmpxchg() fails, which normally results in a retry. Unless the race condition that resulted in the bogus value recurs, this retry will with high probability succeed, so no harm done. However, please keep in mind that a data_race() load feeding into a cmpxchg_relaxed() might still be subject to load fusing on some architectures. Therefore, it is best to capture the return value from the failing cmpxchg() for the next iteration of the loop, an approach that provides the compiler much less scope for mischievous optimizations. Capturing the return value from cmpxchg() also saves a memory reference in many cases. In theory, plain C-language loads can also be used for this use case. However, in practice this will have the disadvantage of causing KCSAN to generate false positives because KCSAN will have no way of knowing that the resulting data race was intentional. Reads Feeding Into Error-Tolerant Heuristics Values from some reads feed into heuristics that can tolerate occasional errors. Such reads can use data_race(), thus allowing KCSAN to focus on the other accesses to the relevant shared variables. But please note that data_race() loads are subject to load fusing, which can result in consistent errors, which in turn are quite capable of breaking heuristics. Therefore use of data_race() should be limited to cases where some other code (such as a barrier() call) will force the occasional reload. In theory, plain C-language loads can also be used for this use case. However, in practice this will have the disadvantage of causing KCSAN to generate false positives because KCSAN will have no way of knowing that the resulting data race was intentional. Writes Setting Values Feeding Into Error-Tolerant Heuristics The values read into error-tolerant heuristics come from somewhere, for example, from sysfs. This means that some code in sysfs writes to this same variable, and these writes can also use data_race(). After all, if the heuristic can tolerate the occasional bogus value due to compiler-mangled reads, it can also tolerate the occasional compiler-mangled write, at least assuming that the proper value is in place once the write completes. Plain C-language stores can also be used for this use case. However, in kernels built with CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n, this will have the disadvantage of causing KCSAN to generate false positives because KCSAN will have no way of knowing that the resulting data race was intentional. Use of Plain C-Language Accesses -------------------------------- Here are some example situations where plain C-language accesses should used instead of READ_ONCE(), WRITE_ONCE(), and data_race(): 1. Accesses protected by mutual exclusion, including strict locking and sequence locking. 2. Initialization-time and cleanup-time accesses. This covers a wide variety of situations, including the uniprocessor phase of system boot, variables to be used by not-yet-spawned kthreads, structures not yet published to reference-counted or RCU-protected data structures, and the cleanup side of any of these situations. 3. Per-CPU variables that are not accessed from other CPUs. 4. Private per-task variables, including on-stack variables, some fields in the task_struct structure, and task-private heap data. 5. Any other loads for which there is not supposed to be a concurrent store to that same variable. 6. Any other stores for which there should be neither concurrent loads nor concurrent stores to that same variable. But note that KCSAN makes two explicit exceptions to this rule by default, refraining from flagging plain C-language stores: a. No matter what. You can override this default by building with CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n. b. When the store writes the value already contained in that variable. You can override this default by building with CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=n. c. When one of the stores is in an interrupt handler and the other in the interrupted code. You can override this default by building with CONFIG_KCSAN_INTERRUPT_WATCHER=y. Note that it is important to use plain C-language accesses in these cases, because doing otherwise prevents KCSAN from detecting violations of your code's synchronization rules. ACCESS-DOCUMENTATION OPTIONS ============================ It is important to comment marked accesses so that people reading your code, yourself included, are reminded of the synchronization design. However, it is even more important to comment plain C-language accesses that are intentionally involved in data races. Such comments are needed to remind people reading your code, again, yourself included, of how the compiler has been prevented from optimizing those accesses into concurrency bugs. It is also possible to tell KCSAN about your synchronization design. For example, ASSERT_EXCLUSIVE_ACCESS(foo) tells KCSAN that any concurrent access to variable foo by any other CPU is an error, even if that concurrent access is marked with READ_ONCE(). In addition, ASSERT_EXCLUSIVE_WRITER(foo) tells KCSAN that although it is OK for there to be concurrent reads from foo from other CPUs, it is an error for some other CPU to be concurrently writing to foo, even if that concurrent write is marked with data_race() or WRITE_ONCE(). Note that although KCSAN will call out data races involving either ASSERT_EXCLUSIVE_ACCESS() or ASSERT_EXCLUSIVE_WRITER() on the one hand and data_race() writes on the other, KCSAN will not report the location of these data_race() writes. EXAMPLES ======== As noted earlier, the goal is to prevent the compiler from destroying your concurrent algorithm, to help the human reader, and to inform KCSAN of aspects of your concurrency design. This section looks at a few examples showing how this can be done. Lock Protection With Lockless Diagnostic Access ----------------------------------------------- For example, suppose a shared variable "foo" is read only while a reader-writer spinlock is read-held, written only while that same spinlock is write-held, except that it is also read locklessly for diagnostic purposes. The code might look as follows: int foo; DEFINE_RWLOCK(foo_rwlock); void update_foo(int newval) { write_lock(&foo_rwlock); foo = newval; do_something(newval); write_unlock(&foo_rwlock); } int read_foo(void) { int ret; read_lock(&foo_rwlock); do_something_else(); ret = foo; read_unlock(&foo_rwlock); return ret; } int read_foo_diagnostic(void) { return data_race(foo); } The reader-writer lock prevents the compiler from introducing concurrency bugs into any part of the main algorithm using foo, which means that the accesses to foo within both update_foo() and read_foo() can (and should) be plain C-language accesses. One benefit of making them be plain C-language accesses is that KCSAN can detect any erroneous lockless reads from or updates to foo. The data_race() in read_foo_diagnostic() tells KCSAN that data races are expected, and should be silently ignored. This data_race() also tells the human reading the code that read_foo_diagnostic() might sometimes return a bogus value. However, please note that your kernel must be built with CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n in order for KCSAN to detect a buggy lockless write. If you need KCSAN to detect such a write even if that write did not change the value of foo, you also need CONFIG_KCSAN_REPORT_VALUE_CHANGE_ONLY=n. If you need KCSAN to detect such a write happening in an interrupt handler running on the same CPU doing the legitimate lock-protected write, you also need CONFIG_KCSAN_INTERRUPT_WATCHER=y. With some or all of these Kconfig options set properly, KCSAN can be quite helpful, although it is not necessarily a full replacement for hardware watchpoints. On the other hand, neither are hardware watchpoints a full replacement for KCSAN because it is not always easy to tell hardware watchpoint to conditionally trap on accesses. Lock-Protected Writes With Lockless Reads ----------------------------------------- For another example, suppose a shared variable "foo" is updated only while holding a spinlock, but is read locklessly. The code might look as follows: int foo; DEFINE_SPINLOCK(foo_lock); void update_foo(int newval) { spin_lock(&foo_lock); WRITE_ONCE(foo, newval); ASSERT_EXCLUSIVE_WRITER(foo); do_something(newval); spin_unlock(&foo_wlock); } int read_foo(void) { do_something_else(); return READ_ONCE(foo); } Because foo is read locklessly, all accesses are marked. The purpose of the ASSERT_EXCLUSIVE_WRITER() is to allow KCSAN to check for a buggy concurrent lockless write. Lockless Reads and Writes ------------------------- For another example, suppose a shared variable "foo" is both read and updated locklessly. The code might look as follows: int foo; int update_foo(int newval) { int ret; ret = xchg(&foo, newval); do_something(newval); return ret; } int read_foo(void) { do_something_else(); return READ_ONCE(foo); } Because foo is accessed locklessly, all accesses are marked. It does not make sense to use ASSERT_EXCLUSIVE_WRITER() in this case because there really can be concurrent lockless writers. KCSAN would flag any concurrent plain C-language reads from foo, and given CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=n, also any concurrent plain C-language writes to foo. Lockless Reads and Writes, But With Single-Threaded Initialization ------------------------------------------------------------------ For yet another example, suppose that foo is initialized in a single-threaded manner, but that a number of kthreads are then created that locklessly and concurrently access foo. Some snippets of this code might look as follows: int foo; void initialize_foo(int initval, int nkthreads) { int i; foo = initval; ASSERT_EXCLUSIVE_ACCESS(foo); for (i = 0; i < nkthreads; i++) kthread_run(access_foo_concurrently, ...); } /* Called from access_foo_concurrently(). */ int update_foo(int newval) { int ret; ret = xchg(&foo, newval); do_something(newval); return ret; } /* Also called from access_foo_concurrently(). */ int read_foo(void) { do_something_else(); return READ_ONCE(foo); } The initialize_foo() uses a plain C-language write to foo because there are not supposed to be concurrent accesses during initialization. The ASSERT_EXCLUSIVE_ACCESS() allows KCSAN to flag buggy concurrent unmarked reads, and the ASSERT_EXCLUSIVE_ACCESS() call further allows KCSAN to flag buggy concurrent writes, even if: (1) Those writes are marked or (2) The kernel was built with CONFIG_KCSAN_ASSUME_PLAIN_WRITES_ATOMIC=y. Checking Stress-Test Race Coverage ---------------------------------- When designing stress tests it is important to ensure that race conditions of interest really do occur. For example, consider the following code fragment: int foo; int update_foo(int newval) { return xchg(&foo, newval); } int xor_shift_foo(int shift, int mask) { int old, new, newold; newold = data_race(foo); /* Checked by cmpxchg(). */ do { old = newold; new = (old << shift) ^ mask; newold = cmpxchg(&foo, old, new); } while (newold != old); return old; } int read_foo(void) { return READ_ONCE(foo); } If it is possible for update_foo(), xor_shift_foo(), and read_foo() to be invoked concurrently, the stress test should force this concurrency to actually happen. KCSAN can evaluate the stress test when the above code is modified to read as follows: int foo; int update_foo(int newval) { ASSERT_EXCLUSIVE_ACCESS(foo); return xchg(&foo, newval); } int xor_shift_foo(int shift, int mask) { int old, new, newold; newold = data_race(foo); /* Checked by cmpxchg(). */ do { old = newold; new = (old << shift) ^ mask; ASSERT_EXCLUSIVE_ACCESS(foo); newold = cmpxchg(&foo, old, new); } while (newold != old); return old; } int read_foo(void) { ASSERT_EXCLUSIVE_ACCESS(foo); return READ_ONCE(foo); } If a given stress-test run does not result in KCSAN complaints from each possible pair of ASSERT_EXCLUSIVE_ACCESS() invocations, the stress test needs improvement. If the stress test was to be evaluated on a regular basis, it would be wise to place the above instances of ASSERT_EXCLUSIVE_ACCESS() under #ifdef so that they did not result in false positives when not evaluating the stress test. REFERENCES ========== [1] "Concurrency bugs should fear the big bad data-race detector (part 2)" https://lwn.net/Articles/816854/ [2] "Who's afraid of a big bad optimizing compiler?" https://lwn.net/Articles/793253/