diff mbox

[v2,3/3] pppoatm: protect against freeing of vcc

Message ID 1354292626.21562.298.camel@shinybook.infradead.org
State RFC, archived
Delegated to: David Miller
Headers show

Commit Message

David Woodhouse Nov. 30, 2012, 4:23 p.m. UTC
On Fri, 2012-11-30 at 12:10 +0000, David Woodhouse wrote:
> In that case I think we're fine. I'll just do the same thing in
> br2684_push(), fix up the comment you just corrected, and we're all
> good.

OK, here's an update to me my patch 8/17 'br2684: don't send frames on
not-ready vcc'. It takes the socket lock and does fairly much the same
thing as your pppoatm version. It returns NETDEV_TX_BUSY and stops the
queue if the socket is locked, and it gets woken from the ->release_cb
callback.

I've dropped your Acked-By: since it's mostly new, but feel free to give
me a fresh one. With this I think we're done.

Unless Chas has any objections, I'll ask Dave to pull it...


From 47d5ad4c98452bcddfd00da1c659dac85202f213 Mon Sep 17 00:00:00 2001
From: David Woodhouse <dwmw2@infradead.org>
Date: Tue, 27 Nov 2012 23:28:36 +0000
Subject: [PATCH] br2684: don't send frames on not-ready vcc

Avoid submitting packets to a vcc which is being closed. Things go badly
wrong when the ->pop method gets later called after everything's been
torn down.

Use the ATM socket lock for synchronisation with vcc_destroy_socket(),
which clears the ATM_VF_READY bit under the same lock. Otherwise, we
could end up submitting a packet to the device driver even after its
->ops->close method has been called. And it could call the vcc's ->pop
method after the protocol has been shut down. Which leads to a panic.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
---
 net/atm/br2684.c | 48 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 45 insertions(+), 3 deletions(-)

Comments

Krzysztof Mazur Nov. 30, 2012, 5 p.m. UTC | #1
On Fri, Nov 30, 2012 at 04:23:46PM +0000, David Woodhouse wrote:
> 
> +static void br2684_release_cb(struct atm_vcc *atmvcc)
> +{
> +	struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
> +
> +	/*
> +	 * A race with br2684_xmit_vcc() might cause a spurious wakeup just
> +	 * after that function *stops* the queue, and qspace might actually
> +	 * go negative before the queue stops again. We cope with that.
> +	 */

We cannot race with br2684_xmit_vcc() because both br2684_xmit_vcc()
and br2684_release_cb() are called with locked sk->sk_lock.slock.

> +	if (atomic_read(&brvcc->qspace) > 0)
> +		netif_wake_queue(brvcc->device);
> +
> +	if (brvcc->old_release_cb)
> +		brvcc->old_release_cb(atmvcc);
> +}

Except that comment, the patch looks good:

Acked-by: Krzysztof Mazur <krzysiek@podlesie.net>

Krzysiek
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
chas williams - CONTRACTOR Nov. 30, 2012, 5:12 p.m. UTC | #2
On Fri, 30 Nov 2012 16:23:46 +0000
David Woodhouse <dwmw2@infradead.org> wrote:

> On Fri, 2012-11-30 at 12:10 +0000, David Woodhouse wrote:
> > In that case I think we're fine. I'll just do the same thing in
> > br2684_push(), fix up the comment you just corrected, and we're all
> > good.
> 
> OK, here's an update to me my patch 8/17 'br2684: don't send frames on
> not-ready vcc'. It takes the socket lock and does fairly much the same
> thing as your pppoatm version. It returns NETDEV_TX_BUSY and stops the
> queue if the socket is locked, and it gets woken from the ->release_cb
> callback.
> 
> I've dropped your Acked-By: since it's mostly new, but feel free to give
> me a fresh one. With this I think we're done.
> 
> Unless Chas has any objections, I'll ask Dave to pull it...

no objections.  i think this deals with my concerns.  as for splitting
the close functions, from one of your previous messages:


>Really, what we're saying is that *one* of the driver or protocol close
>functions needs to be split, and we need to do DPD or PDP. Since the
>device driver *can* abort/flush the TX queue and also any pending RX
>being handled by a tasklet, I think it makes most sense to keep it in
>the middle, with the protocol being handled first and last... which is
>the current order, as long as we consider setting ATM_VF_CLOSE to be the
>first part.

i believe this is essentially already done with the release_cb()
implementation right?  that is splitting the protocol detach/shutdown
into two parts.
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Krzysztof Mazur Nov. 30, 2012, 5:39 p.m. UTC | #3
On Fri, Nov 30, 2012 at 12:12:56PM -0500, chas williams - CONTRACTOR wrote:
> >Really, what we're saying is that *one* of the driver or protocol close
> >functions needs to be split, and we need to do DPD or PDP. Since the
> >device driver *can* abort/flush the TX queue and also any pending RX
> >being handled by a tasklet, I think it makes most sense to keep it in
> >the middle, with the protocol being handled first and last... which is
> >the current order, as long as we consider setting ATM_VF_CLOSE to be the
> >first part.
> 
> i believe this is essentially already done with the release_cb()
> implementation right?  that is splitting the protocol detach/shutdown
> into two parts.

partially, release_cb() is about ATM socket locking. To avoid some races
we need to take the ATM socket lock in protocol send function
(br2684_start_xmit, pppoatm_send, ...). That functions are executed
in bh context and we cannot sleep and wait for releasing the ATM socket
lock, so we just block sending and when the ATM socket is unlocked
release_cb() is called and we re-enabling sending.


Currently the first part of detach is just:

	lock_sock(sk)

	(without latest "br2684: don't send frames on not-ready vcc"
	the first part was

	set_bit(ATM_VF_CLOSE, &vcc->flags);
	clear_bit(ATM_VF_READY, &vcc->flags);

	for br2684)

