* [PATCH] usb: gadget: u_serial: implement flush_buffer
@ 2026-10-15 16:27 Mehmet Fide
2026-10-15 16:42 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Mehmet Fide @ 2026-10-15 16:27 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel, mehmet.fide
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
gs_close() waits up to GS_CLOSE_TIMEOUT (15 s) for the write buffer
to drain while the gadget is connected. When the host does not read
the port, for example because no application on the host has it open,
the last close blocks for the full timeout. tcflush(TCOFLUSH) cannot
avoid that: u_serial has no flush_buffer operation, so the request is
a no-op.
Implement flush_buffer: discard the buffered output under the port
lock and wake up the close and write waiters. As on other tty drivers,
a hangup and an interrupt character (ISIG without NOFLSH) now discard
the pending output as well.
Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
---
drivers/usb/gadget/function/u_serial.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index cdd1dfc666c4..2b99e33cbf3a 100644
--- a/drivers/usb/gadget/function/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
@@ -822,6 +822,19 @@ static void gs_flush_chars(struct tty_struct *tty)
spin_unlock_irqrestore(&port->port_lock, flags);
}
+static void gs_flush_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ kfifo_reset(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ wake_up_interruptible(&port->drain_wait);
+ tty_wakeup(tty);
+}
+
static unsigned int gs_write_room(struct tty_struct *tty)
{
struct gs_port *port = tty->driver_data;
@@ -914,6 +927,7 @@ static const struct tty_operations gs_tty_ops = {
.write = gs_write,
.put_char = gs_put_char,
.flush_chars = gs_flush_chars,
+ .flush_buffer = gs_flush_buffer,
.write_room = gs_write_room,
.chars_in_buffer = gs_chars_in_buffer,
.unthrottle = gs_unthrottle,
--
2.55.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] usb: gadget: u_serial: implement flush_buffer
2026-10-15 16:27 [PATCH] usb: gadget: u_serial: implement flush_buffer Mehmet Fide
@ 2026-10-15 16:42 ` sashiko-bot
2026-10-15 19:41 ` Mehmet Fide
0 siblings, 1 reply; 3+ messages in thread
From: sashiko-bot @ 2026-10-15 16:42 UTC (permalink / raw)
To: Mehmet Fide; +Cc: linux-usb
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] tty: u_serial: UAF in gs_flush_buffer during TTY hangup
--
commit c239a0119a25b9f8fab856b838b114d124d55f62
Author: Mehmet Fide <mehmet.fide@screeningeagle.com>
usb: gadget: u_serial: implement flush_buffer
This patch implements flush_buffer for u_serial to discard buffered output
under the port lock. This aims to prevent gs_close() from blocking for the
full 15-second timeout when the host does not read the port.
> diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c
> index cdd1dfc666c4f..2b99e33cbf3af 100644
> --- a/drivers/usb/gadget/function/u_serial.c
> +++ b/drivers/usb/gadget/function/u_serial.c
> @@ -822,6 +822,19 @@ static void gs_flush_chars(struct tty_struct *tty)
> spin_unlock_irqrestore(&port->port_lock, flags);
> }
>
> +static void gs_flush_buffer(struct tty_struct *tty)
> +{
> + struct gs_port *port = tty->driver_data;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&port->port_lock, flags);
[Severity: High]
Could this lead to a use-after-free during TTY hangup?
If a USB gadget unbinds, gserial_disconnect() schedules a hangup via
tty_hangup(), which queues hangup_work. Concurrently, if userspace closes
the TTY, tty_release() calls gs_close(), dropping port->count to 0. This
wakes up gserial_free_port(), which proceeds to kfree(port).
Once tty_release() drops tty_lock(), the scheduled hangup_work executing
__tty_hangup() can acquire tty_lock() and call tty_ldisc_hangup().
Because tty_ldisc_hangup() unconditionally calls tty_driver_flush_buffer()
if a line discipline is present, it will invoke this newly added callback. By
this point, the port structure has already been freed by gserial_free_port(),
so attempting to take port->port_lock here dereferences freed memory.
> + kfifo_reset(&port->port_write_buf);
> + spin_unlock_irqrestore(&port->port_lock, flags);
> +
> + wake_up_interruptible(&port->drain_wait);
> + tty_wakeup(tty);
> +}
> +
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261015162732.3170938-1-mehmet.fide@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] usb: gadget: u_serial: implement flush_buffer
2026-10-15 16:42 ` sashiko-bot
@ 2026-10-15 19:41 ` Mehmet Fide
0 siblings, 0 replies; 3+ messages in thread
From: Mehmet Fide @ 2026-10-15 19:41 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-usb, linux-kernel, mehmet.fide
The window is real, but the use-after-free in it is older than this
patch.
I reproduced it on a Verdin AM62 with 6.12.113 and KASAN, with a 3 s
delay added at the top of gs_close() to widen the window. At shutdown
systemd closes ttyGS0 while the configfs teardown unbinds the gadget
and removes the acm function:
BUG: KASAN: slab-use-after-free in check_tty_count+0x230/0x278
Workqueue: events do_tty_hangup
__tty_hangup.part.0
do_tty_hangup
Allocated by: gserial_alloc_line_no_console <- acm_alloc_instance
Freed by: gserial_free_port <- gserial_free_line <- acm_free_instance
In one of the runs the closing task then faulted in release_tty() ->
tty_buffer_cancel_work(). The KASAN report is the same with this patch
not applied.
gserial_free_port() frees the gs_port, and with it the embedded
tty_port, while a tty still refers to it: the tty core keeps using
tty->port until release_tty(), and u_serial has no install/cleanup pair
that holds a reference on the port meanwhile. I will send a v2 as a
series: first keep the gs_port alive through the tty_port reference
count (get in install, put in cleanup, free in the destructor), then
flush_buffer on top of it.
Greg, please drop this version.
Thanks,
Mehmet
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-15 19:41 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-15 16:27 [PATCH] usb: gadget: u_serial: implement flush_buffer Mehmet Fide
2026-10-15 16:42 ` sashiko-bot
2026-10-15 19:41 ` Mehmet Fide
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.