From: Gyokhan Kochmarla gyokhan@amazon.com
This patch series backports two commits from mainline to fix CVE-2025-40075, a data race vulnerability in dst->dev access.
The first patch introduces dst->dev_rcu and dst_dev_net_rcu() helper to provide proper RCU protection with lockdep support. The second patch uses the new helper in tcp_metrics to eliminate unsafe dst_dev() calls.
These are clean cherry-picks from mainline commits: - caedcc5b6df1 ("net: dst: introduce dst->dev_rcu") - 50c127a69cd6 ("tcp_metrics: use dst_dev_net_rcu()")
Eric Dumazet (2): net: dst: introduce dst->dev_rcu tcp_metrics: use dst_dev_net_rcu()
include/net/dst.h | 16 +++++++++++----- net/core/dst.c | 2 +- net/ipv4/route.c | 4 ++-- net/ipv4/tcp_metrics.c | 6 +++--- 4 files changed, 17 insertions(+), 11 deletions(-)
From: Eric Dumazet edumazet@google.com
[ Upstream commit caedcc5b6df1b2e2b5f39079e3369c1d4d5c5f50 ]
Followup of commit 88fe14253e1818 ("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()") Cc: stable@vger.kernel.org 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 Stable-dep-of: 50c127a69cd62 ("Replace three dst_dev() with a lockdep enabled helper.") Signed-off-by: Gyokhan Kochmarla gyokhan@amazon.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 e5c9ea188383..e7c1eb69570e 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; @@ -568,9 +571,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) @@ -590,7 +596,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 9a0ddef8bee4..8dbb54148c03 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -150,7 +150,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 e219bb423c3a..7579001d5b29 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1030,7 +1030,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); @@ -1328,7 +1328,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();
From: Eric Dumazet edumazet@google.com
[ Upstream commit 50c127a69cd6285300931853b352a1918cfa180f ]
Replace three dst_dev() with a lockdep enabled helper.
Fixes: 4a6ce2b6f2ec ("net: introduce a new function dst_dev_put()") Cc: stable@vger.kernel.org Signed-off-by: Eric Dumazet edumazet@google.com Reviewed-by: David Ahern dsahern@kernel.org Link: https://patch.msgid.link/20250828195823.3958522-7-edumazet@google.com Signed-off-by: Jakub Kicinski kuba@kernel.org Signed-off-by: Gyokhan Kochmarla gyokhan@amazon.com --- net/ipv4/tcp_metrics.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c index 03c068ea27b6..10e86f1008e9 100644 --- a/net/ipv4/tcp_metrics.c +++ b/net/ipv4/tcp_metrics.c @@ -170,7 +170,7 @@ static struct tcp_metrics_block *tcpm_new(struct dst_entry *dst, struct net *net;
spin_lock_bh(&tcp_metrics_lock); - net = dev_net_rcu(dst_dev(dst)); + net = dst_dev_net_rcu(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_rcu(dst_dev(dst)); + net = dst_dev_net_rcu(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_rcu(dst_dev(dst)); + net = dst_dev_net_rcu(dst); hash ^= net_hash_mix(net); hash = hash_32(hash, tcp_metrics_hash_log);
linux-stable-mirror@lists.linaro.org