From 16b21287ee99c4f79d49f1840dddb8cc050abee1 Mon Sep 17 00:00:00 2001
From: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Date: Tue, 23 Jun 2026 14:01:58 -0400
Subject: [PATCH] urcu-pointer: Fix missing memory ordering in
 rcu_dereference_sym()

The non-LGPL rcu_dereference() macro reads the RCU-protected pointer with
a plain (non-atomic, non-volatile) load at the call site and passes the
resulting value to rcu_dereference_sym(). Because the load happens in the
caller, before the library function is entered, two distinct guarantees of
rcu_dereference() are lost:

  - Atomicity of the load. The pointer is read with an ordinary C access
    instead of a volatile/atomic one, so the compiler is free to tear,
    fuse, duplicate, refetch or value-speculate it; the single-access
    (READ_ONCE-style) guarantee is gone. The memory_order_consume load
    inside rcu_dereference_sym() merely operates on a private stack copy
    of the already-loaded value and changes nothing.

  - Ordering. Once the address dependency is broken (value speculation,
    the cmm_ptr_eq() scenario documented in urcu/static/pointer.h, or LTO
    inlining of this trivial wrapper), a weakly-ordered CPU (e.g. arm64)
    can reorder the dependent dereference ahead of the pointer load and
    observe pre-publication data.

This patch is a workaround that addresses the *ordering* aspect only:
issue a cmm_smp_mb() in rcu_dereference_sym() to order the caller's
pointer load (sequenced before the call) before any dependent access
performed after the function returns, regardless of whether the address
dependency survived. The barrier is elided on x86 (URCU_ARCH_X86): TSO
preserves load-to-load and load-to-store ordering, so the pointer load is
already ordered before any dependent access, whether that access is a read
or a write. The only reordering TSO permits is store-to-load, which is
irrelevant here because the pointer access is a load, not a store. The
common TSO case therefore pays no overhead.

It does NOT restore the atomicity of the load, which still happens as a
plain access in the caller; that also remains a data race reported by
ThreadSanitizer. Both are fixed by moving the load into the library
function, done in the following commit.

Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
Change-Id: I8d91474f25e0ed6ab0ccc7e576027b3f75d0a74b
---
 src/urcu-pointer.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/urcu-pointer.c b/src/urcu-pointer.c
index a42fe87be..0b42f20c1 100644
--- a/src/urcu-pointer.c
+++ b/src/urcu-pointer.c
@@ -17,9 +17,33 @@
 
 extern void synchronize_rcu(void);
 
+/*
+ * This wrapper receives the RCU-protected pointer by *value*: the load of
+ * the pointer therefore already happened in the caller as a plain
+ * (non-atomic) access, before this function is even entered. As a result
+ * the compiler may have broken the address dependency between the pointer
+ * load and its later dereference (e.g. through value speculation, or LTO
+ * inlining of this trivial wrapper), which lets a weakly-ordered CPU
+ * reorder the dependent access ahead of the pointer load and observe
+ * pre-publication data, defeating the memory_order_consume guarantee of
+ * rcu_dereference().
+ *
+ * Issue a full barrier to order the caller's pointer load (sequenced
+ * before this call) before any dependent access performed after this
+ * function returns, restoring the publication ordering regardless of
+ * whether the dependency survived. This is unneeded on x86 (TSO): the
+ * hardware preserves load-to-load and load-to-store ordering, so the
+ * pointer load is already ordered before any dependent access, and the
+ * bug cannot manifest. (TSO permits only store-to-load reordering, which
+ * is irrelevant here since the pointer access is a load.) Elide the
+ * barrier there to avoid penalising the common case.
+ */
 void *rcu_dereference_sym(void *p)
 {
-	return _rcu_dereference(p);
+#ifndef URCU_ARCH_X86
+	cmm_smp_mb();
+#endif
+	return p;
 }
 
 void *rcu_set_pointer_sym(void **p, void *v)
