| From d56e002511a2d98cc201d61e8751e538230a0907 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 10 Mar 2021 23:23:09 -0800 |
| Subject: net: lapbether: Prevent racing when checking whether the netif is |
| running |
| |
| From: Xie He <xie.he.0141@gmail.com> |
| |
| [ 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: 1da177e4c3f4 ("Linux-2.6.12-rc2") |
| Signed-off-by: Xie He <xie.he.0141@gmail.com> |
| Acked-by: Martin Schiller <ms@dev.tdt.de> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/net/wan/lapbether.c | 32 +++++++++++++++++++++++++------- |
| 1 file changed, 25 insertions(+), 7 deletions(-) |
| |
| diff --git a/drivers/net/wan/lapbether.c b/drivers/net/wan/lapbether.c |
| index c3372498f4f1..8fda0446ff71 100644 |
| --- a/drivers/net/wan/lapbether.c |
| +++ b/drivers/net/wan/lapbether.c |
| @@ -51,6 +51,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); |
| @@ -101,8 +103,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; |
| @@ -117,11 +120,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. |
| @@ -194,6 +198,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); |
| @@ -285,6 +290,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) { |
| @@ -292,13 +298,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); |
| |
| @@ -356,6 +371,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; |
| -- |
| 2.30.2 |
| |