[PATCH AUTOSEL 6.19-6.12] ipv6: annotate data-races over sysctl.flowlabel_reflect

Sasha Levin
2026-02-14T21:25:32Z

From: Eric Dumazet <edumazet@google.com>

[ Upstream commit 5ade47c974b46eb2a1279185962a0ffa15dc5450 ]

Add missing READ_ONCE() when reading ipv6.sysctl.flowlabel_reflect,
as its value can be changed under us.

Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260115094141.3124990-6-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---

LLM Generated explanations, may be completely bogus:

## Analysis of Commit: "ipv6: annotate data-races over
sysctl.flowlabel_reflect"

### 1. COMMIT MESSAGE ANALYSIS

The commit message is clear and direct: it adds missing `READ_ONCE()`
annotations when reading `ipv6.sysctl.flowlabel_reflect` because this
sysctl value can be changed concurrently by another CPU (via the sysctl
write path). The author is Eric Dumazet, a top-tier networking
maintainer known for systematically fixing data races in the networking
stack. Reviewed by Simon Horman, another well-known networking reviewer.

### 2. CODE CHANGE ANALYSIS

The patch modifies exactly 3 locations across 3 files, adding
`READ_ONCE()` around reads of `net->ipv6.sysctl.flowlabel_reflect`:

1. **net/ipv6/af_inet6.c** (`inet6_create`): Socket creation path —
reads the sysctl to set the `REPFLOW` bit on new IPv6 sockets.

2. **net/ipv6/icmp.c** (`icmpv6_echo_reply`): ICMPv6 echo reply path —
reads the sysctl to decide whether to reflect the flowlabel in echo
replies.

3. **net/ipv6/tcp_ipv6.c** (`tcp_v6_send_reset`): TCP reset sending path
— reads the sysctl to decide whether to reflect the flowlabel in TCP
resets.

In all three cases, the pattern is identical: a plain read of
`net->ipv6.sysctl.flowlabel_reflect` is wrapped with `READ_ONCE()`. This
is a textbook KCSAN data-race annotation fix.

### 3. BUG CLASSIFICATION

This is a **data race fix**. The sysctl `flowlabel_reflect` can be
modified at any time from another CPU via the sysctl interface. Without
`READ_ONCE()`, the compiler is free to:
- Load the value multiple times (potentially seeing different values
within a single check)
- Optimize the read in ways that produce undefined behavior under the C
memory model

This is the exact pattern of KCSAN-detected data races that Eric Dumazet
has been systematically fixing across the networking stack. These are
real data races even if the consequences in practice may be minor (store
tearing or inconsistent reads).

### 4. SCOPE AND RISK ASSESSMENT

- **Size**: Extremely small — 3 locations, each changing a single read
to `READ_ONCE()`.
- **Risk**: Essentially zero. `READ_ONCE()` is a pure annotation that
prevents compiler optimization issues. It cannot introduce new bugs.
- **Files touched**: 3 files in net/ipv6/, all well-established code
paths.
- **Subsystem**: Core IPv6 networking — widely used.

### 5. STABLE KERNEL CRITERIA

- **Obviously correct**: Yes — adding `READ_ONCE()` for a concurrently-
modified sysctl is a textbook fix.
- **Fixes a real bug**: Yes — data races are real bugs, detectable by
KCSAN, and can cause undefined behavior.
- **Small and contained**: Yes — minimal change, 3 lines modified.
- **No new features**: Correct — pure bug fix annotation.
- **Tested**: Reviewed by Simon Horman, authored by Eric Dumazet
(networking co-maintainer).

### 6. USER IMPACT

The `flowlabel_reflect` sysctl affects IPv6 flow label behavior for:
- New socket creation
- ICMPv6 echo replies
- TCP reset packets

While the practical impact of the data race is likely limited (a torn
read of an integer sysctl would just result in a brief wrong decision),
the fix is important for:
- KCSAN cleanliness (preventing false positives that mask real bugs)
- Correctness under the kernel memory model
- Preventing potential compiler-induced bugs on architectures with weak
memory ordering

### 7. DEPENDENCY CHECK

This commit is self-contained. It only adds `READ_ONCE()` wrappers and
has no dependencies on other commits. The code being modified exists in
all recent stable trees.

### 8. RISK vs BENEFIT

- **Risk**: Near zero. `READ_ONCE()` is a safe, well-understood
annotation.
- **Benefit**: Fixes real data races in the IPv6 networking path,
prevents potential compiler-induced misbehavior, enables clean KCSAN
runs.

This is the type of small, surgical, zero-risk fix that is ideal for
stable backporting. Eric Dumazet's data-race annotation series in
networking has been consistently backported to stable trees.

**YES**

net/ipv6/af_inet6.c | 4 ++--
net/ipv6/icmp.c | 3 ++-
net/ipv6/tcp_ipv6.c | 3 ++-
3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index b705751eb73c6..bd29840659f34 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -224,8 +224,8 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
inet6_set_bit(MC6_LOOP, sk);
inet6_set_bit(MC6_ALL, sk);
np->pmtudisc = IPV6_PMTUDISC_WANT;
- inet6_assign_bit(REPFLOW, sk, net->ipv6.sysctl.flowlabel_reflect &
- FLOWLABEL_REFLECT_ESTABLISHED);
+ inet6_assign_bit(REPFLOW, sk, READ_ONCE(net->ipv6.sysctl.flowlabel_reflect) &
+ FLOWLABEL_REFLECT_ESTABLISHED);
sk->sk_ipv6only = net->ipv6.sysctl.bindv6only;
sk->sk_txrehash = READ_ONCE(net->core.sysctl_txrehash);

diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c
index 9d37e7711bc2b..1a25ecb926951 100644
--- a/net/ipv6/icmp.c
+++ b/net/ipv6/icmp.c
@@ -958,7 +958,8 @@ static enum skb_drop_reason icmpv6_echo_reply(struct sk_buff *skb)
tmp_hdr.icmp6_type = type;

memset(&fl6, 0, sizeof(fl6));
- if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES)
+ if (READ_ONCE(net->ipv6.sysctl.flowlabel_reflect) &
+ FLOWLABEL_REFLECT_ICMPV6_ECHO_REPLIES)
fl6.flowlabel = ip6_flowlabel(ipv6_hdr(skb));

fl6.flowi6_proto = IPPROTO_ICMPV6;
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index 280fe59785598..4ae664b05fa91 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -1085,7 +1085,8 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb,
txhash = inet_twsk(sk)->tw_txhash;
}
} else {
- if (net->ipv6.sysctl.flowlabel_reflect & FLOWLABEL_REFLECT_TCP_RESET)
+ if (READ_ONCE(net->ipv6.sysctl.flowlabel_reflect) &
+ FLOWLABEL_REFLECT_TCP_RESET)
label = ip6_flowlabel(ipv6h);
}

--
2.51.0