net/ipv6/raw.c | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-)
raw_v6_match() is a lockless match helper under sk_for_each_rcu()
and still reads inet_sk(sk)->inet_num, sk->sk_bound_dev_if,
sk->sk_v6_daddr and sk->sk_v6_rcv_saddr with plain loads.
Add READ_ONCE() annotations for these fields.
Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
---
net/ipv6/raw.c | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index 3cc58698cbbd..7160d5513742 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -64,20 +64,37 @@
struct raw_hashinfo raw_v6_hashinfo;
EXPORT_SYMBOL_GPL(raw_v6_hashinfo);
+static void raw_v6_addr_snapshot(struct in6_addr *dst,
+ const struct in6_addr *src)
+{
+ dst->s6_addr32[0] = READ_ONCE(src->s6_addr32[0]);
+ dst->s6_addr32[1] = READ_ONCE(src->s6_addr32[1]);
+ dst->s6_addr32[2] = READ_ONCE(src->s6_addr32[2]);
+ dst->s6_addr32[3] = READ_ONCE(src->s6_addr32[3]);
+}
+
bool raw_v6_match(struct net *net, const struct sock *sk, unsigned short num,
const struct in6_addr *loc_addr,
const struct in6_addr *rmt_addr, int dif, int sdif)
{
- if (inet_sk(sk)->inet_num != num ||
+ unsigned short match_num = READ_ONCE(inet_sk(sk)->inet_num);
+ int match_bound_dev_if = READ_ONCE(sk->sk_bound_dev_if);
+ struct in6_addr match_daddr;
+ struct in6_addr match_rcv_saddr;
+
+ raw_v6_addr_snapshot(&match_daddr, &sk->sk_v6_daddr);
+ raw_v6_addr_snapshot(&match_rcv_saddr, &sk->sk_v6_rcv_saddr);
+
+ if (match_num != num ||
!net_eq(sock_net(sk), net) ||
- (!ipv6_addr_any(&sk->sk_v6_daddr) &&
- !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) ||
- !raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if,
+ (!ipv6_addr_any(&match_daddr) &&
+ !ipv6_addr_equal(&match_daddr, rmt_addr)) ||
+ !raw_sk_bound_dev_eq(net, match_bound_dev_if,
dif, sdif))
return false;
- if (ipv6_addr_any(&sk->sk_v6_rcv_saddr) ||
- ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr) ||
+ if (ipv6_addr_any(&match_rcv_saddr) ||
+ ipv6_addr_equal(&match_rcv_saddr, loc_addr) ||
(ipv6_addr_is_multicast(loc_addr) &&
inet6_mc_check(sk, loc_addr, rmt_addr)))
return true;
--
2.34.1
On Mon, Jun 1, 2026 at 12:42 AM Runyu Xiao <runyu.xiao@seu.edu.cn> wrote:
>
> raw_v6_match() is a lockless match helper under sk_for_each_rcu()
> and still reads inet_sk(sk)->inet_num, sk->sk_bound_dev_if,
> sk->sk_v6_daddr and sk->sk_v6_rcv_saddr with plain loads.
>
> Add READ_ONCE() annotations for these fields.
>
> Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn>
> ---
> net/ipv6/raw.c | 29 +++++++++++++++++++++++------
> 1 file changed, 23 insertions(+), 6 deletions(-)
>
> diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
> index 3cc58698cbbd..7160d5513742 100644
> --- a/net/ipv6/raw.c
> +++ b/net/ipv6/raw.c
> @@ -64,20 +64,37 @@
> struct raw_hashinfo raw_v6_hashinfo;
> EXPORT_SYMBOL_GPL(raw_v6_hashinfo);
>
> +static void raw_v6_addr_snapshot(struct in6_addr *dst,
> + const struct in6_addr *src)
> +{
> + dst->s6_addr32[0] = READ_ONCE(src->s6_addr32[0]);
> + dst->s6_addr32[1] = READ_ONCE(src->s6_addr32[1]);
> + dst->s6_addr32[2] = READ_ONCE(src->s6_addr32[2]);
> + dst->s6_addr32[3] = READ_ONCE(src->s6_addr32[3]);
> +}
I do not think we want to incur an extra cost for such a minor race.
Note that your patch does not solve the race, since writers do not
update these fields atomically anyway.
© 2016 - 2026 Red Hat, Inc.