[PATCH AUTOSEL 6.19-5.15] ipv6: exthdrs: annotate data-race over multiple sysctl
From: Sasha Levin <sashal@kernel.org>
Date: 2026-02-14 21:25:53
Also in:
linux-patches, stable
Subsystem:
networking [general], networking [ipv4/ipv6], the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Ahern, Ido Schimmel, Linus Torvalds
From: Eric Dumazet <edumazet@google.com> [ Upstream commit 978b67d28358b0b4eacfa94453d1ad4e09b123ad ] Following four sysctls can change under us, add missing READ_ONCE(). - ipv6.sysctl.max_dst_opts_len - ipv6.sysctl.max_dst_opts_cnt - ipv6.sysctl.max_hbh_opts_len - ipv6.sysctl.max_hbh_opts_cnt Signed-off-by: Eric Dumazet <edumazet@google.com> Reviewed-by: Simon Horman <horms@kernel.org> Link: https://patch.msgid.link/20260115094141.3124990-8-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: exthdrs: annotate data-race over multiple sysctl" ### 1. COMMIT MESSAGE ANALYSIS The commit message is straightforward: it adds `READ_ONCE()` annotations to four sysctl variables that can be modified concurrently from userspace while being read in the packet processing path. The author is Eric Dumazet, a prolific and highly respected networking maintainer at Google, who frequently contributes data-race annotations and fixes. Reviewed by Simon Horman, another well-known networking reviewer. The four sysctls affected: - `ipv6.sysctl.max_dst_opts_len` - `ipv6.sysctl.max_dst_opts_cnt` - `ipv6.sysctl.max_hbh_opts_len` - `ipv6.sysctl.max_hbh_opts_cnt` ### 2. CODE CHANGE ANALYSIS The changes are minimal and mechanical - wrapping four sysctl reads with `READ_ONCE()`: 1. **`ipv6_destopt_rcv()`** (line ~317): `net->ipv6.sysctl.max_dst_opts_len` → `READ_ONCE(net->ipv6.sysctl.max_dst_opts_len)` 2. **`ipv6_destopt_rcv()`** (line ~326): `net->ipv6.sysctl.max_dst_opts_cnt` → `READ_ONCE(net->ipv6.sysctl.max_dst_opts_cnt)` 3. **`ipv6_parse_hopopts()`** (line ~1053): `net->ipv6.sysctl.max_hbh_opts_len` → `READ_ONCE(net->ipv6.sysctl.max_hbh_opts_len)` 4. **`ipv6_parse_hopopts()`** (line ~1056): `net->ipv6.sysctl.max_hbh_opts_cnt` → `READ_ONCE(net->ipv6.sysctl.max_hbh_opts_cnt)` These are in the IPv6 extension header packet receive path - hot path code that processes every incoming IPv6 packet with destination options or hop-by-hop options. The sysctl values can be changed from userspace at any time via `/proc/sys/net/ipv6/`, creating a data race. ### 3. BUG MECHANISM Without `READ_ONCE()`, the compiler is free to: - Re-read the value multiple times (store tearing), potentially getting different values in the same function - Optimize based on assumptions about the value not changing This is a real data race detectable by KCSAN (Kernel Concurrency Sanitizer). While the practical consequences of this particular race are relatively mild (the comparison values might be slightly stale or torn), the race is real and in a networking hot path. ### 4. CLASSIFICATION This is a **data-race fix** — category 3 (Race Conditions) from the analysis framework. `READ_ONCE()`/`WRITE_ONCE()` annotations are a common pattern for KCSAN-detected data races and are regularly backported to stable. ### 5. SCOPE AND RISK ASSESSMENT - **Lines changed**: ~8 lines across one file - **Files touched**: 1 (`net/ipv6/exthdrs.c`) - **Risk**: Extremely low. `READ_ONCE()` is a pure compiler annotation that generates identical or near-identical machine code on most architectures. It cannot introduce regressions. - **Subsystem**: IPv6 networking — core infrastructure used by virtually all systems ### 6. USER IMPACT - **Who is affected**: Any system processing IPv6 packets with extension headers where sysctl values might be modified concurrently - **Severity**: Low to medium — the race could theoretically cause inconsistent enforcement of the max length/count limits, but more importantly it silences KCSAN reports and ensures correct compiler behavior - **In the networking hot path**: These functions process packets, so correctness matters ### 7. STABILITY INDICATORS - **Author**: Eric Dumazet (Google, top networking contributor) — very high trust - **Reviewer**: Simon Horman — respected networking reviewer - **Pattern**: This is part of a series (patch 8 of a set) of data-race annotations, which is a well-established pattern in the networking subsystem ### 8. DEPENDENCY CHECK This commit is self-contained. `READ_ONCE()` is a basic kernel primitive available in all stable trees. The sysctl variables being annotated have existed for a long time. No dependencies on other patches. ### 9. VERDICT This is a small, surgical, zero-risk fix for a real data race in the IPv6 networking path. It follows the well-established pattern of `READ_ONCE()` annotations that Eric Dumazet has been systematically adding across the networking stack. These annotations are routinely backported to stable trees. The fix is obviously correct, has essentially zero regression risk, and addresses a real concurrency issue in core networking code. **YES** net/ipv6/exthdrs.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c
index a23eb8734e151..54088fa0c09d0 100644
--- a/net/ipv6/exthdrs.c
+++ b/net/ipv6/exthdrs.c@@ -314,7 +314,7 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) } extlen = (skb_transport_header(skb)[1] + 1) << 3; - if (extlen > net->ipv6.sysctl.max_dst_opts_len) + if (extlen > READ_ONCE(net->ipv6.sysctl.max_dst_opts_len)) goto fail_and_free; opt->lastopt = opt->dst1 = skb_network_header_len(skb);
@@ -322,7 +322,8 @@ static int ipv6_destopt_rcv(struct sk_buff *skb) dstbuf = opt->dst1; #endif - if (ip6_parse_tlv(false, skb, net->ipv6.sysctl.max_dst_opts_cnt)) { + if (ip6_parse_tlv(false, skb, + READ_ONCE(net->ipv6.sysctl.max_dst_opts_cnt))) { skb->transport_header += extlen; opt = IP6CB(skb); #if IS_ENABLED(CONFIG_IPV6_MIP6)
@@ -1049,11 +1050,12 @@ int ipv6_parse_hopopts(struct sk_buff *skb) } extlen = (skb_transport_header(skb)[1] + 1) << 3; - if (extlen > net->ipv6.sysctl.max_hbh_opts_len) + if (extlen > READ_ONCE(net->ipv6.sysctl.max_hbh_opts_len)) goto fail_and_free; opt->flags |= IP6SKB_HOPBYHOP; - if (ip6_parse_tlv(true, skb, net->ipv6.sysctl.max_hbh_opts_cnt)) { + if (ip6_parse_tlv(true, skb, + READ_ONCE(net->ipv6.sysctl.max_hbh_opts_cnt))) { skb->transport_header += extlen; opt = IP6CB(skb); opt->nhoff = sizeof(struct ipv6hdr);
--
2.51.0