From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=100.103.45.18 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793211410; b=rfu9YwYrfPl2x5rWJk73z7gMtx1X+zw2Po2nlBy1WgwFc+W3GygbBQFoJwNy6sza4+Zx gCCGery0TEv/11jQHxQu/Wq9OJYMDZxT9t+taBkXbf4wUq6g/cRzgvoYhZYgdcprPNMSR nnvnnLkwcun0DwQXiIJoKnCmjbzvF+ySksOyB89AtC2Q4VKTZ0yttCqpgx3+nmo4cuCEe hEPmsvPZkFRwEMR6I8YxZei8sx72LeBCL/zlPd0JOEyjYNQdvI3jUnoQ3IY9O9WpZDdpg RPmWSw4Pe5wOMB4XHUe0xQ0G+t7rgEXr/R2ZU1RUEfDl2s97uhp85IuVBoJdhv7p74Q== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793211410; h=Received:DKIM-Signature:From:To:Cc:Subject:Date:Message-ID:X-Mailer: In-Reply-To:References:User-Agent:X-stable:X-Patchwork-Hint: MIME-Version:Content-Transfer-Encoding; bh=WAYj4j/3XJ/WIeWB6CLgyLVNDpdcxiguP5t/X0VNNNg=; b=JPVXrseSAwWgbecD5CPsIncG4Ue/4yrltqVVU7q9jIE3oAev/lDYehK1occI1hHcshAm zEuLSyvkrPwSPtgvZkb8faWtZOakwmlftUjwGW6nyidAGlBOd7e+Yl2U/eKGJ/j+LzkAg m1lKtGRZVYonAG1kwNHhuhNBg259sMROQ91xF4t0GXdeZkOj1WWYtVPdo7MrkEpw2kcDF Kzck07Ail18tfSo4z1WnnAET3iTbp+YLLvFJRhI0Eb/WTwqR6ktUQE/PcHhNkrDB9Zy7L n1WibJ/Vo7oc4vZztTa8jV364wxc4DgVtjd1dbdfBR+ObJJswgnwFn14jNxLOfrMwUg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=hh5lx45C; arc=none smtp.remote-ip=100.103.45.18 Received: from smtp.kernel.org (quasi.space.kernel.org [100.103.45.18]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 77BFF606A321; Wed, 28 Oct 2026 18:16:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.a=rsa-sha256 header.s=korg header.b=hh5lx45C Received: by smtp.kernel.org (Postfix) with ESMTPSA id C87D41F01825; Wed, 28 Oct 2026 18:16:49 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1793211410; bh=WAYj4j/3XJ/WIeWB6CLgyLVNDpdcxiguP5t/X0VNNNg=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=hh5lx45CupEvlGfNAnEMDDd7SnqQrlXAogfe5A9d6ONjUb9bgYHThK/w7yVae0qIE Fmm3b8kiR0A5RwREgssurO9ZxpuIRsqSYqx5y2qYniBmXuzxj/Eh/V0q/aU5PutDj9 jdaoDEpmk7pvnJxvAm2Gr8DIkiEqBErDiaqX3acs= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Nick Bowler , syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com, Johan Hovold , Sasha Levin Subject: [PATCH 5.15 322/571] USB: serial: fix ioctl hangup race Date: Wed, 28 Oct 2026 17:57:49 +0100 Message-ID: <20261028170015.644043564@linuxfoundation.org> X-Mailer: git-send-email 2.56.0 In-Reply-To: <20261028170008.009143722@linuxfoundation.org> References: <20261028170008.009143722@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore X-Mailing-List: patches@lists.linux.dev List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 5.15-stable review patch. If anyone has any objections, please let me know. ------------------ From: Johan Hovold [ Upstream commit ae0e61d95677efa778379853e495d748e6efbf95 ] The tty ioctls can race with hangup and end up calling into a tty driver for a device that is already gone or powered down. Serialise the callbacks using the tty port mutex and add the missing checks to make sure the port has not been hung up before accessing port driver data or hardware. This specifically avoids dereferencing a NULL-pointer if a device is disconnected during lengthy break signalling. Note that TIOCGICOUNT and TIOCMIWAIT do not access port driver data or hardware and are therefore not affected by the race. Fixes: f34d7a5b7010 ("tty: The big operations rework") Reported-by: Nick Bowler Link: https://lore.kernel.org/all/CADyTPExB2kYOOwkO0JqGhKaYVDqO9uS9WCw0J=MCTdVhcGOogA@mail.gmail.com/ Reported-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com Closes: https://lore.kernel.org/r/6a9087a4.4d659fcc.734b4.0013.GAE@google.com Cc: stable@vger.kernel.org # 2.6.26 Signed-off-by: Johan Hovold [ sashal: Adapted to void-returning break_ctl() and the older termios signature while omitting hunks for absent XR RS-485 handlers. ] Signed-off-by: Sasha Levin --- drivers/usb/serial/generic.c | 10 +++- drivers/usb/serial/usb-serial.c | 92 ++++++++++++++++++++++++++------- 2 files changed, 82 insertions(+), 20 deletions(-) diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c index 15b6dee3a8e50..64890f6d78e03 100644 --- a/drivers/usb/serial/generic.c +++ b/drivers/usb/serial/generic.c @@ -268,6 +268,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer); void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; unsigned int bps; unsigned long period; unsigned long expire; @@ -287,7 +288,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout) __func__, jiffies_to_msecs(timeout), jiffies_to_msecs(period)); expire = jiffies + timeout; - while (!port->serial->type->tx_empty(port)) { + for (;;) { + mutex_lock(&tport->mutex); + if (tty_io_error(tty) || port->serial->type->tx_empty(port)) { + mutex_unlock(&tport->mutex); + break; + } + mutex_unlock(&tport->mutex); + schedule_timeout_interruptible(period); if (signal_pending(current)) break; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 97a031033e946..ec84825d45aae 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -401,17 +401,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty) static void serial_wait_until_sent(struct tty_struct *tty, int timeout) { struct usb_serial_port *port = tty->driver_data; - struct usb_serial *serial = port->serial; dev_dbg(&port->dev, "%s\n", __func__); if (!port->serial->type->wait_until_sent) return; - mutex_lock(&serial->disc_mutex); - if (!serial->disconnected) - port->serial->type->wait_until_sent(tty, timeout); - mutex_unlock(&serial->disc_mutex); + port->serial->type->wait_until_sent(tty, timeout); } static void serial_throttle(struct tty_struct *tty) @@ -439,8 +435,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) struct usb_serial_port *port = tty->driver_data; struct tty_port *tport = &port->port; unsigned int close_delay, closing_wait; + int ret = 0; mutex_lock(&tport->mutex); + if (tty_io_error(tty)) { + ret = -EIO; + goto out_unlock; + } close_delay = jiffies_to_msecs(tport->close_delay) / 10; closing_wait = tport->closing_wait; @@ -453,10 +454,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss) if (port->serial->type->get_serial) port->serial->type->get_serial(tty, ss); - +out_unlock: mutex_unlock(&tport->mutex); - return 0; + return ret; } static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) @@ -472,6 +473,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss) closing_wait = msecs_to_jiffies(closing_wait * 10); mutex_lock(&tport->mutex); + if (tty_io_error(tty)) { + ret = -EIO; + goto out_unlock; + } if (!capable(CAP_SYS_ADMIN)) { if (close_delay != tport->close_delay || @@ -499,6 +504,7 @@ static int serial_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; int retval = -ENOIOCTLCMD; dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd); @@ -509,8 +515,21 @@ static int serial_ioctl(struct tty_struct *tty, retval = port->serial->type->tiocmiwait(tty, arg); break; default: - if (port->serial->type->ioctl) + if (!port->serial->type->ioctl) + break; + + if (cmd == TIOCSRS485) + down_write(&tty->termios_rwsem); + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + retval = -EIO; + else retval = port->serial->type->ioctl(tty, cmd, arg); + mutex_unlock(&tport->mutex); + + if (cmd == TIOCSRS485) + up_write(&tty->termios_rwsem); } return retval; @@ -519,25 +538,40 @@ static int serial_ioctl(struct tty_struct *tty, static void serial_set_termios(struct tty_struct *tty, struct ktermios *old) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->set_termios) - port->serial->type->set_termios(tty, port, old); - else + if (!port->serial->type->set_termios) { tty_termios_copy_hw(&tty->termios, old); + return; + } + + mutex_lock(&tport->mutex); + if (!tty_io_error(tty)) + port->serial->type->set_termios(tty, port, old); + mutex_unlock(&tport->mutex); } static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + int ret = 0; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->break_ctl) + if (!port->serial->type->break_ctl) + return 0; + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + ret = -EIO; + else port->serial->type->break_ctl(tty, break_state); + mutex_unlock(&tport->mutex); - return 0; + return ret; } static int serial_proc_show(struct seq_file *m, void *v) @@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v) static int serial_tiocmget(struct tty_struct *tty) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + int ret; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->tiocmget) - return port->serial->type->tiocmget(tty); - return -ENOTTY; + if (!port->serial->type->tiocmget) + return -ENOTTY; + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + ret = -EIO; + else + ret = port->serial->type->tiocmget(tty); + mutex_unlock(&tport->mutex); + + return ret; } static int serial_tiocmset(struct tty_struct *tty, unsigned int set, unsigned int clear) { struct usb_serial_port *port = tty->driver_data; + struct tty_port *tport = &port->port; + int ret; dev_dbg(&port->dev, "%s\n", __func__); - if (port->serial->type->tiocmset) - return port->serial->type->tiocmset(tty, set, clear); - return -ENOTTY; + if (!port->serial->type->tiocmset) + return -ENOTTY; + + mutex_lock(&tport->mutex); + if (tty_io_error(tty)) + ret = -EIO; + else + ret = port->serial->type->tiocmset(tty, set, clear); + mutex_unlock(&tport->mutex); + + return ret; } static int serial_get_icount(struct tty_struct *tty, -- 2.53.0