diff mbox

ircomm: release tty before sleeping potentially indefintely

Message ID 1362350153-26225-1-git-send-email-sasha.levin@oracle.com
State Rejected, archived
Delegated to: David Miller
Headers show

Commit Message

Sasha Levin March 3, 2013, 10:35 p.m. UTC
ircomm_tty_block_til_ready would hold tty lock while blocking. Since the sleep
might take a long time we can prevent other processes from accessing the tty,
causing hung tasks and a dead tty.

Diagnosed-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
---
 net/irda/ircomm/ircomm_tty.c | 2 ++
 1 file changed, 2 insertions(+)

Comments

David Miller March 3, 2013, 10:47 p.m. UTC | #1
From: Sasha Levin <sasha.levin@oracle.com>
Date: Sun,  3 Mar 2013 17:35:53 -0500

> ircomm_tty_block_til_ready would hold tty lock while blocking. Since the sleep
> might take a long time we can prevent other processes from accessing the tty,
> causing hung tasks and a dead tty.
> 
> Diagnosed-by: Peter Hurley <peter@hurleysoftware.com>
> Signed-off-by: Sasha Levin <sasha.levin@oracle.com>

But then you invalidate all of the tty state tests made under
the lock at the beginning of this function, before enterring
the loop.  If you drop the lock, those pieces of state could
change.

I'm not applying this.
--
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
Sasha Levin March 3, 2013, 11:17 p.m. UTC | #2
On 03/03/2013 05:47 PM, David Miller wrote:
> From: Sasha Levin <sasha.levin@oracle.com>
> Date: Sun,  3 Mar 2013 17:35:53 -0500
> 
>> ircomm_tty_block_til_ready would hold tty lock while blocking. Since the sleep
>> might take a long time we can prevent other processes from accessing the tty,
>> causing hung tasks and a dead tty.
>>
>> Diagnosed-by: Peter Hurley <peter@hurleysoftware.com>
>> Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
> 
> But then you invalidate all of the tty state tests made under
> the lock at the beginning of this function, before enterring
> the loop.  If you drop the lock, those pieces of state could
> change.
> 
> I'm not applying this.

I'm unsure. A similar patch was applied back in 2010 that does the same thing
to a bunch of drivers, including the core tty code (e142a31da "tty: release
BTM while sleeping in block_til_ready").

This IR code looks very much like tty_port_block_til_ready() where it was
okay to do that change, so I should be the same with ircomm_tty_block_til_ready.


Thanks,
Sasha


--
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
Peter Hurley March 4, 2013, 12:04 a.m. UTC | #3
On Sun, 2013-03-03 at 17:47 -0500, David Miller wrote:
> From: Sasha Levin <sasha.levin@oracle.com>
> Date: Sun,  3 Mar 2013 17:35:53 -0500
> 
> > ircomm_tty_block_til_ready would hold tty lock while blocking. Since the sleep
> > might take a long time we can prevent other processes from accessing the tty,
> > causing hung tasks and a dead tty.
> > 
> > Diagnosed-by: Peter Hurley <peter@hurleysoftware.com>
> > Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
> 
> But then you invalidate all of the tty state tests made under
> the lock at the beginning of this function, before enterring
> the loop.  If you drop the lock, those pieces of state could
> change.

Yes, the state could change. For example, the tty could be hung up while
ircomm_tty_block_til_ready() is sleeping. Or the session leader could be
exiting and SIGHUPed this task. Or the port could have been shutdown.

All these are re-tested in the loop. What state test isn't repeated?

> I'm not applying this.

That's certainly your perogative.
But you should know this bug hangs the entire tty subsystem.

This is the correct fix and exactly how this is done by the tty port.

Regards,
Peter Hurley







--
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 Miller March 4, 2013, 12:31 a.m. UTC | #4
From: Sasha Levin <sasha.levin@oracle.com>
Date: Sun, 03 Mar 2013 18:17:38 -0500

> On 03/03/2013 05:47 PM, David Miller wrote:
>> From: Sasha Levin <sasha.levin@oracle.com>
>> Date: Sun,  3 Mar 2013 17:35:53 -0500
>> 
>>> ircomm_tty_block_til_ready would hold tty lock while blocking. Since the sleep
>>> might take a long time we can prevent other processes from accessing the tty,
>>> causing hung tasks and a dead tty.
>>>
>>> Diagnosed-by: Peter Hurley <peter@hurleysoftware.com>
>>> Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
>> 
>> But then you invalidate all of the tty state tests made under
>> the lock at the beginning of this function, before enterring
>> the loop.  If you drop the lock, those pieces of state could
>> change.
>> 
>> I'm not applying this.
> 
> I'm unsure. A similar patch was applied back in 2010 that does the same thing
> to a bunch of drivers, including the core tty code (e142a31da "tty: release
> BTM while sleeping in block_til_ready").
> 
> This IR code looks very much like tty_port_block_til_ready() where it was
> okay to do that change, so I should be the same with ircomm_tty_block_til_ready.

That assumes that the other changes don't have the same bug.

Releasing locks are dangerous, because it invalidates the context in
which all previous tests of state have been performed.  Anything can
happen to the TTY once you drop that lock.
--
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 Miller March 4, 2013, 12:33 a.m. UTC | #5
From: Peter Hurley <peter@hurleysoftware.com>
Date: Sun, 03 Mar 2013 19:04:25 -0500

> All these are re-tested in the loop. What state test isn't repeated?

One that rechecks the non-blocking filp flag, the
TTY_IO_ERROR tty flag and the termios settings.

Like I said, all of the state tests performed at the beginning of
this function, before enterring the loop.
--
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
Peter Hurley March 4, 2013, 1:06 a.m. UTC | #6
On Sun, 2013-03-03 at 19:33 -0500, David Miller wrote:
> From: Peter Hurley <peter@hurleysoftware.com>
> Date: Sun, 03 Mar 2013 19:04:25 -0500
> 
> > All these are re-tested in the loop. What state test isn't repeated?
> 
> One that rechecks the non-blocking filp flag, the
> TTY_IO_ERROR tty flag and the termios settings.
> 
> Like I said, all of the state tests performed at the beginning of
> this function, before enterring the loop.

How is O_NONBLOCK going to change? This function is sitting on the
user-space open.

The filp parameter is only on this task stack. It hasn't been linked in
anywhere else. Because of course the file isn't open yet because this
function hasn't returned success.

The TTY_IO_ERROR flag is used by drivers (this one included) to turn
away concurrent reads and writes when shutting down. The tty core does
not set this. Now this driver might set this, if commanded to hangup via
ircomm_tty_hangup, but like I said that's already handled in the loop
by testing tty_hung_up_p.

The initial termios setting cflag settings are set by the driver open().
In this driver, its here:

	driver->init_termios    = tty_std_termios;
	driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;


Now, it's possible that one could construct an imaginary race, where the
tty has already been opened and that task now sets the termios without
CLOCAL and meanwhile a second task is racing this termios setting with
an open() of its own, but since there is no expectation from userspace
that those operations are serialized, there's no reason to serialize
them here.

But regardless, this function __cannot__ sleep holding the tty_lock().

Regards,
Peter Hurley


--
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
Peter Hurley March 4, 2013, 2:23 a.m. UTC | #7
On Sun, 2013-03-03 at 17:47 -0500, David Miller wrote:
> From: Sasha Levin <sasha.levin@oracle.com>
> Date: Sun,  3 Mar 2013 17:35:53 -0500
> 
> > ircomm_tty_block_til_ready would hold tty lock while blocking. Since the sleep
> > might take a long time we can prevent other processes from accessing the tty,
> > causing hung tasks and a dead tty.
> > 
> > Diagnosed-by: Peter Hurley <peter@hurleysoftware.com>
> > Signed-off-by: Sasha Levin <sasha.levin@oracle.com>
> 
> But then you invalidate all of the tty state tests made under
> the lock at the beginning of this function, before enterring
> the loop.  If you drop the lock, those pieces of state could
> change.
> 
> I'm not applying this.

BTW, Sasha deserves a medal for finding and fixing this. Here's the
initial report [1] by him from Halloween. And he doesn't even have an IR
device.

So this fix needs to be cc'd to stable too.

Regards,
Peter Hurley


[1]

On Wed, 2012-10-31 at 16:10 -0400, Sasha Levin wrote:
On 10/31/2012 11:32 AM, Jiri Slaby wrote:
> > On 10/31/2012 04:30 PM, Sasha Levin wrote:
> >> On Wed, Oct 31, 2012 at 8:53 AM, Jiri Slaby <jslaby@suse.cz> wrote:
> >>> On 10/25/2012 08:02 PM, Sasha Levin wrote:
> >>>> Fuzzing with trinity inside a KVM tools (lkvm) guest with -next kernel
> >>>> uncovered the following warning:
> >>>
> >>> I cannot reproduce that :(. Do you still see it?
> >>
> >> Yes, it reproduces pretty easily while fuzzing.
> > 
> > What is your exact setup? I tried trinity with 100 000 syscalls inside
> > KVM with an LDEP-enabled kernel. How many serial ports do you have in
> > the guest? Any USB serials in there?
> 
> btw, I'm also seeing the following lockups, don't know if it's related:
> 
> 
> [ 2283.070569] INFO: task trinity-child20:9161 blocked for more than 120 seconds.
> [ 2283.071775] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
> [ 2283.074673] trinity-child20 D ffff8800276cb000  5424  9161   6364 0x00000000
> [ 2283.076018]  ffff880059d9da58 0000000000000002 0000000000000002 0000000000000000
> [ 2283.077393]  ffff880059d7b000 ffff880059d9dfd8 ffff880059d9dfd8 ffff880059d9dfd8
> [ 2283.078763]  ffff8800276cb000 ffff880059d7b000 ffff880059d9da78 ffff88001a095180
> [ 2283.084144] Call Trace:
> [ 2283.085039]  [<ffffffff83a98bd5>] schedule+0x55/0x60
> [ 2283.086748]  [<ffffffff83a98bf3>] schedule_preempt_disabled+0x13/0x20
> [ 2283.089000]  [<ffffffff83a9735d>] __mutex_lock_common+0x36d/0x5a0
> [ 2283.090658]  [<ffffffff83a9afb3>] ? tty_lock_nested+0x73/0x80
> [ 2283.091691]  [<ffffffff83a9afb3>] ? tty_lock_nested+0x73/0x80
> [ 2283.092779]  [<ffffffff83a975cf>] mutex_lock_nested+0x3f/0x50
> [ 2283.093875]  [<ffffffff83a9afb3>] tty_lock_nested+0x73/0x80
> [ 2283.094872]  [<ffffffff83a9afcb>] tty_lock+0xb/0x10
> [ 2283.095443]  [<ffffffff81bae880>] tty_open+0x270/0x5f0
> [ 2283.096181]  [<ffffffff8127cda8>] chrdev_open+0xf8/0x1d0
> [ 2283.097054]  [<ffffffff8127693c>] do_dentry_open+0x1fc/0x310
> [ 2283.098015]  [<ffffffff8127ccb0>] ? cdev_put+0x20/0x20
> [ 2283.098943]  [<ffffffff8127777a>] finish_open+0x4a/0x60
> [ 2283.099935]  [<ffffffff81286947>] do_last+0xb87/0xe70
> [ 2283.100910]  [<ffffffff812844b0>] ? link_path_walk+0x70/0x900
> [ 2283.101553]  [<ffffffff81286cf2>] path_openat+0xc2/0x500
> [ 2283.102282]  [<ffffffff83a9a314>] ? _raw_spin_unlock_irqrestore+0x84/0xb0
> [ 2283.103506]  [<ffffffff8128716c>] do_filp_open+0x3c/0xa0
> [ 2283.104282]  [<ffffffff81296c11>] ? __alloc_fd+0x1e1/0x200
> [ 2283.105278]  [<ffffffff81277c0c>] do_sys_open+0x11c/0x1c0
> [ 2283.106519]  [<ffffffff81277ccc>] sys_open+0x1c/0x20
> [ 2283.107241]  [<ffffffff81277d01>] sys_creat+0x11/0x20
> [ 2283.107975]  [<ffffffff83a9be18>] tracesys+0xe1/0xe6

--
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 Miller March 4, 2013, 2:36 a.m. UTC | #8
From: Peter Hurley <peter@hurleysoftware.com>
Date: Sun, 03 Mar 2013 20:06:18 -0500

> But regardless, this function __cannot__ sleep holding the tty_lock().

So drop it across the schedule(), but recheck the termios after
regrabbing it.
--
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
Peter Hurley March 4, 2013, 4:24 a.m. UTC | #9
On Sun, 2013-03-03 at 21:36 -0500, David Miller wrote:
> From: Peter Hurley <peter@hurleysoftware.com>
> Date: Sun, 03 Mar 2013 20:06:18 -0500
> 
> > But regardless, this function __cannot__ sleep holding the tty_lock().
> 
> So drop it across the schedule(), but recheck the termios after
> regrabbing it.

I'll have to do some research on that.

1) The code is using a deliberate snapshot.

	if (tty->termios.c_cflag & CLOCAL) {
		IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __func__ );
		do_clocal = 1;
	}

	.....

	while (1) {

		.........

		/*
		 * Check if link is ready now. Even if CLOCAL is
		 * specified, we cannot return before the IrCOMM link is
		 * ready
		 */
		if (!test_bit(ASYNCB_CLOSING, &port->flags) &&
		    (do_clocal || tty_port_carrier_raised(port)) &&
		    self->state == IRCOMM_TTY_READY)
		{
			break;
		}


2) The only reason this driver isn't using tty_port_block_til_ready() is
the lone state check:
		    self->state == IRCOMM_TTY_READY

I take it IRDA has some kind of virtual cabling protocol. But it's
unclear why this can't be implemented in the driver without duplicating
tty_port_block_til_ready(). For example, if the device can't do CLOCAL
open (meaning no underlying device attached prior to open) then why
specify that in the driver flags? Additionally, CLOCAL can be masked out
by the driver's set_termios() method. And then it could implement the
state check in its .carrier_raised() method.

3) The do_clocal snapshot is universally employed by every tty driver. I
don't mean that as some kind of lame excuse. But if this should change,
it should change across every tty driver with a really good reason.

