tty: n_gsm: fix reset fifo race condition

commit 73029a4d71 upstream.

gsmtty_write() and gsm_dlci_data_output() properly guard the fifo access.
However, gsm_dlci_close() and gsmtty_flush_buffer() modifies the fifo but
do not guard this.
Add a guard here to prevent race conditions on parallel writes to the fifo.

Fixes: e1eaea46bb ("tty: n_gsm line discipline")
Cc: stable@vger.kernel.org
Signed-off-by: Daniel Starke <daniel.starke@siemens.com>
Link: https://lore.kernel.org/r/20220414094225.4527-17-daniel.starke@siemens.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Daniel Starke 2022-04-14 02:42:22 -07:00 committed by Greg Kroah-Hartman
parent f92783b756
commit e0a84ae1c6

View file

@ -1417,13 +1417,17 @@ static int gsm_control_wait(struct gsm_mux *gsm, struct gsm_control *control)
static void gsm_dlci_close(struct gsm_dlci *dlci) static void gsm_dlci_close(struct gsm_dlci *dlci)
{ {
unsigned long flags;
del_timer(&dlci->t1); del_timer(&dlci->t1);
if (debug & 8) if (debug & 8)
pr_debug("DLCI %d goes closed.\n", dlci->addr); pr_debug("DLCI %d goes closed.\n", dlci->addr);
dlci->state = DLCI_CLOSED; dlci->state = DLCI_CLOSED;
if (dlci->addr != 0) { if (dlci->addr != 0) {
tty_port_tty_hangup(&dlci->port, false); tty_port_tty_hangup(&dlci->port, false);
spin_lock_irqsave(&dlci->lock, flags);
kfifo_reset(&dlci->fifo); kfifo_reset(&dlci->fifo);
spin_unlock_irqrestore(&dlci->lock, flags);
/* Ensure that gsmtty_open() can return. */ /* Ensure that gsmtty_open() can return. */
tty_port_set_initialized(&dlci->port, 0); tty_port_set_initialized(&dlci->port, 0);
wake_up_interruptible(&dlci->port.open_wait); wake_up_interruptible(&dlci->port.open_wait);
@ -3078,13 +3082,17 @@ static unsigned int gsmtty_chars_in_buffer(struct tty_struct *tty)
static void gsmtty_flush_buffer(struct tty_struct *tty) static void gsmtty_flush_buffer(struct tty_struct *tty)
{ {
struct gsm_dlci *dlci = tty->driver_data; struct gsm_dlci *dlci = tty->driver_data;
unsigned long flags;
if (dlci->state == DLCI_CLOSED) if (dlci->state == DLCI_CLOSED)
return; return;
/* Caution needed: If we implement reliable transport classes /* Caution needed: If we implement reliable transport classes
then the data being transmitted can't simply be junked once then the data being transmitted can't simply be junked once
it has first hit the stack. Until then we can just blow it it has first hit the stack. Until then we can just blow it
away */ away */
spin_lock_irqsave(&dlci->lock, flags);
kfifo_reset(&dlci->fifo); kfifo_reset(&dlci->fifo);
spin_unlock_irqrestore(&dlci->lock, flags);
/* Need to unhook this DLCI from the transmit queue logic */ /* Need to unhook this DLCI from the transmit queue logic */
} }