Linux Serial subsystem development
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Maciej W. Rozycki" <macro@orcam.me.uk>
Cc: linux-serial@vger.kernel.org
Subject: Re: [PATCH v3 2/2] serial: core: Don't clobber the baud rate on hangup via B0
Date: Tue, 03 Nov 2026 22:22:44 +0000	[thread overview]
Message-ID: <sashiko-outbox-196102@kernel.org> (raw)
In-Reply-To: <alpine.DEB.2.21.2611032050590.15685@angie.orcam.me.uk>

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [High] serial: core: out-of-range hup_speed on B0 in uart_get_baud_rate_save() returns 0
- [Medium] serial: dz: dz_set_termios() saves unvalidated baud in hup_speed breaking B0
- [Low] serial: docs: stale uart_get_baud_rate kernel-doc identifier in driver.rst
--

commit 73f85543b84c52391e1ff13d5519c702bc72e894
Author: Maciej W. Rozycki <macro@orcam.me.uk>

serial: core: Don't clobber the baud rate on hangup via B0

This commit saves the most recently selected baud rate in port->hup_speed
and uses it instead of max(min, 9600) when hangup is requested via B0. It
also renames uart_get_baud_rate() to uart_get_baud_rate_save() with a save
parameter so serial8250_console_restore() can avoid modifying
port->hup_speed without holding port->mutex.

> diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
> index 012f22cf3a60e..18d42491e86d2 100644
> --- a/drivers/tty/serial/serial_core.c
> +++ b/drivers/tty/serial/serial_core.c
> @@ -458,29 +458,32 @@ uart_update_timeout(struct uart_port *port, unsigned int cflag,
>  EXPORT_SYMBOL(uart_update_timeout);
>  
>  /**
> - * uart_get_baud_rate - return baud rate for a particular port
> + * uart_get_baud_rate_save - return baud rate for a particular port

[Severity: Low]
Does Documentation/driver-api/serial/driver.rst also need to be updated for
this rename? It still lists uart_get_baud_rate under the kernel-doc
identifiers for drivers/tty/serial/serial_core.c:

Documentation/driver-api/serial/driver.rst:
.. kernel-doc:: drivers/tty/serial/serial_core.c
   :identifiers: uart_update_timeout uart_get_baud_rate uart_get_divisor

which causes a kernel-doc warning when building the documentation and omits
both uart_get_baud_rate_save() and the new static inline
uart_get_baud_rate() in include/linux/serial_core.h.

[ ... ]

> @@ -519,10 +522,14 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
>  		 * Special case: B0 rate.
>  		 */
>  		if (baud == 0)
> -			baud = max(min, 9600);
> +			baud = (port->hup_speed ?
> +				port->hup_speed : max(min, 9600));
>  
> -		if (baud >= min && baud <= max)
> +		if (baud >= min && baud <= max) {
> +			if (save)
> +				port->hup_speed = baud;
>  			return baud;
> +		}

[Severity: High]
What happens in uart_get_baud_rate_save() when B0 is requested and
port->hup_speed is outside the [min, max] range passed by the caller?

Several 8250 drivers dynamically adjust port->uartclk from
tty_termios_baud_rate(termios) before calling serial8250_do_set_termios() or
dw8250_do_set_termios(). For example, in drivers/tty/serial/8250/8250_mid.c:

mid8250_set_termios()
    unsigned int baud = tty_termios_baud_rate(termios);
    ...
    fuart = baud * 16;
    ...
    /* Gracefully handle the B0 case: fall back to B9600 */
    fuart = fuart ? fuart : 9600 * 16;
    ...
    p->uartclk = fuart;
    serial8250_do_set_termios(p, termios, old);

Similar logic exists in byt_set_termios() in
drivers/tty/serial/8250/8250_lpss.c and dw8250_set_termios() in
drivers/tty/serial/8250/8250_dw.c. In addition, port->uartclk or
UPF_MAGIC_MULTIPLIER can be changed via uart_set_info() or
serial8250_update_uartclk().

If a high baud rate (such as 3000000 on Tangier/Penwell in
mid8250_set_termios() or 5000000 on BayTrail in byt_set_termios()) is set first,
that speed is saved in port->hup_speed. When B0 is subsequently requested via
uart_set_termios(), port->uartclk is lowered for the B0 call, reducing max in
serial8250_get_baud_rate() below both port->hup_speed and the speed recorded
in old:

1. On try == 0, baud == 0 is replaced with port->hup_speed, which exceeds max.
   Execution falls through, clears CBAUD in termios, encodes old's high baud
   rate into termios (overwriting B0), sets old = NULL, and continues.
2. On try == 1, baud from old still exceeds max. Because old is now NULL, it
   clips baud to max - 1, encodes that into termios, exits the loop, triggers
   WARN_ON(1), and returns 0.

Returning 0 from uart_get_baud_rate_save() leads to a divide-by-zero in
callers such as serial8250_get_divisor(), dw8250_get_divisor(), and
uart_update_timeout(). Furthermore, because B0 in termios->c_cflag was
overwritten with a non-zero speed, uart_set_termios() in
drivers/tty/serial/serial_core.c no longer sees (cflag & CBAUD) == B0:

uart_set_termios()
    /* Handle transition to B0 status */
    if (((old_termios->c_cflag & CBAUD) != B0) && ((cflag & CBAUD) == B0))
        uart_clear_mctrl(uport, TIOCM_RTS | TIOCM_DTR);

so RTS and DTR are not dropped on hangup.

A related issue occurs in mtk8250_set_termios() in
drivers/tty/serial/8250/8250_mtk.c, which calls serial8250_do_set_termios()
with old == NULL (where max is (port->uartclk + tolerance) / 16) before
calling uart_get_baud_rate() with max = port->uartclk:

mtk8250_set_termios()
    baud = tty_termios_baud_rate(termios);
    serial8250_do_set_termios(port, termios, NULL);
    tty_termios_encode_baud_rate(termios, baud, baud);
    baud = uart_get_baud_rate(port, termios, old,
                              port->uartclk / 16 / UART_DIV_MAX,
                              port->uartclk);

When a high-speed rate above (port->uartclk + tolerance) / 16 is saved in
port->hup_speed and B0 is later requested, the first call inside
serial8250_do_set_termios() sees port->hup_speed > max with old == NULL and
overwrites port->hup_speed with the clipped rate (port->uartclk + tolerance) /
16 - 1 before mtk8250_set_termios() reaches its second uart_get_baud_rate()
call.

Should the B0 fallback clamp port->hup_speed to [min, max] (and avoid saving
back a clamped rate over port->hup_speed when baud == 0)?

[Severity: Medium]
Can saving port->hup_speed inside uart_get_baud_rate_save() also break B0
hangup in drivers/tty/serial/dz.c when an unsupported baud rate within
[50, 9600] is requested?

In dz_set_termios():

drivers/tty/serial/dz.c:dz_set_termios() {
    ...
    baud = uart_get_baud_rate(uport, termios, old_termios, 50, 9600);
    bflag = dz_encode_baud_rate(baud);
    if (bflag < 0) {
        if (old_termios) {
            /* Keep unchanged. */
            baud = tty_termios_baud_rate(old_termios);
            bflag = dz_encode_baud_rate(baud);
        }
        if (bflag < 0) {        /* Resort to 9600.  */
            baud = 9600;
            bflag = DZ_B9600;
        }
        tty_termios_encode_baud_rate(termios, baud, baud);
    }
    ...
}

If userspace requests a baud rate in [50, 9600] that dz_encode_baud_rate()
rejects (such as B200), uart_get_baud_rate() saves 200 into uport->hup_speed
before dz_encode_baud_rate() returns -1 and falls back to old_termios or 9600.

When B0 is later requested to hang up, uart_get_baud_rate() returns
uport->hup_speed (200), dz_encode_baud_rate(200) fails again, and the fallback
block calls tty_termios_encode_baud_rate(termios, baud, baud) with the
non-zero fallback rate. That overwrites B0 in termios->c_cflag, preventing
uart_set_termios() from clearing TIOCM_RTS | TIOCM_DTR.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/alpine.DEB.2.21.2611030057510.15685@angie.orcam.me.uk?part=2

  reply	other threads:[~2026-11-03 22:22 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-03 22:07 [PATCH v3 0/2] serial: core: Don't clobber the baud rate on hangup via B0 Maciej W. Rozycki
2026-11-03 22:07 ` [PATCH v3 1/2] serial: core: Unify uart_get_baud_rate() success return paths Maciej W. Rozycki
2026-11-03 22:23   ` sashiko-bot
2026-11-04 11:11   ` Andy Shevchenko
2026-11-03 22:07 ` [PATCH v3 2/2] serial: core: Don't clobber the baud rate on hangup via B0 Maciej W. Rozycki
2026-11-03 22:22   ` sashiko-bot [this message]
2026-11-04 11:10   ` Andy Shevchenko
2026-11-04 11:28     ` Johan Hovold
2026-11-04 11:50       ` Greg Kroah-Hartman
2026-11-04 11:52       ` Andy Shevchenko

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=sashiko-outbox-196102@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-serial@vger.kernel.org \
    --cc=macro@orcam.me.uk \
    --cc=sashiko-reviews@lists.linux.dev \
    /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