Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

tty: Avoid dropping ldisc_mutex over hangup tty re-initialization

A couple of people have hit the WARN_ON() in drivers/char/tty_io.c,
tty_open() that is unhappy about seeing the tty line discipline go away
during the tty hangup. See for example

http://bugzilla.kernel.org/show_bug.cgi?id=14255

and the reason is that we do the tty_ldisc_halt() outside the
ldisc_mutex in order to be able to flush the scheduled work without a
deadlock with vhangup_work.

However, it turns out that we can solve this particular case by

- using "cancel_delayed_work_sync()" in tty_ldisc_halt(), which waits
for just the particular work, rather than synchronizing with any
random outstanding pending work.

This won't deadlock, since the buf.work we synchronize with doesn't
care about the ldisc_mutex, it just flushes the tty ldisc buffers.

- realize that for this particular case, we don't need to wait for any
hangup work, because we are inside the hangup codepaths ourselves.

so as a result we can just drop the flush_scheduled_work() entirely, and
then move the tty_ldisc_halt() call to inside the mutex. That way we
never expose the partially torn down ldisc state to tty_open(), and hold
the ldisc_mutex over the whole sequence.

Reported-by: Ingo Molnar <mingo@elte.hu>
Reported-by: Heinz Diehl <htd@fancy-poultry.org>
Cc: stable@kernel.org
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

+2 -5
+2 -5
drivers/char/tty_ldisc.c
··· 518 518 static int tty_ldisc_halt(struct tty_struct *tty) 519 519 { 520 520 clear_bit(TTY_LDISC, &tty->flags); 521 - return cancel_delayed_work(&tty->buf.work); 521 + return cancel_delayed_work_sync(&tty->buf.work); 522 522 } 523 523 524 524 /** ··· 756 756 * N_TTY. 757 757 */ 758 758 if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { 759 - /* Make sure the old ldisc is quiescent */ 760 - tty_ldisc_halt(tty); 761 - flush_scheduled_work(); 762 - 763 759 /* Avoid racing set_ldisc or tty_ldisc_release */ 764 760 mutex_lock(&tty->ldisc_mutex); 761 + tty_ldisc_halt(tty); 765 762 if (tty->ldisc) { /* Not yet closed */ 766 763 /* Switch back to N_TTY */ 767 764 tty_ldisc_reinit(tty);