Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8e352671 authored by Xie He's avatar Xie He Committed by Greg Kroah-Hartman
Browse files

net: lapbether: Prevent racing when checking whether the netif is running



[ Upstream commit 5acd0cfbfbb5a688da1bfb1a2152b0c855115a35 ]

There are two "netif_running" checks in this driver. One is in
"lapbeth_xmit" and the other is in "lapbeth_rcv". They serve to make
sure that the LAPB APIs called in these functions are called before
"lapb_unregister" is called by the "ndo_stop" function.

However, these "netif_running" checks are unreliable, because it's
possible that immediately after "netif_running" returns true, "ndo_stop"
is called (which causes "lapb_unregister" to be called).

This patch adds locking to make sure "lapbeth_xmit" and "lapbeth_rcv" can
reliably check and ensure the netif is running while doing their work.

Fixes: 1da177e4 ("Linux-2.6.12-rc2")
Signed-off-by: default avatarXie He <xie.he.0141@gmail.com>
Acked-by: default avatarMartin Schiller <ms@dev.tdt.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent eda1827e
Loading
Loading
Loading
Loading
+25 −7
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ struct lapbethdev {
	struct list_head	node;
	struct net_device	*ethdev;	/* link to ethernet device */
	struct net_device	*axdev;		/* lapbeth device (lapb#) */
	bool			up;
	spinlock_t		up_lock;	/* Protects "up" */
};

static LIST_HEAD(lapbeth_devices);
@@ -103,8 +105,9 @@ static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
	rcu_read_lock();
	lapbeth = lapbeth_get_x25_dev(dev);
	if (!lapbeth)
		goto drop_unlock;
	if (!netif_running(lapbeth->axdev))
		goto drop_unlock_rcu;
	spin_lock_bh(&lapbeth->up_lock);
	if (!lapbeth->up)
		goto drop_unlock;

	len = skb->data[0] + skb->data[1] * 256;
@@ -119,11 +122,14 @@ static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
		goto drop_unlock;
	}
out:
	spin_unlock_bh(&lapbeth->up_lock);
	rcu_read_unlock();
	return 0;
drop_unlock:
	kfree_skb(skb);
	goto out;
drop_unlock_rcu:
	rcu_read_unlock();
drop:
	kfree_skb(skb);
	return 0;
@@ -151,13 +157,11 @@ static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)
static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
				      struct net_device *dev)
{
	struct lapbethdev *lapbeth = netdev_priv(dev);
	int err;

	/*
	 * Just to be *really* sure not to send anything if the interface
	 * is down, the ethernet device may have gone.
	 */
	if (!netif_running(dev))
	spin_lock_bh(&lapbeth->up_lock);
	if (!lapbeth->up)
		goto drop;

	/* There should be a pseudo header of 1 byte added by upper layers.
@@ -188,6 +192,7 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
		goto drop;
	}
out:
	spin_unlock_bh(&lapbeth->up_lock);
	return NETDEV_TX_OK;
drop:
	kfree_skb(skb);
@@ -279,6 +284,7 @@ static const struct lapb_register_struct lapbeth_callbacks = {
 */
static int lapbeth_open(struct net_device *dev)
{
	struct lapbethdev *lapbeth = netdev_priv(dev);
	int err;

	if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {
@@ -286,13 +292,22 @@ static int lapbeth_open(struct net_device *dev)
		return -ENODEV;
	}

	spin_lock_bh(&lapbeth->up_lock);
	lapbeth->up = true;
	spin_unlock_bh(&lapbeth->up_lock);

	return 0;
}

static int lapbeth_close(struct net_device *dev)
{
	struct lapbethdev *lapbeth = netdev_priv(dev);
	int err;

	spin_lock_bh(&lapbeth->up_lock);
	lapbeth->up = false;
	spin_unlock_bh(&lapbeth->up_lock);

	if ((err = lapb_unregister(dev)) != LAPB_OK)
		pr_err("lapb_unregister error: %d\n", err);

@@ -350,6 +365,9 @@ static int lapbeth_new_device(struct net_device *dev)
	dev_hold(dev);
	lapbeth->ethdev = dev;

	lapbeth->up = false;
	spin_lock_init(&lapbeth->up_lock);

	rc = -EIO;
	if (register_netdevice(ndev))
		goto fail;