Re: [PATCH net] ipv6: use READ_ONCE() in ipv6_flowlabel_get()

From: Eric Dumazet

Date: Mon Jun 01 2026 - 08:37:51 EST


On Mon, Jun 1, 2026 at 5:22 AM David Laight
<david.laight.linux@xxxxxxxxx> wrote:
>
> On Sun, 31 May 2026 23:39:46 +0800
> Runyu Xiao <runyu.xiao@xxxxxxxxxx> wrote:
>
> > ipv6_flowlabel_get() still reads the shared per-net sysctl fields
> > flowlabel_consistency and flowlabel_state_ranges with plain loads,
> > while writers update them through proc_dou8vec_minmax(). These checks
> > run in the live IPV6_FLOWLABEL_MGR path, so lockless plain reads leave
> > KCSAN-visible data races and can make the policy checks observe stale or
> > inconsistent values.
> >
> > The race can be reached on a running system by toggling
> > /proc/sys/net/ipv6/flowlabel_consistency and
> > /proc/sys/net/ipv6/flowlabel_state_ranges while another task repeatedly
> > issues IPV6_FLOWLABEL_MGR requests with IPV6_FL_F_REFLECT or a
> > state-ranges flow label.
> >
> > This issue was first flagged by our static analysis tool while scanning
> > lockless IPv6 sysctl readers, then manually audited on Linux v6.18.21.
> > The IPV6_FLOWLABEL_MGR paths were runtime-reproduced with QEMU/KCSAN by
> > concurrently flipping the two sysctls while TCP reflect and UDP
> > state-ranges setsockopt actors exercised ipv6_flowlabel_get(). KCSAN
> > reported races between proc_dou8vec_minmax() and the two plain-load
> > sites in ipv6_flowlabel_get().
> >
> > A narrower second-round UDPv6 + IPV6_AUTOFLOWLABEL send-side reproducer
> > also hit the inline ip6_make_flowlabel() reader through
> > __ip6_make_skb() / proc_dou8vec_minmax(), but that site is already
> > fixed in this tree by commit ded139b59b5d
> > ("ipv6: annotate data-races from ip6_make_flowlabel()"). The remaining
> > plain readers in this tree are both in ipv6_flowlabel_get().
> >
> > Use READ_ONCE() for those remaining sysctl reads so they follow the same
> > lockless reader contract already used by other IPv6 sysctl readers.
> >
> > Build-tested by compiling net/ipv6/ip6_flowlabel.o on x86_64.
> >
> > Representative QEMU/KCSAN reports from the two target reader paths:
> >
> > BUG: KCSAN: data-race in ipv6_flowlabel_opt / proc_dou8vec_minmax
> > write: proc_dou8vec_minmax+0x206/0x220
> > read: ipv6_flowlabel_opt+0x6d8/0xd20
> > do_ipv6_setsockopt+0x873/0x2220
> > tcp_setsockopt+0x72/0xb0
> >
> > BUG: KCSAN: data-race in ipv6_flowlabel_opt / proc_dou8vec_minmax
> > write: proc_dou8vec_minmax+0x206/0x220
> > read: ipv6_flowlabel_opt+0x129/0xd20
> > do_ipv6_setsockopt+0x873/0x2220
> > udpv6_setsockopt+0x21/0x40
> >
> > Fixes: 6444f72b4b74 ("ipv6: add flowlabel_consistency sysctl")
> > Fixes: 82a584b7cd36 ("ipv6: Flow label state ranges")
> > Cc: stable@xxxxxxxxxxxxxxx
> > Signed-off-by: Runyu Xiao <runyu.xiao@xxxxxxxxxx>
> > ---
> > net/ipv6/ip6_flowlabel.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/net/ipv6/ip6_flowlabel.c b/net/ipv6/ip6_flowlabel.c
> > index b1ccdf0dc646..1ab5ad0dcf24 100644
> > --- a/net/ipv6/ip6_flowlabel.c
> > +++ b/net/ipv6/ip6_flowlabel.c
> > @@ -620,7 +620,7 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
> > int err;
> >
> > if (freq->flr_flags & IPV6_FL_F_REFLECT) {
> > - if (net->ipv6.sysctl.flowlabel_consistency) {
> > + if (READ_ONCE(net->ipv6.sysctl.flowlabel_consistency)) {
>
> That can't actually fix anything.

It fixes a KCSAN splat.

If you think you can fix KCSAN instead, please do so.

> If the value can be written concurrently it will still be zero or non-zero
> even if the write gets split.
> So it can only ever be the same as the write happening a bit earlier or
> a bit later.
>
> There might be a real bug if the code looks at
> net->ipv6.sysctl.flowlabel_consistency again.
> But a READ_ONCE() in an if won't fix anything.
>
> > net_info_ratelimited("Can not set IPV6_FL_F_REFLECT if flowlabel_consistency sysctl is enable\n");
> > return -EPERM;
> > }
> > @@ -633,7 +633,7 @@ static int ipv6_flowlabel_get(struct sock *sk, struct in6_flowlabel_req *freq,
> >
> > if (freq->flr_label & ~IPV6_FLOWLABEL_MASK)
> > return -EINVAL;
> > - if (net->ipv6.sysctl.flowlabel_state_ranges &&
> > + if (READ_ONCE(net->ipv6.sysctl.flowlabel_state_ranges) &&
>
> Ditto.
>
> > (freq->flr_label & IPV6_FLOWLABEL_STATELESS_FLAG))
> > return -ERANGE;
> >
>
> -- David
>