* [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu()
@ 2026-08-31 16:06 Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 1/4] net: dst: add four helpers to annotate data-races around dst->dev Miguel Gazquez (Schneider Electric)
` (4 more replies)
0 siblings, 5 replies; 6+ messages in thread
From: Miguel Gazquez (Schneider Electric) @ 2026-08-31 16:06 UTC (permalink / raw)
To: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Martin KaFai Lau, Wei Wang, David Ahern,
Steffen Klassert, Herbert Xu, Pablo Neira Ayuso,
Jozsef Kadlecsik, Florian Westphal
Cc: Thomas Petazzoni, netdev, linux-kernel, netfilter-devel,
coreteam, Miguel Gazquez (Schneider Electric),
Kuniyuki Iwashima
Three other patches are required to be able to apply cleanly the final patch.
Signed-off-by: Miguel Gazquez (Schneider Electric) <miguel.gazquez@bootlin.com>
---
Eric Dumazet (4):
net: dst: add four helpers to annotate data-races around dst->dev
ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu]
net: dst: introduce dst->dev_rcu
ipv4: start using dst_dev_rcu()
include/net/dst.h | 34 ++++++++++++++++++++++++++++++----
include/net/inet_hashtables.h | 2 +-
include/net/ip.h | 11 ++++++-----
include/net/route.h | 2 +-
net/core/dst.c | 4 ++--
net/core/sock.c | 8 ++++----
net/ipv4/icmp.c | 28 +++++++++++++++-------------
net/ipv4/igmp.c | 2 +-
net/ipv4/ip_fragment.c | 6 ++++--
net/ipv4/ip_output.c | 4 ++--
net/ipv4/ip_vti.c | 4 ++--
net/ipv4/ipmr.c | 2 +-
net/ipv4/netfilter.c | 4 ++--
net/ipv4/route.c | 10 +++++-----
net/ipv4/tcp_fastopen.c | 4 +++-
net/ipv4/tcp_ipv4.c | 3 ++-
net/ipv4/tcp_metrics.c | 8 ++++----
net/ipv4/xfrm4_output.c | 2 +-
18 files changed, 86 insertions(+), 52 deletions(-)
---
base-commit: a4a971135a2ff64382ae4235b3ae60503bb1036a
change-id: 20260831-cve-2025-40074-6-6-aaa573b09651
Best regards,
--
Miguel Gazquez (Schneider Electric) <miguel.gazquez@bootlin.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 6.6.y 1/4] net: dst: add four helpers to annotate data-races around dst->dev
2026-08-31 16:06 [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
@ 2026-08-31 16:06 ` Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 2/4] ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu] Miguel Gazquez (Schneider Electric)
` (3 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Miguel Gazquez (Schneider Electric) @ 2026-08-31 16:06 UTC (permalink / raw)
To: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Martin KaFai Lau, Wei Wang, David Ahern,
Steffen Klassert, Herbert Xu, Pablo Neira Ayuso,
Jozsef Kadlecsik, Florian Westphal
Cc: Thomas Petazzoni, netdev, linux-kernel, netfilter-devel,
coreteam, Miguel Gazquez (Schneider Electric),
Kuniyuki Iwashima
From: Eric Dumazet <edumazet@google.com>
[ Upstream commit 88fe14253e181878c2ddb51a298ae8c468a63010 ]
dst->dev is read locklessly in many contexts,
and written in dst_dev_put().
Fixing all the races is going to need many changes.
We probably will have to add full RCU protection.
Add three helpers to ease this painful process.
static inline struct net_device *dst_dev(const struct dst_entry *dst)
{
return READ_ONCE(dst->dev);
}
static inline struct net_device *skb_dst_dev(const struct sk_buff *skb)
{
return dst_dev(skb_dst(skb));
}
static inline struct net *skb_dst_dev_net(const struct sk_buff *skb)
{
return dev_net(skb_dst_dev(skb));
}
static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb)
{
return dev_net_rcu(skb_dst_dev(skb));
}
Fixes: 4a6ce2b6f2ec ("net: introduce a new function dst_dev_put()")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250630121934.3399505-7-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[ minor modifications to fix conflict ]
Signed-off-by: Miguel Gazquez (Schneider Electric) <miguel.gazquez@bootlin.com>
---
include/net/dst.h | 20 ++++++++++++++++++++
net/core/dst.c | 4 ++--
net/core/sock.c | 8 ++++----
3 files changed, 26 insertions(+), 6 deletions(-)
diff --git a/include/net/dst.h b/include/net/dst.h
index 55d1be268d24..1983945dfcdf 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -569,6 +569,11 @@ static inline void skb_dst_update_pmtu_no_confirm(struct sk_buff *skb, u32 mtu)
dst->ops->update_pmtu(dst, NULL, skb, mtu, false);
}
+static inline struct net_device *dst_dev(const struct dst_entry *dst)
+{
+ return READ_ONCE(dst->dev);
+}
+
static inline struct net_device *dst_dev_rcu(const struct dst_entry *dst)
{
/* In the future, use rcu_dereference(dst->dev) */
@@ -576,11 +581,26 @@ static inline struct net_device *dst_dev_rcu(const struct dst_entry *dst)
return READ_ONCE(dst->dev);
}
+static inline struct net_device *skb_dst_dev(const struct sk_buff *skb)
+{
+ return dst_dev(skb_dst(skb));
+}
+
static inline struct net_device *skb_dst_dev_rcu(const struct sk_buff *skb)
{
return dst_dev_rcu(skb_dst(skb));
}
+static inline struct net *skb_dst_dev_net(const struct sk_buff *skb)
+{
+ return dev_net(skb_dst_dev(skb));
+}
+
+static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb)
+{
+ return dev_net_rcu(skb_dst_dev(skb));
+}
+
struct dst_entry *dst_blackhole_check(struct dst_entry *dst, u32 cookie);
void dst_blackhole_update_pmtu(struct dst_entry *dst, struct sock *sk,
struct sk_buff *skb, u32 mtu, bool confirm_neigh);
diff --git a/net/core/dst.c b/net/core/dst.c
index 5ed8cb10748f..f65e6be7e257 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -153,7 +153,7 @@ void dst_dev_put(struct dst_entry *dst)
dst->ops->ifdown(dst, dev);
WRITE_ONCE(dst->input, dst_discard);
WRITE_ONCE(dst->output, dst_discard_out);
- dst->dev = blackhole_netdev;
+ WRITE_ONCE(dst->dev, blackhole_netdev);
netdev_ref_replace(dev, blackhole_netdev, &dst->dev_tracker,
GFP_ATOMIC);
}
@@ -266,7 +266,7 @@ unsigned int dst_blackhole_mtu(const struct dst_entry *dst)
{
unsigned int mtu = dst_metric_raw(dst, RTAX_MTU);
- return mtu ? : dst->dev->mtu;
+ return mtu ? : dst_dev(dst)->mtu;
}
EXPORT_SYMBOL_GPL(dst_blackhole_mtu);
diff --git a/net/core/sock.c b/net/core/sock.c
index 846e95805c19..2f82dc45f93e 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -2454,8 +2454,8 @@ static u32 sk_dst_gso_max_size(struct sock *sk, struct dst_entry *dst)
!ipv6_addr_v4mapped(&sk->sk_v6_rcv_saddr));
#endif
/* pairs with the WRITE_ONCE() in netif_set_gso(_ipv4)_max_size() */
- max_size = is_ipv6 ? READ_ONCE(dst->dev->gso_max_size) :
- READ_ONCE(dst->dev->gso_ipv4_max_size);
+ max_size = is_ipv6 ? READ_ONCE(dst_dev(dst)->gso_max_size) :
+ READ_ONCE(dst_dev(dst)->gso_ipv4_max_size);
if (max_size > GSO_LEGACY_MAX_SIZE && !sk_is_tcp(sk))
max_size = GSO_LEGACY_MAX_SIZE;
@@ -2466,7 +2466,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
{
u32 max_segs = 1;
- sk->sk_route_caps = dst->dev->features;
+ sk->sk_route_caps = dst_dev(dst)->features;
if (sk_is_tcp(sk))
sk->sk_route_caps |= NETIF_F_GSO;
if (sk->sk_route_caps & NETIF_F_GSO)
@@ -2480,7 +2480,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst)
sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
sk->sk_gso_max_size = sk_dst_gso_max_size(sk, dst);
/* pairs with the WRITE_ONCE() in netif_set_gso_max_segs() */
- max_segs = max_t(u32, READ_ONCE(dst->dev->gso_max_segs), 1);
+ max_segs = max_t(u32, READ_ONCE(dst_dev(dst)->gso_max_segs), 1);
}
}
sk->sk_gso_max_segs = max_segs;
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 6.6.y 2/4] ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu]
2026-08-31 16:06 [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 1/4] net: dst: add four helpers to annotate data-races around dst->dev Miguel Gazquez (Schneider Electric)
@ 2026-08-31 16:06 ` Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 3/4] net: dst: introduce dst->dev_rcu Miguel Gazquez (Schneider Electric)
` (2 subsequent siblings)
4 siblings, 0 replies; 6+ messages in thread
From: Miguel Gazquez (Schneider Electric) @ 2026-08-31 16:06 UTC (permalink / raw)
To: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Martin KaFai Lau, Wei Wang, David Ahern,
Steffen Klassert, Herbert Xu, Pablo Neira Ayuso,
Jozsef Kadlecsik, Florian Westphal
Cc: Thomas Petazzoni, netdev, linux-kernel, netfilter-devel,
coreteam, Miguel Gazquez (Schneider Electric),
Kuniyuki Iwashima
From: Eric Dumazet <edumazet@google.com>
[ Upstream commit a74fc62eec155ca5a6da8ff3856f3dc87fe24558 ]
Use the new helpers as a first step to deal with
potential dst->dev races.
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Link: https://patch.msgid.link/20250630121934.3399505-8-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[ minor modifications to fix conflict ]
Signed-off-by: Miguel Gazquez (Schneider Electric) <miguel.gazquez@bootlin.com>
---
include/net/inet_hashtables.h | 2 +-
include/net/ip.h | 11 ++++++-----
include/net/route.h | 2 +-
net/ipv4/icmp.c | 24 +++++++++++++-----------
net/ipv4/igmp.c | 2 +-
net/ipv4/ip_fragment.c | 2 +-
net/ipv4/ip_output.c | 4 ++--
net/ipv4/ip_vti.c | 4 ++--
net/ipv4/netfilter.c | 4 ++--
net/ipv4/route.c | 8 ++++----
net/ipv4/tcp_fastopen.c | 4 +++-
net/ipv4/tcp_ipv4.c | 3 ++-
net/ipv4/tcp_metrics.c | 8 ++++----
net/ipv4/xfrm4_output.c | 2 +-
14 files changed, 43 insertions(+), 37 deletions(-)
diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h
index 8eeb3708f20b..21119e962f00 100644
--- a/include/net/inet_hashtables.h
+++ b/include/net/inet_hashtables.h
@@ -511,7 +511,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo,
const int sdif,
bool *refcounted)
{
- struct net *net = dev_net(skb_dst(skb)->dev);
+ struct net *net = skb_dst_dev_net(skb);
const struct iphdr *iph = ip_hdr(skb);
struct sock *sk;
diff --git a/include/net/ip.h b/include/net/ip.h
index bacdb4fecc89..f66dd581ba83 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -458,7 +458,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
bool forwarding)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net *net = dev_net(dst->dev);
+ struct net *net = dev_net(dst_dev(dst));
unsigned int mtu;
if (READ_ONCE(net->ipv4.sysctl_ip_fwd_use_pmtu) ||
@@ -474,7 +474,7 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
if (mtu)
goto out;
- mtu = READ_ONCE(dst->dev->mtu);
+ mtu = READ_ONCE(dst_dev(dst)->mtu);
if (unlikely(ip_mtu_locked(dst))) {
if (rt->rt_uses_gateway && mtu > 576)
@@ -490,16 +490,17 @@ static inline unsigned int ip_dst_mtu_maybe_forward(const struct dst_entry *dst,
static inline unsigned int ip_skb_dst_mtu(struct sock *sk,
const struct sk_buff *skb)
{
+ const struct dst_entry *dst = skb_dst(skb);
unsigned int mtu;
if (!sk || !sk_fullsock(sk) || ip_sk_use_pmtu(sk)) {
bool forwarding = IPCB(skb)->flags & IPSKB_FORWARDED;
- return ip_dst_mtu_maybe_forward(skb_dst(skb), forwarding);
+ return ip_dst_mtu_maybe_forward(dst, forwarding);
}
- mtu = min(READ_ONCE(skb_dst(skb)->dev->mtu), IP_MAX_MTU);
- return mtu - lwtunnel_headroom(skb_dst(skb)->lwtstate, mtu);
+ mtu = min(READ_ONCE(dst_dev(dst)->mtu), IP_MAX_MTU);
+ return mtu - lwtunnel_headroom(dst->lwtstate, mtu);
}
struct dst_metrics *ip_fib_metrics_init(struct nlattr *fc_mx, int fc_mx_len,
diff --git a/include/net/route.h b/include/net/route.h
index 1d5d975c1707..d443de683628 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -359,7 +359,7 @@ static inline int ip4_dst_hoplimit(const struct dst_entry *dst)
const struct net *net;
rcu_read_lock();
- net = dev_net_rcu(dst->dev);
+ net = dev_net_rcu(dst_dev(dst));
hoplimit = READ_ONCE(net->ipv4.sysctl_ip_default_ttl);
rcu_read_unlock();
}
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index 7c4c7b7fe384..f0c8c3366740 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -313,18 +313,20 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
{
struct dst_entry *dst = &rt->dst;
struct inet_peer *peer;
+ struct net_device *dev;
bool rc = true;
if (!apply_ratelimit)
return true;
/* No rate limit on loopback */
- if (dst->dev && (dst->dev->flags&IFF_LOOPBACK))
+ dev = dst_dev(dst);
+ if (dev && (dev->flags & IFF_LOOPBACK))
goto out;
rcu_read_lock();
peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr,
- l3mdev_master_ifindex_rcu(dst->dev));
+ l3mdev_master_ifindex_rcu(dev));
rc = inet_peer_xrlim_allow(peer,
READ_ONCE(net->ipv4.sysctl_icmp_ratelimit));
rcu_read_unlock();
@@ -472,13 +474,13 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb)
*/
static struct net_device *icmp_get_route_lookup_dev(struct sk_buff *skb)
{
- struct net_device *route_lookup_dev = NULL;
+ struct net_device *dev = skb->dev;
+ const struct dst_entry *dst;
- if (skb->dev)
- route_lookup_dev = skb->dev;
- else if (skb_dst(skb))
- route_lookup_dev = skb_dst(skb)->dev;
- return route_lookup_dev;
+ if (dev)
+ return dev;
+ dst = skb_dst(skb);
+ return dst ? dst_dev(dst) : NULL;
}
static struct rtable *icmp_route_lookup(struct net *net, struct flowi4 *fl4,
@@ -911,7 +913,7 @@ static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
struct net *net;
u32 info = 0;
- net = dev_net_rcu(skb_dst(skb)->dev);
+ net = skb_dst_dev_net_rcu(skb);
/*
* Incomplete header ?
@@ -1054,7 +1056,7 @@ static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
struct icmp_bxm icmp_param;
struct net *net;
- net = dev_net_rcu(skb_dst(skb)->dev);
+ net = skb_dst_dev_net_rcu(skb);
/* should there be an ICMP stat for ignored echos? */
if (READ_ONCE(net->ipv4.sysctl_icmp_echo_ignore_all))
return SKB_NOT_DROPPED_YET;
@@ -1231,7 +1233,7 @@ static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
return SKB_NOT_DROPPED_YET;
out_err:
- __ICMP_INC_STATS(dev_net_rcu(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
+ __ICMP_INC_STATS(skb_dst_dev_net_rcu(skb), ICMP_MIB_INERRORS);
return SKB_DROP_REASON_PKT_TOO_SMALL;
}
diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c
index 732804f3a0aa..8509e9473834 100644
--- a/net/ipv4/igmp.c
+++ b/net/ipv4/igmp.c
@@ -428,7 +428,7 @@ static int igmpv3_sendpack(struct sk_buff *skb)
pig->csum = ip_compute_csum(igmp_hdr(skb), igmplen);
- return ip_local_out(dev_net(skb_dst(skb)->dev), skb->sk, skb);
+ return ip_local_out(skb_dst_dev_net(skb), skb->sk, skb);
}
static int grec_size(struct ip_mc_list *pmc, int type, int gdel, int sdel)
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 8524f2ac2cf3..46b84d029205 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -470,7 +470,7 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
/* Process an incoming IP datagram fragment. */
int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
{
- struct net_device *dev = skb->dev ? : skb_dst(skb)->dev;
+ struct net_device *dev = skb->dev ? : skb_dst_dev(skb);
int vif = l3mdev_master_ifindex_rcu(dev);
struct ipq *qp;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index 150cdea8aacb..3bb198b2c101 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -116,7 +116,7 @@ int __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb)
skb->protocol = htons(ETH_P_IP);
return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,
- net, sk, skb, NULL, skb_dst(skb)->dev,
+ net, sk, skb, NULL, skb_dst_dev(skb),
dst_output);
}
@@ -199,7 +199,7 @@ static int ip_finish_output2(struct net *net, struct sock *sk, struct sk_buff *s
{
struct dst_entry *dst = skb_dst(skb);
struct rtable *rt = (struct rtable *)dst;
- struct net_device *dev = dst->dev;
+ struct net_device *dev = dst_dev(dst);
unsigned int hh_len = LL_RESERVED_SPACE(dev);
struct neighbour *neigh;
bool is_v6gw = false;
diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c
index 02744447e435..cd61a417c44d 100644
--- a/net/ipv4/ip_vti.c
+++ b/net/ipv4/ip_vti.c
@@ -226,7 +226,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
goto tx_error_icmp;
}
- tdev = dst->dev;
+ tdev = dst_dev(dst);
if (tdev == dev) {
dst_release(dst);
@@ -256,7 +256,7 @@ static netdev_tx_t vti_xmit(struct sk_buff *skb, struct net_device *dev,
xmit:
skb_scrub_packet(skb, !net_eq(tunnel->net, dev_net(dev)));
skb_dst_set(skb, dst);
- skb->dev = skb_dst(skb)->dev;
+ skb->dev = skb_dst_dev(skb);
err = dst_output(tunnel->net, skb->sk, skb);
if (net_xmit_eval(err) == 0)
diff --git a/net/ipv4/netfilter.c b/net/ipv4/netfilter.c
index bd135165482a..c450509d6efd 100644
--- a/net/ipv4/netfilter.c
+++ b/net/ipv4/netfilter.c
@@ -19,12 +19,12 @@
/* route_me_harder function, used by iptable_nat, iptable_mangle + ip_queue */
int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, unsigned int addr_type)
{
+ struct net_device *dev = skb_dst_dev(skb);
const struct iphdr *iph = ip_hdr(skb);
struct rtable *rt;
struct flowi4 fl4 = {};
__be32 saddr = iph->saddr;
__u8 flags;
- struct net_device *dev = skb_dst(skb)->dev;
struct flow_keys flkeys;
unsigned int hh_len;
@@ -73,7 +73,7 @@ int ip_route_me_harder(struct net *net, struct sock *sk, struct sk_buff *skb, un
#endif
/* Change in oif may mean change in hh_len. */
- hh_len = skb_dst(skb)->dev->hard_header_len;
+ hh_len = skb_dst_dev(skb)->hard_header_len;
if (skb_headroom(skb) < hh_len &&
pskb_expand_head(skb, HH_DATA_ALIGN(hh_len - skb_headroom(skb)),
0, GFP_ATOMIC))
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 789953096c80..5010a9a75418 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -416,7 +416,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
const void *daddr)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net_device *dev = dst->dev;
+ struct net_device *dev = dst_dev(dst);
struct neighbour *n;
rcu_read_lock();
@@ -443,7 +443,7 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
static void ipv4_confirm_neigh(const struct dst_entry *dst, const void *daddr)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net_device *dev = dst->dev;
+ struct net_device *dev = dst_dev(dst);
const __be32 *pkey = daddr;
if (rt->rt_gw_family == AF_INET) {
@@ -1069,7 +1069,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
return;
rcu_read_lock();
- net = dev_net_rcu(dst->dev);
+ net = dev_net_rcu(dst_dev(dst));
if (mtu < net->ipv4.ip_rt_min_pmtu) {
lock = true;
mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu);
@@ -1367,7 +1367,7 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
struct net *net;
rcu_read_lock();
- net = dev_net_rcu(dst->dev);
+ net = dev_net_rcu(dst_dev(dst));
advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
net->ipv4.ip_rt_min_advmss);
rcu_read_unlock();
diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c
index 408985eb74ee..86c995dc1c5e 100644
--- a/net/ipv4/tcp_fastopen.c
+++ b/net/ipv4/tcp_fastopen.c
@@ -558,6 +558,7 @@ bool tcp_fastopen_active_should_disable(struct sock *sk)
void tcp_fastopen_active_disable_ofo_check(struct sock *sk)
{
struct tcp_sock *tp = tcp_sk(sk);
+ struct net_device *dev;
struct dst_entry *dst;
struct sk_buff *skb;
@@ -575,7 +576,8 @@ void tcp_fastopen_active_disable_ofo_check(struct sock *sk)
} else if (tp->syn_fastopen_ch &&
atomic_read(&sock_net(sk)->ipv4.tfo_active_disable_times)) {
dst = sk_dst_get(sk);
- if (!(dst && dst->dev && (dst->dev->flags & IFF_LOOPBACK)))
+ dev = dst ? dst_dev(dst) : NULL;
+ if (!(dev && (dev->flags & IFF_LOOPBACK)))
atomic_set(&sock_net(sk)->ipv4.tfo_active_disable_times, 0);
dst_release(dst);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 3f9e1cfde008..3055478860c1 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -732,7 +732,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb)
arg.iov[0].iov_base = (unsigned char *)&rep;
arg.iov[0].iov_len = sizeof(rep.th);
- net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
+ net = sk ? sock_net(sk) : skb_dst_dev_net(skb);
+
#ifdef CONFIG_TCP_MD5SIG
rcu_read_lock();
hash_location = tcp_parse_md5sig_option(th);
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index e0883ba709b0..49983d864e4f 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -166,11 +166,11 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst,
unsigned int hash)
{
struct tcp_metrics_block *tm;
- struct net *net;
bool reclaim = false;
+ struct net *net;
spin_lock_bh(&tcp_metrics_lock);
- net = dev_net(dst->dev);
+ net = dev_net(dst_dev(dst));
/* While waiting for the spin-lock the cache might have been populated
* with this entry and so we have to check again.
@@ -273,7 +273,7 @@ static struct tcp_metrics_block *__tcp_get_metrics_req(struct request_sock *req,
return NULL;
}
- net = dev_net(dst->dev);
+ net = dev_net(dst_dev(dst));
hash ^= net_hash_mix(net);
hash = hash_32(hash, tcp_metrics_hash_log);
@@ -318,7 +318,7 @@ static struct tcp_metrics_block *tcp_get_metrics(struct sock *sk,
else
return NULL;
- net = dev_net(dst->dev);
+ net = dev_net(dst_dev(dst));
hash ^= net_hash_mix(net);
hash = hash_32(hash, tcp_metrics_hash_log);
diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 3cff51ba72bb..0ae67d537499 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -31,7 +31,7 @@ static int __xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)
int xfrm4_output(struct net *net, struct sock *sk, struct sk_buff *skb)
{
return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING,
- net, sk, skb, skb->dev, skb_dst(skb)->dev,
+ net, sk, skb, skb->dev, skb_dst_dev(skb),
__xfrm4_output,
!(IPCB(skb)->flags & IPSKB_REROUTED));
}
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 6.6.y 3/4] net: dst: introduce dst->dev_rcu
2026-08-31 16:06 [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 1/4] net: dst: add four helpers to annotate data-races around dst->dev Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 2/4] ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu] Miguel Gazquez (Schneider Electric)
@ 2026-08-31 16:06 ` Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 4/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
2026-09-01 1:21 ` [PATCH 6.6.y 0/4] " Sasha Levin
4 siblings, 0 replies; 6+ messages in thread
From: Miguel Gazquez (Schneider Electric) @ 2026-08-31 16:06 UTC (permalink / raw)
To: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Martin KaFai Lau, Wei Wang, David Ahern,
Steffen Klassert, Herbert Xu, Pablo Neira Ayuso,
Jozsef Kadlecsik, Florian Westphal
Cc: Thomas Petazzoni, netdev, linux-kernel, netfilter-devel,
coreteam, Miguel Gazquez (Schneider Electric)
From: Eric Dumazet <edumazet@google.com>
[ Upstream commit caedcc5b6df1b2e2b5f39079e3369c1d4d5c5f50 ]
Followup of commit 88fe14253e18 ("net: dst: add four helpers
to annotate data-races around dst->dev").
We want to gradually add explicit RCU protection to dst->dev,
including lockdep support.
Add an union to alias dst->dev_rcu and dst->dev.
Add dst_dev_net_rcu() helper.
Fixes: 4a6ce2b6f2ec ("net: introduce a new function dst_dev_put()")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://patch.msgid.link/20250828195823.3958522-2-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Miguel Gazquez (Schneider Electric) <miguel.gazquez@bootlin.com>
---
include/net/dst.h | 16 +++++++++++-----
net/core/dst.c | 2 +-
net/ipv4/route.c | 4 ++--
3 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/include/net/dst.h b/include/net/dst.h
index 1983945dfcdf..4b54b2f6730e 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -24,7 +24,10 @@
struct sk_buff;
struct dst_entry {
- struct net_device *dev;
+ union {
+ struct net_device *dev;
+ struct net_device __rcu *dev_rcu;
+ };
struct dst_ops *ops;
unsigned long _metrics;
unsigned long expires;
@@ -576,9 +579,12 @@ static inline struct net_device *dst_dev(const struct dst_entry *dst)
static inline struct net_device *dst_dev_rcu(const struct dst_entry *dst)
{
- /* In the future, use rcu_dereference(dst->dev) */
- WARN_ON_ONCE(!rcu_read_lock_held());
- return READ_ONCE(dst->dev);
+ return rcu_dereference(dst->dev_rcu);
+}
+
+static inline struct net *dst_dev_net_rcu(const struct dst_entry *dst)
+{
+ return dev_net_rcu(dst_dev_rcu(dst));
}
static inline struct net_device *skb_dst_dev(const struct sk_buff *skb)
@@ -598,7 +604,7 @@ static inline struct net *skb_dst_dev_net(const struct sk_buff *skb)
static inline struct net *skb_dst_dev_net_rcu(const struct sk_buff *skb)
{
- return dev_net_rcu(skb_dst_dev(skb));
+ return dev_net_rcu(skb_dst_dev_rcu(skb));
}
struct dst_entry *dst_blackhole_check(struct dst_entry *dst, u32 cookie);
diff --git a/net/core/dst.c b/net/core/dst.c
index f65e6be7e257..46cafba29991 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -153,7 +153,7 @@ void dst_dev_put(struct dst_entry *dst)
dst->ops->ifdown(dst, dev);
WRITE_ONCE(dst->input, dst_discard);
WRITE_ONCE(dst->output, dst_discard_out);
- WRITE_ONCE(dst->dev, blackhole_netdev);
+ rcu_assign_pointer(dst->dev_rcu, blackhole_netdev);
netdev_ref_replace(dev, blackhole_netdev, &dst->dev_tracker,
GFP_ATOMIC);
}
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 5010a9a75418..ce3397583b8f 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -1069,7 +1069,7 @@ static void __ip_rt_update_pmtu(struct rtable *rt, struct flowi4 *fl4, u32 mtu)
return;
rcu_read_lock();
- net = dev_net_rcu(dst_dev(dst));
+ net = dst_dev_net_rcu(dst);
if (mtu < net->ipv4.ip_rt_min_pmtu) {
lock = true;
mtu = min(old_mtu, net->ipv4.ip_rt_min_pmtu);
@@ -1367,7 +1367,7 @@ static unsigned int ipv4_default_advmss(const struct dst_entry *dst)
struct net *net;
rcu_read_lock();
- net = dev_net_rcu(dst_dev(dst));
+ net = dst_dev_net_rcu(dst);
advmss = max_t(unsigned int, ipv4_mtu(dst) - header_size,
net->ipv4.ip_rt_min_advmss);
rcu_read_unlock();
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 6.6.y 4/4] ipv4: start using dst_dev_rcu()
2026-08-31 16:06 [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
` (2 preceding siblings ...)
2026-08-31 16:06 ` [PATCH 6.6.y 3/4] net: dst: introduce dst->dev_rcu Miguel Gazquez (Schneider Electric)
@ 2026-08-31 16:06 ` Miguel Gazquez (Schneider Electric)
2026-09-01 1:21 ` [PATCH 6.6.y 0/4] " Sasha Levin
4 siblings, 0 replies; 6+ messages in thread
From: Miguel Gazquez (Schneider Electric) @ 2026-08-31 16:06 UTC (permalink / raw)
To: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Martin KaFai Lau, Wei Wang, David Ahern,
Steffen Klassert, Herbert Xu, Pablo Neira Ayuso,
Jozsef Kadlecsik, Florian Westphal
Cc: Thomas Petazzoni, netdev, linux-kernel, netfilter-devel,
coreteam, Miguel Gazquez (Schneider Electric)
From: Eric Dumazet <edumazet@google.com>
[ Upstream commit 6ad8de3cefdb6ffa6708b21c567df0dbf82c43a8 ]
Change icmpv4_xrlim_allow(), ip_defrag() to prevent possible UAF.
Change ipmr_prepare_xmit(), ipmr_queue_fwd_xmit(), ip_mr_output(),
ipv4_neigh_lookup() to use lockdep enabled dst_dev_rcu().
Fixes: 4a6ce2b6f2ec ("net: introduce a new function dst_dev_put()")
Signed-off-by: Eric Dumazet <edumazet@google.com>
Reviewed-by: David Ahern <dsahern@kernel.org>
Link: https://patch.msgid.link/20250828195823.3958522-9-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
[ minor modifications to fix conflict ]
Signed-off-by: Miguel Gazquez (Schneider Electric) <miguel.gazquez@bootlin.com>
---
net/ipv4/icmp.c | 6 +++---
net/ipv4/ip_fragment.c | 6 ++++--
net/ipv4/ipmr.c | 2 +-
net/ipv4/route.c | 4 ++--
4 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c
index f0c8c3366740..5c38af80bc14 100644
--- a/net/ipv4/icmp.c
+++ b/net/ipv4/icmp.c
@@ -320,17 +320,17 @@ static bool icmpv4_xrlim_allow(struct net *net, struct rtable *rt,
return true;
/* No rate limit on loopback */
- dev = dst_dev(dst);
+ rcu_read_lock();
+ dev = dst_dev_rcu(dst);
if (dev && (dev->flags & IFF_LOOPBACK))
goto out;
- rcu_read_lock();
peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr,
l3mdev_master_ifindex_rcu(dev));
rc = inet_peer_xrlim_allow(peer,
READ_ONCE(net->ipv4.sysctl_icmp_ratelimit));
- rcu_read_unlock();
out:
+ rcu_read_unlock();
if (!rc)
__ICMP_INC_STATS(net, ICMP_MIB_RATELIMITHOST);
else
diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c
index 46b84d029205..e79ebe911548 100644
--- a/net/ipv4/ip_fragment.c
+++ b/net/ipv4/ip_fragment.c
@@ -470,14 +470,16 @@ static int ip_frag_reasm(struct ipq *qp, struct sk_buff *skb,
/* Process an incoming IP datagram fragment. */
int ip_defrag(struct net *net, struct sk_buff *skb, u32 user)
{
- struct net_device *dev = skb->dev ? : skb_dst_dev(skb);
- int vif = l3mdev_master_ifindex_rcu(dev);
+ struct net_device *dev;
struct ipq *qp;
+ int vif;
__IP_INC_STATS(net, IPSTATS_MIB_REASMREQDS);
/* Lookup (or create) queue header */
rcu_read_lock();
+ dev = skb->dev ? : skb_dst_dev_rcu(skb);
+ vif = l3mdev_master_ifindex_rcu(dev);
qp = ip_find(net, ip_hdr(skb), user, vif);
if (qp) {
int ret, refs = 0;
diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
index af9412a507cf..f39cd565569a 100644
--- a/net/ipv4/ipmr.c
+++ b/net/ipv4/ipmr.c
@@ -1893,7 +1893,7 @@ static void ipmr_queue_xmit(struct net *net, struct mr_table *mrt,
goto out_free;
}
- dev = rt->dst.dev;
+ dev = dst_dev_rcu(&rt->dst);
if (skb->len+encap > dst_mtu(&rt->dst) && (ntohs(iph->frag_off) & IP_DF)) {
/* Do not fragment multicasts. Alas, IPv4 does not
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index ce3397583b8f..b175a8292eb3 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -416,11 +416,11 @@ static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
const void *daddr)
{
const struct rtable *rt = container_of(dst, struct rtable, dst);
- struct net_device *dev = dst_dev(dst);
+ struct net_device *dev;
struct neighbour *n;
rcu_read_lock();
-
+ dev = dst_dev_rcu(dst);
if (likely(rt->rt_gw_family == AF_INET)) {
n = ip_neigh_gw4(dev, rt->rt_gw4);
} else if (rt->rt_gw_family == AF_INET6) {
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu()
2026-08-31 16:06 [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
` (3 preceding siblings ...)
2026-08-31 16:06 ` [PATCH 6.6.y 4/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
@ 2026-09-01 1:21 ` Sasha Levin
4 siblings, 0 replies; 6+ messages in thread
From: Sasha Levin @ 2026-09-01 1:21 UTC (permalink / raw)
To: stable, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Martin KaFai Lau, Wei Wang, David Ahern,
Steffen Klassert, Herbert Xu, Pablo Neira Ayuso,
Jozsef Kadlecsik, Florian Westphal
Cc: Sasha Levin, Thomas Petazzoni, netdev, linux-kernel,
netfilter-devel, coreteam, Miguel Gazquez (Schneider Electric),
Kuniyuki Iwashima
> Three other patches are required to be able to apply cleanly the final patch.
>
> Eric Dumazet (4):
> net: dst: add four helpers to annotate data-races around dst->dev
> ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu]
> net: dst: introduce dst->dev_rcu
> ipv4: start using dst_dev_rcu()
Queued the series for 6.6, thanks.
--
Thanks,
Sasha
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-01 1:22 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-31 16:06 [PATCH 6.6.y 0/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 1/4] net: dst: add four helpers to annotate data-races around dst->dev Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 2/4] ipv4: adopt dst_dev, skb_dst_dev and skb_dst_dev_net[_rcu] Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 3/4] net: dst: introduce dst->dev_rcu Miguel Gazquez (Schneider Electric)
2026-08-31 16:06 ` [PATCH 6.6.y 4/4] ipv4: start using dst_dev_rcu() Miguel Gazquez (Schneider Electric)
2026-09-01 1:21 ` [PATCH 6.6.y 0/4] " Sasha Levin
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®