After that the protocol stops sending new packets so the vcc may be
fully closed by ATM driver. The protocol is still ready to process
received packets. After vcc is closed the protocol can safely detach
knowing that no new packets will be received.

Krzysiek
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
David Woodhouse Nov. 30, 2012, 6:33 p.m. UTC | #4
On Fri, 2012-11-30 at 18:00 +0100, Krzysztof Mazur wrote:
> On Fri, Nov 30, 2012 at 04:23:46PM +0000, David Woodhouse wrote:
> > 
> > +static void br2684_release_cb(struct atm_vcc *atmvcc)
> > +{
> > +	struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
> > +
> > +	/*
> > +	 * A race with br2684_xmit_vcc() might cause a spurious wakeup just
> > +	 * after that function *stops* the queue, and qspace might actually
> > +	 * go negative before the queue stops again. We cope with that.
> > +	 */
> 
> We cannot race with br2684_xmit_vcc() because both br2684_xmit_vcc()
> and br2684_release_cb() are called with locked sk->sk_lock.slock.

Ah, right. For some reason I thought the lock was already dropped when
->release_cb() was called. In that case I'll remove the comment. Thanks.
diff mbox

Patch

diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 8eb6fbe..5ff145f 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -68,6 +68,7 @@  struct br2684_vcc {
 	/* keep old push, pop functions for chaining */
 	void (*old_push)(struct atm_vcc *vcc, struct sk_buff *skb);
 	void (*old_pop)(struct atm_vcc *vcc, struct sk_buff *skb);
+	void (*old_release_cb)(struct atm_vcc *vcc);
 	enum br2684_encaps encaps;
 	struct list_head brvccs;
 #ifdef CONFIG_ATM_BR2684_IPFILTER
@@ -269,6 +270,22 @@  static int br2684_xmit_vcc(struct sk_buff *skb, struct net_device *dev,
 	return !atmvcc->send(atmvcc, skb);
 }
 
+static void br2684_release_cb(struct atm_vcc *atmvcc)
+{
+	struct br2684_vcc *brvcc = BR2684_VCC(atmvcc);
+
+	/*
+	 * A race with br2684_xmit_vcc() might cause a spurious wakeup just
+	 * after that function *stops* the queue, and qspace might actually
+	 * go negative before the queue stops again. We cope with that.
+	 */
+	if (atomic_read(&brvcc->qspace) > 0)
+		netif_wake_queue(brvcc->device);
+
+	if (brvcc->old_release_cb)
+		brvcc->old_release_cb(atmvcc);
+}
+
 static inline struct br2684_vcc *pick_outgoing_vcc(const struct sk_buff *skb,
 						   const struct br2684_dev *brdev)
 {
@@ -280,6 +297,8 @@  static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
 {
 	struct br2684_dev *brdev = BRPRIV(dev);
 	struct br2684_vcc *brvcc;
+	struct atm_vcc *atmvcc;
+	netdev_tx_t ret = NETDEV_TX_OK;
 
 	pr_debug("skb_dst(skb)=%p\n", skb_dst(skb));
 	read_lock(&devs_lock);
@@ -290,9 +309,26 @@  static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
 		dev->stats.tx_carrier_errors++;
 		/* netif_stop_queue(dev); */
 		dev_kfree_skb(skb);
-		read_unlock(&devs_lock);
-		return NETDEV_TX_OK;
+		goto out_devs;
+	}
+	atmvcc = brvcc->atmvcc;
+
+	bh_lock_sock(sk_atm(atmvcc));
+
+	if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) ||
+	    test_bit(ATM_VF_CLOSE, &atmvcc->flags) ||
+	    !test_bit(ATM_VF_READY, &atmvcc->flags)) {
+		dev->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+		goto out;
 	}
+
+	if (sock_owned_by_user(sk_atm(atmvcc))) {
+		netif_stop_queue(brvcc->device);
+		ret = NETDEV_TX_BUSY;
+		goto out;
+	}
+
 	if (!br2684_xmit_vcc(skb, dev, brvcc)) {
 		/*
 		 * We should probably use netif_*_queue() here, but that
@@ -304,8 +340,11 @@  static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
 		dev->stats.tx_errors++;
 		dev->stats.tx_fifo_errors++;
 	}
+ out:
+	bh_unlock_sock(sk_atm(atmvcc));
+ out_devs:
 	read_unlock(&devs_lock);
-	return NETDEV_TX_OK;
+	return ret;
 }
 
 /*
@@ -378,6 +417,7 @@  static void br2684_close_vcc(struct br2684_vcc *brvcc)
 	list_del(&brvcc->brvccs);
 	write_unlock_irq(&devs_lock);
 	brvcc->atmvcc->user_back = NULL;	/* what about vcc->recvq ??? */
+	brvcc->atmvcc->release_cb = brvcc->old_release_cb;
 	brvcc->old_push(brvcc->atmvcc, NULL);	/* pass on the bad news */
 	kfree(brvcc);
 	module_put(THIS_MODULE);
@@ -554,9 +594,11 @@  static int br2684_regvcc(struct atm_vcc *atmvcc, void __user * arg)
 	brvcc->encaps = (enum br2684_encaps)be.encaps;
 	brvcc->old_push = atmvcc->push;
 	brvcc->old_pop = atmvcc->pop;
+	brvcc->old_release_cb = atmvcc->release_cb;
 	barrier();
 	atmvcc->push = br2684_push;
 	atmvcc->pop = br2684_pop;
+	atmvcc->release_cb = br2684_release_cb;
 
 	/* initialize netdev carrier state */
 	if (atmvcc->dev->signal == ATM_PHY_SIG_LOST)