syzkaller reports a slab-use-after-free in mutex_spin_on_owner().
This occurs because a previous thread crashed during tty_open() while
holding the tty_lock, leaving the owner pointer dangling.
The initial crash is an unhandled Page Fault in mem_serial_in():
BUG: unable to handle page fault for address: ffffc90001206014
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
Oops: Oops: 0000 [#1] SMP KASAN PTI
RIP: 0010:mem_serial_in+0x67/0x90
Call Trace:
serial8250_clear_interrupts+0x40/0xf0
serial8250_do_startup+0x3f5/0x3220
serial8250_startup+0x6c/0x90
uart_startup+0x4ff/0x12c0
uart_port_activate+0xf3/0x1a0
tty_port_open+0x15c/0x200
uart_open+0x47/0x60
tty_open+0x39b/0x1310
This sequence is triggered because the TIOCSSERIAL ioctl allows a user
with CAP_SYS_ADMIN to change the io_type of a serial port to UPIO_MEM
(or other memory-mapped types) and inject an arbitrary physical address
via iomem_base.
When uart_change_port() applies these changes, it attempts to remap the
port's I/O memory based on the injected iomem_base. This can result in an
invalid or inaccessible virtual mapping for port->membase. The subsequent
call to mem_serial_in() then dereferences this bogus pointer, triggering
a fatal Page Fault.
Fix this by tightening the validation in serial8250_verify_port().
Reject any TIOCSSERIAL requests that attempt to modify the io_type
to/from memory-mapped types, or change the iomem_base of an existing
memory-mapped port. Userspace can still legitimately modify IRQs or
baud rates of MMIO ports as long as the io_type and mapbase remain
unchanged.
Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Cc:
sta...@vger.kernel.org
Signed-off-by: Mingyu Wang <
25181...@stu.xidian.edu.cn>
---
drivers/tty/serial/8250/8250_port.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c
index e94a0802cbdd..79563afe9db9 100644
--- a/drivers/tty/serial/8250/8250_port.c
+++ b/drivers/tty/serial/8250/8250_port.c
@@ -3146,6 +3146,16 @@ serial8250_verify_port(struct uart_port *port, struct serial_struct *ser)
ser->type >= ARRAY_SIZE(uart_config) || ser->type == PORT_CIRRUS ||
ser->type == PORT_STARTECH)
return -EINVAL;
+
+ if (port->iotype == UPIO_MEM || port->iotype == UPIO_MEM32 ||
+ port->iotype == UPIO_MEM32BE || port->iotype == UPIO_MEM16 ||
+ ser->io_type == UPIO_MEM || ser->io_type == UPIO_MEM32 ||
+ ser->io_type == UPIO_MEM32BE || ser->io_type == UPIO_MEM16) {
+ if (ser->io_type != port->iotype ||
+ (unsigned long)ser->iomem_base != port->mapbase)
+ return -EINVAL;
+ }
+
return 0;
}
--
2.34.1