4) Rechecking termios will change the way the user-space open() for this
device behaves. And I need to think more on how that might or might not
be a problem.

5) The code behavior pre-dates the 2005 check-in so I'll probably have
to do some code archaeology.


That's probably going to take some time.

In the meantime, while reviewing that code, I noticed there's a handful
of serious bugs in that one function that I'll send a patchset for.

Plus, someone could be back to me on if and why the driver needs to be
virtually cabled to open().

Regards,
Peter Hurley

--
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
Peter Hurley March 4, 2013, 4:30 a.m. UTC | #10
On Sun, 2013-03-03 at 21:36 -0500, David Miller wrote:
> From: Peter Hurley <peter@hurleysoftware.com>
> Date: Sun, 03 Mar 2013 20:06:18 -0500
> 
> > But regardless, this function __cannot__ sleep holding the tty_lock().
> 
> So drop it across the schedule(), but recheck the termios after
> regrabbing it.

I'll have to do some research on that.

1) The code is using a deliberate snapshot.

	if (tty->termios.c_cflag & CLOCAL) {
		IRDA_DEBUG(1, "%s(), doing CLOCAL!\n", __func__ );
		do_clocal = 1;
	}

	.....

	while (1) {

		.........

		/*
		 * Check if link is ready now. Even if CLOCAL is
		 * specified, we cannot return before the IrCOMM link is
		 * ready
		 */
		if (!test_bit(ASYNCB_CLOSING, &port->flags) &&
		    (do_clocal || tty_port_carrier_raised(port)) &&
		    self->state == IRCOMM_TTY_READY)
		{
			break;
		}


2) The only reason this driver isn't using tty_port_block_til_ready() is
the lone state check:
		    self->state == IRCOMM_TTY_READY

