* [PATCH v2 0/2] tty: fix break race
@ 2026-09-03 4:24 Adriano Cordova
2026-09-03 4:24 ` [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference Adriano Cordova
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Adriano Cordova @ 2026-09-03 4:24 UTC (permalink / raw)
To: gregkh
Cc: johan, jirislaby, linux-usb, linux-serial, Adriano Cordova,
syzbot+473d7477c523b41d4046
A timed break (TCSBRK/TCSBRKP) and the unconditional TIOCSBRK/TIOCCBRK
can race against USB disconnect and call break_ctl() after the device is
gone, dereferencing freed port data in keyspan_break_ctl().
Both patches are needed: the tty core must not issue break_ctl() to a
hung-up tty, and serial_break() must not call the driver after
disconnect. This is the same two-layer protection read and write already
use.
Changes since v1:
- v1 https://lore.kernel.org/stable/20260903022452.111316-2-adrianox@gmail.com/T/#t
- Proper check on the tty side, plus check on the USB side.
- Tested syzbot
Reported-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com
Link: https://syzkaller.appspot.com/bug?extid=473d7477c523b41d4046
Tested-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com
Adriano Cordova (2):
USB: serial: fix break_ctl() NULL-pointer dereference
tty: fix break race
drivers/tty/tty_io.c | 19 +++++++++++++++----
drivers/usb/serial/usb-serial.c | 3 +++
2 files changed, 18 insertions(+), 4 deletions(-)
--
2.51.0
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference 2026-09-03 4:24 [PATCH v2 0/2] tty: fix break race Adriano Cordova @ 2026-09-03 4:24 ` Adriano Cordova 2026-09-03 4:41 ` sashiko-bot 2026-09-03 4:24 ` [PATCH v2 2/2] tty: fix break race Adriano Cordova 2026-09-03 6:22 ` [PATCH v2 0/2] " Johan Hovold 2 siblings, 1 reply; 6+ messages in thread From: Adriano Cordova @ 2026-09-03 4:24 UTC (permalink / raw) To: gregkh Cc: johan, jirislaby, linux-usb, linux-serial, Adriano Cordova, syzbot+473d7477c523b41d4046, stable Don't call into the driver after disconnect, just like serial_write() already does. Fixes: 9e98966c7bb9 ("tty: rework break handling") Reported-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=473d7477c523b41d4046 Tested-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Adriano Cordova <adrianox@gmail.com> --- drivers/usb/serial/usb-serial.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 17edc057a311..5f42bfe39212 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -532,6 +532,9 @@ static int serial_break(struct tty_struct *tty, int break_state) { struct usb_serial_port *port = tty->driver_data; + if (port->serial->dev->state == USB_STATE_NOTATTACHED) + return -ENODEV; + dev_dbg(&port->dev, "%s\n", __func__); if (port->serial->type->break_ctl) -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference 2026-09-03 4:24 ` [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference Adriano Cordova @ 2026-09-03 4:41 ` sashiko-bot 0 siblings, 0 replies; 6+ messages in thread From: sashiko-bot @ 2026-09-03 4:41 UTC (permalink / raw) To: Adriano Cordova; +Cc: linux-serial Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: Pre-existing issues: - [High] This problem wasn't introduced by this patch, but the patch's attempt to fix it is flawed. The newly added lockless state check of USB_STATE_NOTATTACHED in `serial_break` leaves a Time-of-Check to Time-of-Use (TOCTOU) race window, failing to completely prevent Use-After-Free or NULL-pointer dereference vulnerabilities upon concurrent device disconnect. - [High] This problem wasn't introduced by this patch, but while the patch aims to prevent calling into the driver after disconnect, it completely omits other TTY operations in the generic usb-serial driver, leaving them vulnerable to the same Use-After-Free crashes. -- commit 35b2c0cc5ee711aab91cfbffe24f9565620f8f65 Author: Adriano Cordova <adrianox@gmail.com> USB: serial: fix break_ctl() NULL-pointer dereference This commit attempts to fix a NULL-pointer dereference by checking if the USB device is detached before calling into the sub-driver's break_ctl callback. Link: https://syzkaller.appspot.com/bug?extid=473d7477c523b41d4046 > diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c > --- a/drivers/usb/serial/usb-serial.c > +++ b/drivers/usb/serial/usb-serial.c > @@ -532,6 +532,9 @@ static int serial_break(struct tty_struct *tty, int break_state) > { > struct usb_serial_port *port = tty->driver_data; > > + if (port->serial->dev->state == USB_STATE_NOTATTACHED) > + return -ENODEV; > + [Severity: High] This isn't a bug introduced by this patch, but does this lockless check leave a Time-of-Check to Time-of-Use (TOCTOU) race window? If the thread executing serial_break() in drivers/usb/serial/usb-serial.c evaluates USB_STATE_NOTATTACHED to false and gets preempted, could a concurrent usb_disconnect() finish port_remove() and free driver-private data before the thread resumes? > dev_dbg(&port->dev, "%s\n", __func__); > > if (port->serial->type->break_ctl) > return port->serial->type->break_ctl(tty, break_state); [Severity: High] This is a pre-existing issue, but does this still execute the sub-driver's break_ctl() function on freed memory or a NULL pointer if the race condition described above triggers during a concurrent TIOCSBRK or TIOCCBRK ioctl? Additionally, this is a pre-existing issue, but while this patch aims to prevent calling into the driver after disconnect in serial_break(), what protects other TTY operations in the generic usb-serial driver from the same Use-After-Free crashes? For example, serial_tiocmget() unconditionally calls into sub-drivers without checking for disconnect: drivers/usb/serial/usb-serial.c:serial_tiocmget() { ... if (port->serial->type->tiocmget) return port->serial->type->tiocmget(tty); ... } Similar unprotected calls appear to happen in serial_ioctl(), serial_tiocmset(), serial_throttle(), and serial_unthrottle(). Can these also be triggered by an unprivileged user spamming concurrent TTY ioctls (like TIOCMGET) while the physical device is unplugged? -- Sashiko AI review · https://sashiko.dev/#/patchset/20260903042429.130320-1-adrianox@gmail.com?part=1 ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2 2/2] tty: fix break race 2026-09-03 4:24 [PATCH v2 0/2] tty: fix break race Adriano Cordova 2026-09-03 4:24 ` [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference Adriano Cordova @ 2026-09-03 4:24 ` Adriano Cordova 2026-09-03 4:40 ` sashiko-bot 2026-09-03 6:22 ` [PATCH v2 0/2] " Johan Hovold 2 siblings, 1 reply; 6+ messages in thread From: Adriano Cordova @ 2026-09-03 4:24 UTC (permalink / raw) To: gregkh Cc: johan, jirislaby, linux-usb, linux-serial, Adriano Cordova, syzbot+473d7477c523b41d4046, stable A timed break (TCSBRK and TCSBRKP) can race against hangup and call break_ctl() on a tty whose driver data is gone, causing a use-after-free or NULL-pointer dereference in the driver. Check the tty hung-up state before each break_ctl() call, including the unconditional TIOCSBRK/TIOCCBRK paths, so the driver is no longer called after hangup. Suggested-by: Johan Hovold <johan@kernel.org> Reported-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com Link: https://syzkaller.appspot.com/bug?extid=473d7477c523b41d4046 Tested-by: syzbot+473d7477c523b41d4046@syzkaller.appspotmail.com Cc: stable@vger.kernel.org Signed-off-by: Adriano Cordova <adrianox@gmail.com> --- drivers/tty/tty_io.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 48569035da56..272f2839acdb 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2429,6 +2429,7 @@ static int tiocgetd(struct tty_struct *tty, int __user *p) /** * send_break - performed time break + * @file: file object * @tty: device to break on * @duration: timeout in mS * @@ -2438,13 +2439,16 @@ static int tiocgetd(struct tty_struct *tty, int __user *p) * Locking: * @tty->atomic_write_lock serializes */ -static int send_break(struct tty_struct *tty, unsigned int duration) +static int send_break(struct file *file, struct tty_struct *tty, unsigned int duration) { int retval; if (tty->ops->break_ctl == NULL) return 0; + if (tty_hung_up_p(file)) + return -EIO; + if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) return tty->ops->break_ctl(tty, duration); @@ -2455,7 +2459,10 @@ static int send_break(struct tty_struct *tty, unsigned int duration) retval = tty->ops->break_ctl(tty, -1); if (!retval) { msleep_interruptible(duration); - retval = tty->ops->break_ctl(tty, 0); + if (tty_hung_up_p(file)) + retval = -EIO; + else + retval = tty->ops->break_ctl(tty, 0); } else if (retval == -EOPNOTSUPP) { /* some drivers can tell only dynamically */ retval = 0; @@ -2714,10 +2721,14 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * Break handling */ case TIOCSBRK: /* Turn break on, unconditionally */ + if (tty_hung_up_p(file)) + return -EIO; if (tty->ops->break_ctl) return tty->ops->break_ctl(tty, -1); return 0; case TIOCCBRK: /* Turn break off, unconditionally */ + if (tty_hung_up_p(file)) + return -EIO; if (tty->ops->break_ctl) return tty->ops->break_ctl(tty, 0); return 0; @@ -2727,10 +2738,10 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) * This is used by the tcdrain() termios function. */ if (!arg) - return send_break(tty, 250); + return send_break(file, tty, 250); return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ - return send_break(tty, arg ? arg*100 : 250); + return send_break(file, tty, arg ? arg * 100 : 250); case TIOCMGET: return tty_tiocmget(tty, p); -- 2.51.0 ^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v2 2/2] tty: fix break race 2026-09-03 4:24 ` [PATCH v2 2/2] tty: fix break race Adriano Cordova @ 2026-09-03 4:40 ` sashiko-bot 0 siblings, 0 replies; 6+ messages in thread From: sashiko-bot @ 2026-09-03 4:40 UTC (permalink / raw) To: Adriano Cordova; +Cc: linux-serial Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: Pre-existing issues: - [High] The lockless checks of `tty_hung_up_p(file)` introduce a Time-of-Check to Time-of-Use (TOCTOU) race against `__tty_hangup`, failing to fully resolve the Use-After-Free/NULL pointer dereference. -- commit 8d56321f205bd51f6c216651862e6a270f2637ea Author: Adriano Cordova <adrianox@gmail.com> tty: fix break race This commit attempts to fix a race condition where a timed break can call break_ctl() on a tty whose driver data is gone, causing a use-after-free or NULL-pointer dereference. It adds checks for the tty hung-up state before each break_ctl() call. Link: https://syzkaller.appspot.com/bug?extid=473d7477c523b41d4046 > diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c > index 48569035da56e..272f2839acdba 100644 > --- a/drivers/tty/tty_io.c > +++ b/drivers/tty/tty_io.c [ ... ] > @@ -2438,13 +2439,16 @@ static int tiocgetd(struct tty_struct *tty, int __user *p) > * Locking: > * @tty->atomic_write_lock serializes > */ > -static int send_break(struct tty_struct *tty, unsigned int duration) > +static int send_break(struct file *file, struct tty_struct *tty, unsigned int duration) > { > int retval; > > if (tty->ops->break_ctl == NULL) > return 0; > > + if (tty_hung_up_p(file)) > + return -EIO; > + [Severity: High] This isn't a bug introduced by this patch, but does this lockless check fully resolve the Time-of-Check to Time-of-Use (TOCTOU) race? If a concurrent thread executes __tty_hangup() immediately after tty_hung_up_p() evaluates to false, could __tty_hangup() tear down the driver data before tty->ops->break_ctl() is executed on the hardware break path, leading to a use-after-free? > if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK) > return tty->ops->break_ctl(tty, duration); > > @@ -2455,7 +2459,10 @@ static int send_break(struct tty_struct *tty, unsigned int duration) > retval = tty->ops->break_ctl(tty, -1); > if (!retval) { > msleep_interruptible(duration); > - retval = tty->ops->break_ctl(tty, 0); > + if (tty_hung_up_p(file)) > + retval = -EIO; > + else > + retval = tty->ops->break_ctl(tty, 0); [Severity: High] This is a pre-existing issue, but does checking tty_hung_up_p() here under atomic_write_lock provide mutual exclusion against __tty_hangup()? Could a concurrent hangup occur right after this check, causing the subsequent call to tty->ops->break_ctl() to dereference freed driver data? > } else if (retval == -EOPNOTSUPP) { > /* some drivers can tell only dynamically */ > retval = 0; [ ... ] > @@ -2714,10 +2721,14 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) > * Break handling > */ > case TIOCSBRK: /* Turn break on, unconditionally */ > + if (tty_hung_up_p(file)) > + return -EIO; > if (tty->ops->break_ctl) > return tty->ops->break_ctl(tty, -1); [Severity: High] This isn't a bug introduced by this patch, but do these unconditional break paths suffer from the same TOCTOU race? Since they do not hold tty_lock, could a concurrent __tty_hangup() free the driver data between the tty_hung_up_p() check and the break_ctl() call? > return 0; > case TIOCCBRK: /* Turn break off, unconditionally */ > + if (tty_hung_up_p(file)) > + return -EIO; > if (tty->ops->break_ctl) > return tty->ops->break_ctl(tty, 0); [Severity: High] This is a pre-existing issue, but similar to TIOCSBRK, is this unprotected check before unconditional break off vulnerable to a concurrent hangup? > return 0; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260903042429.130320-1-adrianox@gmail.com?part=2 ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2 0/2] tty: fix break race 2026-09-03 4:24 [PATCH v2 0/2] tty: fix break race Adriano Cordova 2026-09-03 4:24 ` [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference Adriano Cordova 2026-09-03 4:24 ` [PATCH v2 2/2] tty: fix break race Adriano Cordova @ 2026-09-03 6:22 ` Johan Hovold 2 siblings, 0 replies; 6+ messages in thread From: Johan Hovold @ 2026-09-03 6:22 UTC (permalink / raw) To: Adriano Cordova Cc: gregkh, jirislaby, linux-usb, linux-serial, syzbot+473d7477c523b41d4046 On Thu, Sep 03, 2026 at 12:24:27AM -0400, Adriano Cordova wrote: > A timed break (TCSBRK/TCSBRKP) and the unconditional TIOCSBRK/TIOCCBRK > can race against USB disconnect and call break_ctl() after the device is > gone, dereferencing freed port data in keyspan_break_ctl(). > > Both patches are needed: the tty core must not issue break_ctl() to a > hung-up tty, and serial_break() must not call the driver after > disconnect. This is the same two-layer protection read and write already > use. This doesn't work and as I said in my reply to the syzbot report I'm preparing a proper fix. Johan ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-03 6:22 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-09-03 4:24 [PATCH v2 0/2] tty: fix break race Adriano Cordova 2026-09-03 4:24 ` [PATCH v2 1/2] USB: serial: fix break_ctl() NULL-pointer dereference Adriano Cordova 2026-09-03 4:41 ` sashiko-bot 2026-09-03 4:24 ` [PATCH v2 2/2] tty: fix break race Adriano Cordova 2026-09-03 4:40 ` sashiko-bot 2026-09-03 6:22 ` [PATCH v2 0/2] " Johan Hovold
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox