[PATCH 1/2] serdev: fix race between tty-port unregister and in-flight callbacks
Greg Kroah-Hartman <[email protected]> Fri, 31 Jul 2026 10:06:09 +0200
| Newsgroups | org.kernel.vger.linux-serial,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Joshua Rogers <[email protected]> serdev_tty_port_unregister() clears port->client_data and frees the controller without synchronizing with in-flight flip buffer work. This can cause NULL pointer dereferences or use-after-free if ttyport_receive_buf() or ttyport_write_wakeup() runs concurrently. Add cancel_work_sync() to drain pending buffer work before clearing state, and add NULL checks for client_data in both callbacks as secondary hardening. Assisted-by: AISLE:Snapshot Cc: stable <[email protected]> Signed-off-by: Joshua Rogers <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> --- drivers/tty/serdev/serdev-ttyport.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serdev/serdev-ttyport.c b/drivers/tty/serdev/serdev-ttyport.c index bab1b143b8a6..48ce5b3f8308 100644 --- a/drivers/tty/serdev/serdev-ttyport.c +++ b/drivers/tty/serdev/serdev-ttyport.c @@ -26,9 +26,14 @@ static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp, const u8 *fp, size_t count) { struct serdev_controller *ctrl = port->client_data; - struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct serport *serport; size_t ret; + if (!ctrl) + return 0; + + serport = serdev_controller_get_drvdata(ctrl); + if (!test_bit(SERPORT_ACTIVE, &serport->flags)) return 0; @@ -46,9 +51,14 @@ static size_t ttyport_receive_buf(struct tty_port *port, const u8 *cp, static void ttyport_write_wakeup(struct tty_port *port) { struct serdev_controller *ctrl = port->client_data; - struct serport *serport = serdev_controller_get_drvdata(ctrl); + struct serport *serport; struct tty_struct *tty; + if (!ctrl) + return; + + serport = serdev_controller_get_drvdata(ctrl); + tty = tty_port_tty_get(port); if (!tty) return; @@ -312,6 +322,7 @@ int serdev_tty_port_unregister(struct tty_port *port) return -ENODEV; serdev_controller_remove(ctrl); + cancel_work_sync(&port->buf.work); port->client_data = NULL; port->client_ops = &tty_port_default_client_ops; serdev_controller_put(ctrl); -- 2.55.0