Commit 706cc364 authored by Kuniyuki Iwashima's avatar Kuniyuki Iwashima Committed by Jakub Kicinski
Browse files

atm: clip: Fix potential null-ptr-deref in to_atmarpd().



atmarpd is protected by RTNL since commit f3a0592b ("[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: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarKuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: default avatarSimon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20250704062416.1613927-2-kuniyu@google.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent 3c78f91e
Loading
Loading
Loading
Loading
+29 −15
Original line number Diff line number Diff line
@@ -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;
}