public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] n_gsm: race between ld close and gsmtty open
@ 2013-10-17  3:29 channing
  2013-10-17 17:14 ` Greg KH
  0 siblings, 1 reply; 3+ messages in thread
From: channing @ 2013-10-17  3:29 UTC (permalink / raw)
  To: gregkh, jslaby; +Cc: linux-kernel


ttyA has ld associated to n_gsm, when ttyA is closing, it triggers
to release gsmttyB's ld data dlci[B], then race would happen if gsmttyB
is opening in parallel.

Here are some of race cases we found recently in test:

CASE #1
====================================================================
releasing dlci[B] race with gsmtty_install(gsmttyB), then panic
in gsmtty_open(gsmttyB), as below:

 tty_release(ttyA)                  tty_open(gsmttyB)
     |                                   |
   -----                           gsmtty_install(gsmttyB)
     |                                   |
   -----                    gsm_dlci_alloc(gsmttyB) => alloc dlci[B]
 tty_ldisc_release(ttyA)               -----
     |                                   |
 gsm_dlci_release(dlci[B])             -----
     |                                   |
 gsm_dlci_free(dlci[B])                -----
     |                                   |
   -----                           gsmtty_open(gsmttyB)

 gsmtty_open()
 {
     struct gsm_dlci *dlci = tty->driver_data; => here it uses dlci[B]
     ...
 }

 In gsmtty_open(gsmttyA), it uses dlci[B] which was release, so hit a panic.
=====================================================================

CASE #2
=====================================================================
releasing dlci[0] race with gsmtty_install(gsmttyB), then panic
in gsmtty_open(), as below:

 tty_release(ttyA)                  tty_open(gsmttyB)
     |                                   |
   -----                           gsmtty_install(gsmttyB)
     |                                   |
   -----                    gsm_dlci_alloc(gsmttyB) => alloc dlci[B]
     |                                   |
   -----                         gsmtty_open(gsmttyB) fail
     |                                   |
   -----                           tty_release(gsmttyB)
     |                                   |
   -----                           gsmtty_close(gsmttyB)
     |                                   |
   -----                        gsmtty_detach_dlci(dlci[B])
     |                                   |
   -----                             dlci_put(dlci[B])
     |                                   |
 tty_ldisc_release(ttyA)               -----
     |                                   |
 gsm_dlci_release(dlci[0])             -----
     |                                   |
 gsm_dlci_free(dlci[0])                -----
     |                                   |
   -----                             dlci_put(dlci[0])

 In gsmtty_detach_dlci(dlci[B]), it tries to use dlci[0] which was released,
 then hit panic.
=====================================================================

IMHO, n_gsm tty operations would refer released ldisc,  as long as
gsm_dlci_release() has chance to release ldisc data when some gsmtty operations
are not completed..

This patch is try to avoid it by:

1) in n_gsm driver, use a global gsm spin lock to avoid gsm_dlci_release() run in
parallel with gsmtty_install();

2) Increase dlci's ref count in gsmtty_install() instead of in gsmtty_open(), the
purpose is to prevent gsm_dlci_release() releasing dlci after gsmtty_install()
allocats dlci but before gsmtty_open increases dlci's ref count;

3) Decrease dlci's ref count in gsmtty_remove(), which is a tty framework api, and
this is the opposite process of step 2).

Signed-off-by: Chao Bi <chao.bi@intel.com>
---
 drivers/tty/n_gsm.c |   37 +++++++++++++++++++++++++++----------
 1 files changed, 27 insertions(+), 10 deletions(-)

diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index c0f76da..41ef7d7 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -2054,9 +2054,11 @@ void gsm_cleanup_mux(struct gsm_mux *gsm)
 					dlci->state == DLCI_CLOSED);
 	}
 	/* Free up any link layer users */
+	spin_lock(&gsm->lock);
 	for (i = 0; i < NUM_DLCI; i++)
 		if (gsm->dlci[i])
 			gsm_dlci_release(gsm->dlci[i]);
+	spin_unlock(&gsm->lock);
 	/* Now wipe the queues */
 	list_for_each_entry_safe(txq, ntxq, &gsm->tx_list, list)
 		kfree(txq);
@@ -2909,23 +2911,33 @@ static int gsmtty_install(struct tty_driver *driver, struct tty_struct *tty)
 	This is ok from a locking
 	perspective as we don't have to worry about this
 	if DLCI0 is lost */
-	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN)
+	spin_lock(&gsm->lock);
+	if (gsm->dlci[0] && gsm->dlci[0]->state != DLCI_OPEN) {
+		spin_unlock(&gsm->lock);
 		return -EL2NSYNC;
+	}
 	dlci = gsm->dlci[line];
 	if (dlci == NULL) {
 		alloc = true;
 		dlci = gsm_dlci_alloc(gsm, line);
 	}
-	if (dlci == NULL)
+	if (dlci == NULL) {
+		spin_unlock(&gsm->lock);
 		return -ENOMEM;
+	}
 	ret = tty_port_install(&dlci->port, driver, tty);
 	if (ret) {
 		if (alloc)
 			dlci_put(dlci);
+		spin_unlock(&gsm->lock);
 		return ret;
 	}
 
+	dlci_get(dlci);
+	dlci_get(gsm->dlci[0]);
+	mux_get(gsm);
 	tty->driver_data = dlci;
+	spin_unlock(&gsm->lock);
 
 	return 0;
 }
@@ -2936,9 +2948,6 @@ static int gsmtty_open(struct tty_struct *tty, struct file *filp)
 	struct tty_port *port = &dlci->port;
 
 	port->count++;
-	dlci_get(dlci);
-	dlci_get(dlci->gsm->dlci[0]);
-	mux_get(dlci->gsm);
 	tty_port_tty_set(port, tty);
 
 	dlci->modem_rx = 0;
@@ -2965,7 +2974,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
 	mutex_unlock(&dlci->mutex);
 	gsm = dlci->gsm;
 	if (tty_port_close_start(&dlci->port, tty, filp) == 0)
-		goto out;
+		return;
 	gsm_dlci_begin_close(dlci);
 	if (test_bit(ASYNCB_INITIALIZED, &dlci->port.flags)) {
 		if (C_HUPCL(tty))
@@ -2973,10 +2982,7 @@ static void gsmtty_close(struct tty_struct *tty, struct file *filp)
 	}
 	tty_port_close_end(&dlci->port, tty);
 	tty_port_tty_set(&dlci->port, NULL);
-out:
-	dlci_put(dlci);
-	dlci_put(gsm->dlci[0]);
-	mux_put(gsm);
+	return;
 }
 
 static void gsmtty_hangup(struct tty_struct *tty)
@@ -3153,6 +3159,16 @@ static int gsmtty_break_ctl(struct tty_struct *tty, int state)
 	return gsmtty_modem_update(dlci, encode);
 }
 
+static int gsmtty_remove(struct tty_driver *driver, struct tty_struct *tty)
+{
+	struct gsm_dlci *dlci = tty->driver_data;
+	struct gsm_mux *gsm = dlci->gsm;
+
+	dlci_put(dlci);
+	dlci_put(gsm->dlci[0]);
+	mux_put(gsm);
+	driver->ttys[tty->index] = NULL;
+}
 
 /* Virtual ttys for the demux */
 static const struct tty_operations gsmtty_ops = {
@@ -3172,6 +3188,7 @@ static const struct tty_operations gsmtty_ops = {
 	.tiocmget		= gsmtty_tiocmget,
 	.tiocmset		= gsmtty_tiocmset,
 	.break_ctl		= gsmtty_break_ctl,
+	.remove			= gsmtty_remove,
 };
 
 
-- 
1.7.1




^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] n_gsm: race between ld close and gsmtty open
  2013-10-17  3:29 [PATCH] n_gsm: race between ld close and gsmtty open channing
@ 2013-10-17 17:14 ` Greg KH
  2013-10-18  1:59   ` channing
  0 siblings, 1 reply; 3+ messages in thread
From: Greg KH @ 2013-10-17 17:14 UTC (permalink / raw)
  To: channing; +Cc: jslaby, linux-kernel

On Thu, Oct 17, 2013 at 11:29:10AM +0800, channing wrote:
> 
> ttyA has ld associated to n_gsm, when ttyA is closing, it triggers
> to release gsmttyB's ld data dlci[B], then race would happen if gsmttyB
> is opening in parallel.
> 
> Here are some of race cases we found recently in test:

Are these still an issue with 3.12-rc4?  Lots of pty rework has gone
into there that should help to resolve some of these problems.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] n_gsm: race between ld close and gsmtty open
  2013-10-17 17:14 ` Greg KH
