Denis Kirjanov 4bee41
From 788115e0096a2ed4a47293f0484438b5b17f5b0b Mon Sep 17 00:00:00 2001
Denis Kirjanov 4bee41
From: Xie He <xie.he.0141@gmail.com>
Denis Kirjanov 4bee41
Date: Wed, 10 Mar 2021 23:23:09 -0800
Denis Kirjanov 4bee41
Subject: [PATCH 12/19] net: lapbether: Prevent racing when checking whether
Denis Kirjanov 4bee41
 the netif is running
Denis Kirjanov 4bee41
Git-commit: 5acd0cfbfbb5a688da1bfb1a2152b0c855115a35
Denis Kirjanov 4bee41
Patch-mainline: v5.13-rc1
Denis Kirjanov 4bee41
References: git-fixes
Denis Kirjanov 4bee41
Denis Kirjanov 4bee41
There are two "netif_running" checks in this driver. One is in
Denis Kirjanov 4bee41
"lapbeth_xmit" and the other is in "lapbeth_rcv". They serve to make
Denis Kirjanov 4bee41
sure that the LAPB APIs called in these functions are called before
Denis Kirjanov 4bee41
"lapb_unregister" is called by the "ndo_stop" function.
Denis Kirjanov 4bee41
Denis Kirjanov 4bee41
However, these "netif_running" checks are unreliable, because it's
Denis Kirjanov 4bee41
possible that immediately after "netif_running" returns true, "ndo_stop"
Denis Kirjanov 4bee41
is called (which causes "lapb_unregister" to be called).
Denis Kirjanov 4bee41
Denis Kirjanov 4bee41
This patch adds locking to make sure "lapbeth_xmit" and "lapbeth_rcv" can
Denis Kirjanov 4bee41
reliably check and ensure the netif is running while doing their work.
Denis Kirjanov 4bee41
Denis Kirjanov 4bee41
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Denis Kirjanov 4bee41
Signed-off-by: Xie He <xie.he.0141@gmail.com>
Denis Kirjanov 4bee41
Acked-by: Martin Schiller <ms@dev.tdt.de>
Denis Kirjanov 4bee41
Signed-off-by: David S. Miller <davem@davemloft.net>
Denis Kirjanov 4bee41
Signed-off-by: Denis Kirjanov <denis.kirjanov@suse.com>
Denis Kirjanov 4bee41
---
Denis Kirjanov 4bee41
 drivers/net/wan/lapbether.c | 32 +++++++++++++++++++++++++-------
Denis Kirjanov 4bee41
 1 file changed, 25 insertions(+), 7 deletions(-)
Denis Kirjanov 4bee41
Denis Kirjanov 4bee41
diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c
Denis Kirjanov 4bee41
index fd6acf0dcec3..91cfcb98051f 100644
Denis Kirjanov 4bee41
--- a/drivers/net/wan/lapbether.c
Denis Kirjanov 4bee41
+++ b/drivers/net/wan/lapbether.c
Denis Kirjanov 4bee41
@@ -56,6 +56,8 @@ struct lapbethdev {
Denis Kirjanov 4bee41
 	struct list_head	node;
Denis Kirjanov 4bee41
 	struct net_device	*ethdev;	/* link to ethernet device */
Denis Kirjanov 4bee41
 	struct net_device	*axdev;		/* lapbeth device (lapb#) */
Denis Kirjanov 4bee41
+	bool			up;
Denis Kirjanov 4bee41
+	spinlock_t		up_lock;	/* Protects "up" */
Denis Kirjanov 4bee41
 };
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
 static LIST_HEAD(lapbeth_devices);
Denis Kirjanov 4bee41
@@ -103,8 +105,9 @@ static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
Denis Kirjanov 4bee41
 	rcu_read_lock();
Denis Kirjanov 4bee41
 	lapbeth = lapbeth_get_x25_dev(dev);
Denis Kirjanov 4bee41
 	if (!lapbeth)
Denis Kirjanov 4bee41
-		goto drop_unlock;
Denis Kirjanov 4bee41
-	if (!netif_running(lapbeth->axdev))
Denis Kirjanov 4bee41
+		goto drop_unlock_rcu;
Denis Kirjanov 4bee41
+	spin_lock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+	if (!lapbeth->up)
Denis Kirjanov 4bee41
 		goto drop_unlock;
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
 	len = skb->data[0] + skb->data[1] * 256;
Denis Kirjanov 4bee41
@@ -119,11 +122,14 @@ static int lapbeth_rcv(struct sk_buff *skb, struct net_device *dev, struct packe
Denis Kirjanov 4bee41
 		goto drop_unlock;
Denis Kirjanov 4bee41
 	}
Denis Kirjanov 4bee41
 out:
