From: Eric Dumazet <edumazet@google.com> mainline inclusion from mainline-v6.18-rc1 commit 6ad8de3cefdb6ffa6708b21c567df0dbf82c43a8 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/ID3WJ1 CVE: CVE-2025-40074 Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i... -------------------------------- 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> Conflicts: net/ipv4/icmp.c net/ipv4/ip_fragment.c net/ipv4/ipmr.c net/ipv4/route.c [commit a74fc62eec15 is not backport. commit a853c609504e and c73f836caefe(icmp.c), commit ca0359df45a5(ip_fragment.c), commit b2e653bcff0f and 3b7bc938e0ad and 35bec72a24ac(ipmr.c), commit 09eed1192cec(route.c) are not backport] Signed-off-by: Dong Chenchen <dongchenchen2@huawei.com> --- net/ipv4/icmp.c | 10 +++++++--- net/ipv4/ip_fragment.c | 9 +++++++-- net/ipv4/ipmr.c | 2 +- net/ipv4/route.c | 4 ++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index 7041f73d5be6..6fdac117fe16 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -316,23 +316,27 @@ 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; int vif; if (icmpv4_mask_allow(net, type, code)) - goto out; + return true; /* No rate limit on loopback */ - if (dst->dev && (dst->dev->flags&IFF_LOOPBACK)) + rcu_read_lock(); + dev = dst_dev_rcu(dst); + if (dev && (dev->flags&IFF_LOOPBACK)) goto out; - vif = l3mdev_master_ifindex(dst->dev); + vif = l3mdev_master_ifindex(dev); peer = inet_getpeer_v4(net->ipv4.peers, fl4->daddr, vif, 1); rc = inet_peer_xrlim_allow(peer, READ_ONCE(net->ipv4.sysctl_icmp_ratelimit)); if (peer) inet_putpeer(peer); out: + rcu_read_unlock(); return rc; } diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index ec2264adf2a6..5cdc65a5c010 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -475,13 +475,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(skb)->dev; - 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; @@ -491,9 +494,11 @@ int ip_defrag(struct net *net, struct sk_buff *skb, u32 user) ret = ip_frag_queue(qp, skb); spin_unlock(&qp->q.lock); + rcu_read_unlock(); ipq_put(qp); return ret; } + rcu_read_unlock(); __IP_INC_STATS(net, IPSTATS_MIB_REASMFAILS); kfree_skb(skb); diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index be1976536f1c..59104722b03f 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -1855,7 +1855,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 394fa615db82..142798b158e5 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -436,11 +436,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; + struct net_device *dev; struct neighbour *n; rcu_read_lock_bh(); - + 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.25.1