I take it IRDA has some kind of virtual cabling protocol. But it's
unclear why this can't be implemented in the driver without duplicating
tty_port_block_til_ready(). For example, if the device can't do CLOCAL
open (meaning no underlying device attached prior to open) then why
specify that in the driver flags? Additionally, CLOCAL can be masked out
by the driver's set_termios() method. And then it could implement the
state check in its .carrier_raised() method.

The net result of which would obviate the need for
ircomm_tty_block_til_ready() at all.

3) The do_clocal snapshot is universally employed by every tty driver. I
don't mean that as some kind of lame excuse. But if this should change,
it should change across every tty driver with a really good reason.

4) Rechecking termios will change the way the user-space open() for this
device behaves. And I need to think more on how that might or might not
be a problem.

5) The code behavior pre-dates the 2005 check-in so I'll probably have
to do some code archaeology.


That's probably going to take some time.

In the meantime, while reviewing that code, I noticed there's a handful
of serious bugs in that one function that I'll send a patchset for.

Plus, someone could be back to me on if and why the driver needs to be
virtually cabled to open().

Regards,
Peter Hurley


--
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
Peter Hurley March 5, 2013, 4:09 p.m. UTC | #11
On Sun, 2013-03-03 at 23:24 -0500, Peter Hurley wrote:
> On Sun, 2013-03-03 at 21:36 -0500, David Miller wrote:
> > From: Peter Hurley <peter@hurleysoftware.com>
> > Date: Sun, 03 Mar 2013 20:06:18 -0500
> >
> > > But regardless, this function __cannot__ sleep holding the tty_lock().
> >
> > So drop it across the schedule(), but recheck the termios after
> > regrabbing it.
>
> I'll have to do some research on that.