Denis Kirjanov 4bee41
+	spin_unlock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
 	rcu_read_unlock();
Denis Kirjanov 4bee41
 	return 0;
Denis Kirjanov 4bee41
 drop_unlock:
Denis Kirjanov 4bee41
 	kfree_skb(skb);
Denis Kirjanov 4bee41
 	goto out;
Denis Kirjanov 4bee41
+drop_unlock_rcu:
Denis Kirjanov 4bee41
+	rcu_read_unlock();
Denis Kirjanov 4bee41
 drop:
Denis Kirjanov 4bee41
 	kfree_skb(skb);
Denis Kirjanov 4bee41
 	return 0;
Denis Kirjanov 4bee41
@@ -151,13 +157,11 @@ static int lapbeth_data_indication(struct net_device *dev, struct sk_buff *skb)
Denis Kirjanov 4bee41
 static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
Denis Kirjanov 4bee41
 				      struct net_device *dev)
Denis Kirjanov 4bee41
 {
Denis Kirjanov 4bee41
+	struct lapbethdev *lapbeth = netdev_priv(dev);
Denis Kirjanov 4bee41
 	int err;
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
-	/*
Denis Kirjanov 4bee41
-	 * Just to be *really* sure not to send anything if the interface
Denis Kirjanov 4bee41
-	 * is down, the ethernet device may have gone.
Denis Kirjanov 4bee41
-	 */
Denis Kirjanov 4bee41
-	if (!netif_running(dev))
Denis Kirjanov 4bee41
+	spin_lock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+	if (!lapbeth->up)
Denis Kirjanov 4bee41
 		goto drop;
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
 	switch (skb->data[0]) {
Denis Kirjanov 4bee41
@@ -182,6 +186,7 @@ static netdev_tx_t lapbeth_xmit(struct sk_buff *skb,
Denis Kirjanov 4bee41
 		goto drop;
Denis Kirjanov 4bee41
 	}
Denis Kirjanov 4bee41
 out:
Denis Kirjanov 4bee41
+	spin_unlock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
 	return NETDEV_TX_OK;
Denis Kirjanov 4bee41
 drop:
Denis Kirjanov 4bee41
 	kfree_skb(skb);
Denis Kirjanov 4bee41
@@ -271,6 +276,7 @@ static const struct lapb_register_struct lapbeth_callbacks = {
Denis Kirjanov 4bee41
  */
Denis Kirjanov 4bee41
 static int lapbeth_open(struct net_device *dev)
Denis Kirjanov 4bee41
 {
Denis Kirjanov 4bee41
+	struct lapbethdev *lapbeth = netdev_priv(dev);
Denis Kirjanov 4bee41
 	int err;
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
 	if ((err = lapb_register(dev, &lapbeth_callbacks)) != LAPB_OK) {
Denis Kirjanov 4bee41
@@ -278,13 +284,22 @@ static int lapbeth_open(struct net_device *dev)
Denis Kirjanov 4bee41
 		return -ENODEV;
Denis Kirjanov 4bee41
 	}
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
+	spin_lock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+	lapbeth->up = true;
Denis Kirjanov 4bee41
+	spin_unlock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+
Denis Kirjanov 4bee41
 	return 0;
Denis Kirjanov 4bee41
 }
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
 static int lapbeth_close(struct net_device *dev)
Denis Kirjanov 4bee41
 {
Denis Kirjanov 4bee41
+	struct lapbethdev *lapbeth = netdev_priv(dev);
Denis Kirjanov 4bee41
 	int err;
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
+	spin_lock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+	lapbeth->up = false;
Denis Kirjanov 4bee41
+	spin_unlock_bh(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+
Denis Kirjanov 4bee41
 	if ((err = lapb_unregister(dev)) != LAPB_OK)
Denis Kirjanov 4bee41
 		pr_err("lapb_unregister error: %d\n", err);
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
@@ -332,6 +347,9 @@ static int lapbeth_new_device(struct net_device *dev)
Denis Kirjanov 4bee41
 	dev_hold(dev);
Denis Kirjanov 4bee41
 	lapbeth->ethdev = dev;
Denis Kirjanov 4bee41
 
Denis Kirjanov 4bee41
+	lapbeth->up = false;
Denis Kirjanov 4bee41
+	spin_lock_init(&lapbeth->up_lock);
Denis Kirjanov 4bee41
+
Denis Kirjanov 4bee41
 	rc = -EIO;
Denis Kirjanov 4bee41
 	if (register_netdevice(ndev))
Denis Kirjanov 4bee41
 		goto fail;
Denis Kirjanov 4bee41
-- 
Denis Kirjanov 4bee41
2.16.4
Denis Kirjanov 4bee41