@ 2013-10-18  1:59   ` channing
  0 siblings, 0 replies; 3+ messages in thread
From: channing @ 2013-10-18  1:59 UTC (permalink / raw)
  To: Greg KH; +Cc: jslaby, linux-kernel

On Thu, 2013-10-17 at 10:14 -0700, Greg KH wrote:
> On Thu, Oct 17, 2013 at 11:29:10AM +0800, channing wrote:
> > 
> > ttyA has ld associated to n_gsm, when ttyA is closing, it triggers
> > to release gsmttyB's ld data dlci[B], then race would happen if gsmttyB
> > is opening in parallel.
> > 
> > Here are some of race cases we found recently in test:
> 
> Are these still an issue with 3.12-rc4?  Lots of pty rework has gone
> into there that should help to resolve some of these problems.
> 

Hi Greg,
I viewed 3.12-rc4 and IMHO it still has this issue, Please correct me if I'm wrong.
This patch was based on 3.12-rc4, following is version info of my code base:

linux/Makefile:
VERSION = 3
PATCHLEVEL = 12
SUBLEVEL = 0
EXTRAVERSION = -rc4

And this version of patch has one line not correct, I updated it in another mail 
with title "[PATCH v2]n_gsm:...", would you please check?

thanks a lot,
chao


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2013-10-18  1:30 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-10-17  3:29 [PATCH] n_gsm: race between ld close and gsmtty open channing
2013-10-17 17:14 ` Greg KH
2013-10-18  1:59   ` channing

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox