From: sashiko-bot@kernel.org
To: "Tapio Reijonen" <tapio.reijonen@vaisala.com>
Cc: linux-serial@vger.kernel.org
Subject: Re: [PATCH v6 8/9] serial: max310x: drive RTS in software when hardware delays are too short
Date: Sun, 04 Oct 2026 12:31:37 +0000 [thread overview]
Message-ID: <20261004123137.B826B1F000FF@smtp.kernel.org> (raw)
In-Reply-To: <20261004-max310x-rs485-sw-delay-v6-8-3a0ef13ed9e3@vaisala.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] serial: max310x: start_tx() unconditionally rewinds active RTS envelope
- [High] serial: max310x: UAF due to timer resurrection bypassing tx_teardown interlock
- [Medium] serial: max310x: TOCTOU race in RTS control leaves pin permanently asserted
--
commit 0a0721a468435345830de47dd7875adbf5a41cb4
Author: Tapio Reijonen <tapio.reijonen@vaisala.com>
serial: max310x: drive RTS in software when hardware delays are too short
This commit adds a software-driven RTS path for the max310x driver to handle
requested RTS delays that exceed what the hardware can natively time. It
uses an hrtimer and workqueues to toggle the RTS line and control transmission
timing.
> diff --git a/drivers/tty/serial/max310x.c b/drivers/tty/serial/max310x.c
> --- a/drivers/tty/serial/max310x.c
> +++ b/drivers/tty/serial/max310x.c
[ ... ]
> +static void max310x_rts_work_proc(struct work_struct *ws)
> +{
> + struct max310x_one *one = container_of(ws, struct max310x_one, rts_work);
> + struct uart_port *port = &one->port;
> + bool rts_on = READ_ONCE(one->tx_state) != MAX310X_TX_OFF ||
> + READ_ONCE(one->tx_break);
> +
> + max310x_rts_ctl(port, max310x_rts_level(port, rts_on));
[Severity: Medium]
Is there a race condition in this lockless state check that could leave the
RTS pin permanently asserted?
If max310x_rts_work_proc executes concurrently with an rs485_config ioctl
that disables RS485, max310x_rs485_config clears tx_state to MAX310X_TX_OFF
but does not cancel any already-queued rts_work.
Could an already-queued rts_work evaluate rts_on based on an older tx_state,
or mistakenly compute an active level because SER_RS485_RTS_AFTER_SEND flags
still linger, and then overwrite the register?
Since RS485 is disabled, this would leave the transceiver permanently driving
the bus.
[ ... ]
> +static void max310x_delayed_start_tx(struct uart_port *port)
> +{
> + struct max310x_one *one = to_max310x_port(port);
> +
> + WRITE_ONCE(one->tx_state, MAX310X_TX_WAIT_BEFORE_SEND);
> + one->cancel_tx_delay_tmr = false;
> + schedule_work(&one->rts_work);
> +}
> +
> +/* called with port.lock taken and irqs off */
> static void max310x_start_tx(struct uart_port *port)
> {
> struct max310x_one *one = to_max310x_port(port);
[ ... ]
> + if (unlikely(res == -1)) {
> + one->cancel_tx_delay_tmr = true;
> + uart_port_unlock(port);
> + hrtimer_cancel(&one->tx_delay_tmr);
> + uart_port_lock(port);
> + /*
> + * The lock was dropped: a teardown may have run to
> + * completion meanwhile. Re-check before starting.
> + */
> + if (one->tx_teardown)
> + return;
> + }
> +
> + max310x_delayed_start_tx(port);
> + } else {
> - schedule_work(&one->tx_work);
> + schedule_work(&one->tx_work);
> + }
> }
[Severity: High]
Does this unconditionally rewind an active RTS envelope?
When new data is written to the UART port ring buffer during an active
transmission, max310x_start_tx is called by the serial core while tx_state
is already MAX310X_TX_SEND.
By calling max310x_delayed_start_tx unconditionally, the state is forced
back to MAX310X_TX_WAIT_BEFORE_SEND. The scheduled rts_work will then
incorrectly re-arm tx_delay_tmr with delay_rts_before_send, even though
data is already shifting out.
Because the state is falsely set to MAX310X_TX_WAIT_BEFORE_SEND, the
TX-empty interrupt handler's attempt to arm the after-send hold via
max310x_delayed_stop_tx will abort.
Does start_tx need to check if the envelope is already active in the
MAX310X_TX_SEND phase, and simply schedule tx_work instead of resetting
the envelope?
[ ... ]
> +static bool max310x_adopt_hw_tx(struct max310x_one *one)
> +{
> + struct uart_port *port = &one->port;
> +
> + if (!max310x_port_read(port, MAX310X_TXFIFOLVL_REG) &&
> + kfifo_is_empty(&port->state->port.xmit_fifo))
> + return false;
> +
> + scoped_guard(spinlock_irqsave, &port->lock) {
> + if (READ_ONCE(one->tx_state) != MAX310X_TX_OFF)
> + return true;
> + WRITE_ONCE(one->tx_state, MAX310X_TX_SEND);
> + one->cancel_tx_delay_tmr = false;
> + }
> +
> + return true;
> +}
[Severity: High]
Does this logic introduce a use-after-free risk by resurrecting the timer
after the port has been shut down?
If a concurrent ioctl queues rs_work right after max310x_remove cancels it:
max310x_remove()
cancel_work_sync(&s->p[i].rs_work);
Since the UART port is unregistered after these cancellations, a concurrent
TIOCSRS485 operation can still queue rs_work. If this delayed rs_work
executes after max310x_shutdown sets the teardown interlocks, it will call
max310x_adopt_hw_tx.
Here, max310x_adopt_hw_tx unconditionally sets cancel_tx_delay_tmr to false
without checking if tx_teardown is true.
Could this arm tx_delay_tmr on the device structure after it has been shut
down, causing a use-after-free when the timer fires on the freed structure?
This problem wasn't introduced by this patch, as the sub-optimal teardown
ordering in max310x_remove is pre-existing, but this bypass of the teardown
interlock in max310x_adopt_hw_tx appears to create a new crash vector.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261004-max310x-rs485-sw-delay-v6-0-3a0ef13ed9e3@vaisala.com?part=8
next prev parent reply other threads:[~2026-10-04 12:31 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-04 12:16 [PATCH v6 0/9] serial: max310x: RS485 delay and RTS fixes, software-timed delays Tapio Reijonen
2026-10-04 12:16 ` [PATCH v6 1/9] serial: max310x: don't clobber the TX break bit in set_termios Tapio Reijonen
2026-10-04 12:23 ` sashiko-bot
2026-10-05 15:45 ` Hugo Villeneuve
2026-10-04 12:16 ` [PATCH v6 2/9] serial: max310x: assert the transceiver during a break Tapio Reijonen
2026-10-04 12:33 ` sashiko-bot
2026-10-04 12:16 ` [PATCH v6 3/9] serial: max310x: centralize the RS485 transceiver programming Tapio Reijonen
2026-10-04 12:24 ` sashiko-bot
2026-10-04 12:16 ` [PATCH v6 4/9] serial: max310x: convert RS485 delays from milliseconds to bit-times Tapio Reijonen
2026-10-04 12:30 ` sashiko-bot
2026-10-04 12:16 ` [PATCH v6 5/9] serial: max310x: wait for TX to drain before powering down in shutdown Tapio Reijonen
2026-10-04 12:28 ` sashiko-bot
2026-10-05 10:12 ` Maarten Brock
2026-10-05 10:51 ` Tapio Reijonen
2026-10-04 12:16 ` [PATCH v6 6/9] serial: max310x: support active-low RTS on the hardware path Tapio Reijonen
2026-10-04 12:25 ` sashiko-bot
2026-10-04 12:16 ` [PATCH v6 7/9] serial: max310x: schedule tx_work directly from the IRQ handler Tapio Reijonen
2026-10-04 12:24 ` sashiko-bot
2026-10-04 12:16 ` [PATCH v6 8/9] serial: max310x: drive RTS in software when hardware delays are too short Tapio Reijonen
2026-10-04 12:31 ` sashiko-bot [this message]
2026-10-04 12:16 ` [PATCH v6 9/9] serial: max310x: don't transmit while an RS485 reconfigure is pending Tapio Reijonen
2026-10-04 12:31 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261004123137.B826B1F000FF@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=linux-serial@vger.kernel.org \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tapio.reijonen@vaisala.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox