Skip to content

Commit 33d4ae9

Browse files
cminyardgregkh
authored andcommitted
drivers:tty:pty: Fix a race causing data loss on close
Remove the tty_vhangup() from the pty code and just release the redirect. The tty_vhangup() results in data loss and data out of order issues. If you write to a pty master an immediately close the pty master, the receiver might get a chunk of data dropped, but then receive some later data. That's obviously something rather unexpected for a user. It certainly confused my test program. It turns out that tty_vhangup() on the slave pty gets called from pty_close(), and that causes the data on the slave side to be flushed, but due to races more data can be copied into the slave side's buffer after that. Consider the following sequence: thread1 thread2 thread3 ------- ------- ------- | |-write data into buffer, | | n_tty buffer is filled | | along with other buffers | |-pty_close(master) | |--tty_vhangup(slave) | |---tty_ldisc_hangup() | |----n_tty_flush_buffer() | |-----reset_buffer_flags() |-n_tty_read() | |--up_read(&tty->termios_rwsem); | |------down_read(&tty->termios_rwsem) | |------clear n_tty buffer contents | |------up_read(&tty->termios_rwsem) |--tty_buffer_flush_work() | |--schedules work calling | | flush_to_ldisc() | | |-flush_to_ldisc() | |--receive_buf() | |---tty_port_default_receive_buf() | |----tty_ldisc_receive_buf() | |-----n_tty_receive_buf2() | |------n_tty_receive_buf_common() | |-------down_read(&tty->termios_rwsem) | |-------__receive_buf() | | copies data into n_tty buffer | |-------up_read(&tty->termios_rwsem) |--down_read(&tty->termios_rwsem) |--copy buffer data to user >From this sequence, you can see that thread2 writes to the buffer then only clears the part of the buffer in n_tty. The n_tty receive buffer code then copies more data into the n_tty buffer. But part of the vhangup, releasing the redirect, is still required to avoid issues with consoles running on pty slaves. So do that. As far as I can tell, that is all that should be required. Signed-off-by: Corey Minyard <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent f446776 commit 33d4ae9

File tree

2 files changed

+16
-4
lines changed

2 files changed

+16
-4
lines changed

drivers/tty/pty.c

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
6666
wake_up_interruptible(&tty->link->read_wait);
6767
wake_up_interruptible(&tty->link->write_wait);
6868
if (tty->driver->subtype == PTY_TYPE_MASTER) {
69-
set_bit(TTY_OTHER_CLOSED, &tty->flags);
69+
struct file *f;
70+
7071
#ifdef CONFIG_UNIX98_PTYS
7172
if (tty->driver == ptm_driver) {
7273
mutex_lock(&devpts_mutex);
@@ -75,7 +76,17 @@ static void pty_close(struct tty_struct *tty, struct file *filp)
7576
mutex_unlock(&devpts_mutex);
7677
}
7778
#endif
78-
tty_vhangup(tty->link);
79+
80+
/*
81+
* This hack is required because a program can open a
82+
* pty and redirect a console to it, but if the pty is
83+
* closed and the console is not released, then the
84+
* slave side will never close. So release the
85+
* redirect when the master closes.
86+
*/
87+
f = tty_release_redirect(tty->link);
88+
if (f)
89+
fput(f);
7990
}
8091
}
8192

drivers/tty/tty_io.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,9 @@ EXPORT_SYMBOL_GPL(tty_wakeup);
545545
* @tty: tty device
546546
*
547547
* This is available to the pty code so if the master closes, if the
548-
* slave is a redirect it can release the redirect.
548+
* slave is a redirect it can release the redirect. It returns the
549+
* filp for the redirect, which must be fput when the operations on
550+
* the tty are completed.
549551
*/
550552
struct file *tty_release_redirect(struct tty_struct *tty)
551553
{
@@ -560,7 +562,6 @@ struct file *tty_release_redirect(struct tty_struct *tty)
560562

561563
return f;
562564
}
563-
EXPORT_SYMBOL_GPL(tty_release_redirect);
564565

565566
/**
566567
* __tty_hangup - actual handler for hangup events

0 commit comments

Comments
 (0)