]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ipv4: start using dst_dev_rcu()
authorEric Dumazet <edumazet@google.com>
Thu, 28 Aug 2025 19:58:23 +0000 (19:58 +0000)
committerJakub Kicinski <kuba@kernel.org>
Sat, 30 Aug 2025 02:36:32 +0000 (19:36 -0700)
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>
net/ipv4/icmp.c
net/ipv4/ip_fragment.c
net/ipv4/ipmr.c
net/ipv4/route.c

index 7248c15cbd7592268dce883482727c994110dae8..823c70e34de835e78f58a7322e502324c795df86 100644 (file)
@@ -319,17 +319,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
index b2584cce90ae1c14550396de486131b700d4afd7..f7012479713ba68db7c1c3fcee07a86141de31d3 100644 (file)
@@ -476,14 +476,16 @@ out_fail:
 /* 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;
index 345e5faac63471249540fa77cb11fb2de50fd323..ca9eaee4c2ef5f5cdc03608291ad1a0dc187d657 100644 (file)
@@ -1905,7 +1905,7 @@ static int ipmr_prepare_xmit(struct net *net, struct mr_table *mrt,
                return -1;
        }
 
-       encap += LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len;
+       encap += LL_RESERVED_SPACE(dst_dev_rcu(&rt->dst)) + rt->dst.header_len;
 
        if (skb_cow(skb, encap)) {
                ip_rt_put(rt);
@@ -1958,7 +1958,7 @@ static void ipmr_queue_fwd_xmit(struct net *net, struct mr_table *mrt,
         * result in receiving multiple packets.
         */
        NF_HOOK(NFPROTO_IPV4, NF_INET_FORWARD,
-               net, NULL, skb, skb->dev, rt->dst.dev,
+               net, NULL, skb, skb->dev, dst_dev_rcu(&rt->dst),
                ipmr_forward_finish);
        return;
 
@@ -2302,7 +2302,7 @@ int ip_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb)
 
        guard(rcu)();
 
-       dev = rt->dst.dev;
+       dev = dst_dev_rcu(&rt->dst);
 
        if (IPCB(skb)->flags & IPSKB_FORWARDED)
                goto mc_output;
index 44382d17558909318770acdf43d2c3b1da2b621c..50309f2ab132cf39d91965b9de468aaa3ee8467a 100644 (file)
@@ -414,11 +414,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) {