[PATCH 1/5] serial: core: fix division by zero if old termios is present but has invalid rate

From: Hugo Villeneuve

Date: Tue Oct 20 2026 - 14:44:39 EST


From: Hugo Villeneuve <hvilleneuve@xxxxxxxxxxxx>

If the old baud rate termios is present, it is implicitely assumed to be
valid. However, if base_baud is lowered via TIOCSSERIAL, and the new
max value is lower than the old baud rate, the later will become invalid,
and uart_get_baud_rate() loop will exit, and return a baud rate of zero,
causing this:

WARNING: drivers/tty/serial/serial_core.c:547 at
uart_get_baud_rate+0x104/0x180, CPU#0: uart_baud_test/782
...
Division by zero in kernel.
CPU: 0 UID: 0 PID: 782 Comm: uart_baud_test Tainted... 7.3.0-rc6-...
Tainted: [W]=WARN
Hardware name: Freescale i.MX6 Ultralite (Device Tree)
Call trace:
unwind_backtrace from show_stack+0x10/0x14
show_stack from dump_stack_lvl+0x50/0x64
dump_stack_lvl from Ldiv0_64+0x8/0x18
Ldiv0_64 from div64_u64+0xb0/0xd4
div64_u64 from uart_update_timeout+0x3c/0x44
uart_update_timeout from sc16is7xx_set_termios+0x308/0x428 [sc16is7xx]
sc16is7xx_set_termios [sc16is7xx] from uart_change_line_settings+0x34/0x1a8
uart_change_line_settings from uart_set_termios+0x98/0x13c
uart_set_termios from tty_set_termios+0x154/0x208
...

Reproducing scenario:
set serial port speed to B115200
set baud_base to 9600 via TIOCSSERIAL:
this will lower maximum allowed baud rate to 9600
call tcsetattr() with B57600 (old = B115200)
uart_get_baud_rate() will return 0

Fix by running the loop an additional iteration, so to always make sure to
return a valid (clipped) baud rate if old is present but invalid.

Update confusing comments about @termios structure update: it will be
updated only if the desired baud rate is invalid, and also if @old
contained B0.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc: stable@xxxxxxxxxxxxxxx
Reported-by: Hui Peng <benquike@xxxxxxxxx>
Link: https://lore.kernel.org/all/20260930125901.778868-1-benquike@xxxxxxxxx/
Signed-off-by: Hugo Villeneuve <hvilleneuve@xxxxxxxxxxxx>
---
drivers/tty/serial/serial_core.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index c0deec0e6af41..c3c1a1d76405d 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -465,12 +465,9 @@ EXPORT_SYMBOL(uart_update_timeout);
* magic 38400 baud rate (with spd_* flags), and mapping the %B0 rate to 9600
* baud or min argument, whichever is greater.
*
- * If the new baud rate is invalid, try the @old termios setting. If it's still
- * invalid, clip to the nearest chip supported rate.
- *
- * The @termios structure is updated to reflect the baud rate we're actually
- * going to be using. Don't do this for the case where B0 is requested ("hang
- * up").
+ * If the desired baud rate is invalid, try the @old termios setting. If @old
+ * termios is absent or if it's still invalid, clip to the nearest chip
+ * supported rate. For these cases, @termios is updated with the fallback rate.
*
* Locking: caller dependent
*/
@@ -501,7 +498,7 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
break;
}

- for (try = 0; try < 2; try++) {
+ for (try = 0; try < 3; try++) {
baud = tty_termios_baud_rate(termios);

/*
--
2.47.3