[PATCH RFC 08/10] rcu-tasks: Fix IRQ read lock/unlock data race

"Paul E. McKenney" <[email protected]> Thu, 30 Jul 2026 18:03:59 -0700
Newsgroups org.kernel.vger.rcu,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
As noted by Marco Elver:

rcu_read_lock_trace()
   ....
t->trc_reader_scp = __srcu_read_lock_fast(&rcu_tasks_trace_srcu_struct);
	<interrupt>
					rcu_read_unlock_trace()
					< ... var decls only ... >
					scp = t->trc_reader_scp;

This constitutes a data race between these two accesses to
t->trc_reader_scp.  If rcu_read_lock_trace() were to tear its store,
this value would be corrupted.

This commit therefore defers the rcu_read_lock_untrace() function's
load from t->trc_reader_scp until after it has verified that this is
the outermost rcu_read_unlock_trace().  With this change, the interrupt
handler increments and decrements t->trc_reader_nesting and does not
access t->trc_reader_scp, thus avoiding the data race.

KCSAN located this issue.

Signed-off-by: Paul E. McKenney <[email protected]>
---
 include/linux/rcupdate_trace.h | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/include/linux/rcupdate_trace.h b/include/linux/rcupdate_trace.h
index fd3ddeb6aa3bd2..70decf877348a6 100644
--- a/include/linux/rcupdate_trace.h
+++ b/include/linux/rcupdate_trace.h
@@ -126,11 +126,13 @@ static inline void rcu_read_unlock_trace(void)
 	struct srcu_ctr __percpu *scp;
 	struct task_struct *t = current;
 
-	scp = t->trc_reader_scp;
-	barrier();  // scp before nesting to protect against interrupt handler.
 	n = READ_ONCE(t->trc_reader_nesting) - 1;
-	WRITE_ONCE(t->trc_reader_nesting, n);
-	if (!n) {
+	if (n) {
+		WRITE_ONCE(t->trc_reader_nesting, n);
+	} else {
+		scp = t->trc_reader_scp; // Compiler cannot hoist load due to data raciness.
+		barrier();  // scp before nesting to protect against interrupt handler.
+		WRITE_ONCE(t->trc_reader_nesting, n);
 		if (!IS_ENABLED(CONFIG_TASKS_TRACE_RCU_NO_MB))
 			smp_mb(); // Placeholder for more selective ordering
 		__srcu_read_unlock_fast(&rcu_tasks_trace_srcu_struct, scp);
-- 
2.40.1