Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1da082f7 authored by Uwe Kleine-König's avatar Uwe Kleine-König Committed by Greg Kroah-Hartman
Browse files

serial: 8250: Fix race condition in RTS-after-send handling



[ Upstream commit dedab69fd650ea74710b2e626e63fd35584ef773 ]

Set em485->active_timer = NULL isn't always enough to take out the stop
timer. While there is a check that it acts in the right state (i.e.
waiting for RTS-after-send to pass after sending some chars) but the
following might happen:

 - CPU1: some chars send, shifter becomes empty, stop tx timer armed
 - CPU0: more chars send before RTS-after-send expired
 - CPU0: shifter empty irq, port lock taken
 - CPU1: tx timer triggers, waits for port lock
 - CPU0: em485->active_timer = &em485->stop_tx_timer, hrtimer_start(),
   releases lock()
 - CPU1: get lock, see em485->active_timer == &em485->stop_tx_timer,
   tear down RTS too early

This fix bases on research done by Steffen Trumtrar.

Fixes: b86f86e8 ("serial: 8250: fix potential deadlock in rs485-mode")
Signed-off-by: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
Link: https://lore.kernel.org/r/20220215160236.344236-1-u.kleine-koenig@pengutronix.de


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 617d9c0b
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -1545,6 +1545,18 @@ static inline void start_tx_rs485(struct uart_port *port)
	if (!(up->port.rs485.flags & SER_RS485_RX_DURING_TX))
		serial8250_stop_rx(&up->port);

	/*
	 * While serial8250_em485_handle_stop_tx() is a noop if
	 * em485->active_timer != &em485->stop_tx_timer, it might happen that
	 * the timer is still armed and triggers only after the current bunch of
	 * chars is send and em485->active_timer == &em485->stop_tx_timer again.
	 * So cancel the timer. There is still a theoretical race condition if
	 * the timer is already running and only comes around to check for
	 * em485->active_timer when &em485->stop_tx_timer is armed again.
	 */
	if (em485->active_timer == &em485->stop_tx_timer)
		hrtimer_try_to_cancel(&em485->stop_tx_timer);

	em485->active_timer = NULL;

	mcr = serial8250_in_MCR(up);