mirror of
https://github.com/torvalds/linux.git
synced 2026-10-26 20:46:02 +01:00
tty: abort break signalling on hangup
The break ioctls can race with hangup and end up calling into a tty
driver for a device that is already gone or powered down.
Drivers must handle this race, but TCSBRK and TCSBRKP should still be
aborted to avoid calling back into the driver after a user-controlled
timeout (possibly even after the tty has been reopened).
Note that drivers should disable any break state on shutdown so just
return on hangup (calling break_ctl() again is racy and will most likely
fail for drivers handling the race).
Fixes: f34d7a5b70 ("tty: The big operations rework")
Cc: stable <stable@kernel.org>
Signed-off-by: Johan Hovold <johan@kernel.org>
Link: https://patch.msgid.link/20260903163736.1499280-1-johan@kernel.org
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
5b114bfc1d
commit
845a2737e0
|
|
@ -620,6 +620,8 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
|
|||
|
||||
tty_ldisc_hangup(tty, cons_filp != NULL);
|
||||
|
||||
wake_up_interruptible(&tty->break_wait);
|
||||
|
||||
spin_lock_irq(&tty->ctrl.lock);
|
||||
clear_bit(TTY_THROTTLED, &tty->flags);
|
||||
clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
|
||||
|
|
@ -2431,6 +2433,7 @@ static int tiocgetd(struct tty_struct *tty, int __user *p)
|
|||
* send_break - performed time break
|
||||
* @tty: device to break on
|
||||
* @duration: timeout in mS
|
||||
* @file: file object
|
||||
*
|
||||
* Perform a timed break on hardware that lacks its own driver level timed
|
||||
* break functionality.
|
||||
|
|
@ -2438,8 +2441,9 @@ static int tiocgetd(struct tty_struct *tty, int __user *p)
|
|||
* Locking:
|
||||
* @tty->atomic_write_lock serializes
|
||||
*/
|
||||
static int send_break(struct tty_struct *tty, unsigned int duration)
|
||||
static int send_break(struct file *file, struct tty_struct *tty, unsigned int duration)
|
||||
{
|
||||
long timeout;
|
||||
int retval;
|
||||
|
||||
if (tty->ops->break_ctl == NULL)
|
||||
|
|
@ -2453,13 +2457,26 @@ static int send_break(struct tty_struct *tty, unsigned int duration)
|
|||
return -EINTR;
|
||||
|
||||
retval = tty->ops->break_ctl(tty, -1);
|
||||
if (!retval) {
|
||||
msleep_interruptible(duration);
|
||||
retval = tty->ops->break_ctl(tty, 0);
|
||||
} else if (retval == -EOPNOTSUPP) {
|
||||
/* some drivers can tell only dynamically */
|
||||
retval = 0;
|
||||
if (retval) {
|
||||
if (retval == -EOPNOTSUPP) {
|
||||
/* some drivers can tell only dynamically */
|
||||
retval = 0;
|
||||
}
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
timeout = msecs_to_jiffies(duration);
|
||||
timeout = wait_event_interruptible_timeout(tty->break_wait,
|
||||
tty_hung_up_p(file),
|
||||
timeout);
|
||||
/* return early on hangup only */
|
||||
if (timeout > 0) {
|
||||
retval = -EIO;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
retval = tty->ops->break_ctl(tty, 0);
|
||||
out_unlock:
|
||||
tty_write_unlock(tty);
|
||||
|
||||
if (signal_pending(current))
|
||||
|
|
@ -2727,10 +2744,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|||
* This is used by the tcdrain() termios function.
|
||||
*/
|
||||
if (!arg)
|
||||
return send_break(tty, 250);
|
||||
return send_break(file, tty, 250);
|
||||
return 0;
|
||||
case TCSBRKP: /* support for POSIX tcsendbreak() */
|
||||
return send_break(tty, arg ? arg*100 : 250);
|
||||
return send_break(file, tty, arg ? arg * 100 : 250);
|
||||
|
||||
case TIOCMGET:
|
||||
return tty_tiocmget(tty, p);
|
||||
|
|
@ -3090,6 +3107,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
|
|||
init_ldsem(&tty->ldisc_sem);
|
||||
init_waitqueue_head(&tty->write_wait);
|
||||
init_waitqueue_head(&tty->read_wait);
|
||||
init_waitqueue_head(&tty->break_wait);
|
||||
INIT_WORK(&tty->hangup_work, do_tty_hangup);
|
||||
mutex_init(&tty->atomic_write_lock);
|
||||
spin_lock_init(&tty->ctrl.lock);
|
||||
|
|
|
|||
|
|
@ -230,6 +230,7 @@ struct tty_struct {
|
|||
struct fasync_struct *fasync;
|
||||
wait_queue_head_t write_wait;
|
||||
wait_queue_head_t read_wait;
|
||||
wait_queue_head_t break_wait;
|
||||
struct work_struct hangup_work;
|
||||
void *disc_data;
|
||||
void *driver_data;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user