Still working on this...

> In the meantime, while reviewing that code, I noticed there's a handful
> of serious bugs in that one function that I'll send a patchset for.

As promised.


Peter Hurley (4):
  net/irda: Fix port open counts
  net/irda: Hold port lock while bumping blocked_open
  net/irda: Use barrier to set task state
  net/irda: Raise dtr in non-blocking open

 net/irda/ircomm/ircomm_tty.c | 29 ++++++++++++++++-------------
 1 file changed, 16 insertions(+), 13 deletions(-)
David Miller March 6, 2013, 4:44 a.m. UTC | #12
From: Peter Hurley <peter@hurleysoftware.com>
Date: Tue,  5 Mar 2013 11:09:03 -0500

> On Sun, 2013-03-03 at 23:24 -0500, Peter Hurley wrote:
>> In the meantime, while reviewing that code, I noticed there's a handful
>> of serious bugs in that one function that I'll send a patchset for.
> 
> As promised.
> 
> 
> Peter Hurley (4):
>   net/irda: Fix port open counts
>   net/irda: Hold port lock while bumping blocked_open
>   net/irda: Use barrier to set task state
>   net/irda: Raise dtr in non-blocking open

Series looks good, all applied, thanks Peter!
--
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
diff mbox

Patch

diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
index 9a5fd3c..7844cb3 100644
--- a/net/irda/ircomm/ircomm_tty.c
+++ b/net/irda/ircomm/ircomm_tty.c
@@ -355,7 +355,9 @@  static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self,
 		IRDA_DEBUG(1, "%s(%d):block_til_ready blocking on %s open_count=%d\n",
 		      __FILE__, __LINE__, tty->driver->name, port->count);
 
+		tty_unlock(tty);
 		schedule();
+		tty_lock(tty);
 	}
 
 	__set_current_state(TASK_RUNNING);