atm: clip: Fix potential null-ptr-deref in to_atmarpd().
[ Upstream commit 706cc36477139c1616a9b2b96610a8bb520b7119 ] atmarpd is protected by RTNL since commitf3a0592b37("[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:1da177e4c3("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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
964c7ce785
commit
70eac9ba7c
@@ -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)
|
||||
@@ -607,10 +619,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);
|
||||
@@ -631,15 +645,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 */
|
||||
@@ -648,7 +662,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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user