]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
atm: clip: Fix potential null-ptr-deref in to_atmarpd().
authorKuniyuki Iwashima <kuniyu@google.com>
Fri, 4 Jul 2025 06:23:51 +0000 (06:23 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 17 Jul 2025 16:25:02 +0000 (18:25 +0200)
[ Upstream commit 706cc36477139c1616a9b2b96610a8bb520b7119 ]

atmarpd is protected by RTNL since commit f3a0592b37b8 ("[ATM]: clip
causes unregister hang").

However, it is not enough because to_atmarpd() is called without RTNL,
especially clip_neigh_solicit() / neigh_ops->solicit() is unsleepable.

Also, there is no RTNL dependency around atmarpd.

Let's use a private mutex and RCU to protect access to atmarpd in
to_atmarpd().

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250704062416.1613927-2-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
net/atm/clip.c

index 294cb9efe3d3820046cd123caf7037eeb0dbe9c4..d5363ae5296ad0c9a8c4e1baf6633bb346c4e65c 100644 (file)
@@ -45,7 +45,8 @@
 #include <net/atmclip.h>
 
 static struct net_device *clip_devs;
-static struct atm_vcc *atmarpd;
+static struct atm_vcc __rcu *atmarpd;
+static DEFINE_MUTEX(atmarpd_lock);
 static struct timer_list idle_timer;
 static const struct neigh_ops clip_neigh_ops;
 
@@ -53,24 +54,35 @@ static int to_atmarpd(enum atmarp_ctrl_type type, int itf, __be32 ip)
 {
        struct sock *sk;
        struct atmarp_ctrl *ctrl;
+       struct atm_vcc *vcc;
        struct sk_buff *skb;
+       int err = 0;
 
        pr_debug("(%d)\n", type);
-       if (!atmarpd)
-               return -EUNATCH;
+
+       rcu_read_lock();
+       vcc = rcu_dereference(atmarpd);
+       if (!vcc) {
+               err = -EUNATCH;
+               goto unlock;
+       }
        skb = alloc_skb(sizeof(struct atmarp_ctrl), GFP_ATOMIC);
-       if (!skb)
-               return -ENOMEM;
+       if (!skb) {
+               err = -ENOMEM;
+               goto unlock;
+       }
        ctrl = skb_put(skb, sizeof(struct atmarp_ctrl));
        ctrl->type = type;
        ctrl->itf_num = itf;
        ctrl->ip = ip;
-       atm_force_charge(atmarpd, skb->truesize);
+       atm_force_charge(vcc, skb->truesize);
 
-       sk = sk_atm(atmarpd);
+       sk = sk_atm(vcc);
        skb_queue_tail(&sk->sk_receive_queue, skb);
        sk->sk_data_ready(sk);
-       return 0;
+unlock:
+       rcu_read_unlock();
+       return err;
 }
 
 static void link_vcc(struct clip_vcc *clip_vcc, struct atmarp_entry *entry)
@@ -608,10 +620,12 @@ static void atmarpd_close(struct atm_vcc *vcc)
 {
        pr_debug("\n");
 
-       rtnl_lock();
-       atmarpd = NULL;
+       mutex_lock(&atmarpd_lock);
+       RCU_INIT_POINTER(atmarpd, NULL);
+       mutex_unlock(&atmarpd_lock);
+
+       synchronize_rcu();
        skb_queue_purge(&sk_atm(vcc)->sk_receive_queue);
-       rtnl_unlock();
 
        pr_debug("(done)\n");
        module_put(THIS_MODULE);
@@ -632,15 +646,15 @@ static struct atm_dev atmarpd_dev = {
 
 static int atm_init_atmarp(struct atm_vcc *vcc)
 {
-       rtnl_lock();
+       mutex_lock(&atmarpd_lock);
        if (atmarpd) {
-               rtnl_unlock();
+               mutex_unlock(&atmarpd_lock);
                return -EADDRINUSE;
        }
 
        mod_timer(&idle_timer, jiffies + CLIP_CHECK_INTERVAL * HZ);
 
-       atmarpd = vcc;
+       rcu_assign_pointer(atmarpd, vcc);
        set_bit(ATM_VF_META, &vcc->flags);
        set_bit(ATM_VF_READY, &vcc->flags);
            /* allow replies and avoid getting closed if signaling dies */
@@ -649,7 +663,7 @@ static int atm_init_atmarp(struct atm_vcc *vcc)
        vcc->push = NULL;
        vcc->pop = NULL; /* crash */
        vcc->push_oam = NULL; /* crash */
-       rtnl_unlock();
+       mutex_unlock(&atmarpd_lock);
        return 0;
 }