From d37cfa3d290cc14940073d642a58d675d5367b65 Mon Sep 17 00:00:00 2001 From: Wayne Chang Date: Thu, 20 May 2021 15:33:44 +0800 Subject: [PATCH] NVIDIA: SAUCE: usb: gadget: serial: Fix possible race with close We rely on the port_lock to synchronous status but the port_lock is released when we called gs_start_rx and gs_start_tx in gs_start_io A B tty_release composite_setup acm_set_alt gserial_connect //lock gs_start_io gs_start_rx//unlock gs_close //lock and release tty gs_start_rx//lock tty_wakeup //panic Check tty and give up the connect callback when the tty has been released call stack sample as follow: [] tty_wakeup+0x18/0x90 [] gs_start_io+0xb4/0x128 [] gserial_connect+0xbc/0x188 [] acm_set_alt+0x70/0x1a0 [] composite_setup+0xddc/0x1a48 [] android_setup+0xc0/0x148 [] tegra_xudc_ep0_delegate_req+0x40/0x60 [] tegra_xudc_handle_ep0_setup_packet+0xa8/0x6c0 [] tegra_xudc_irq+0x368/0x720 http://nvbugs/3310910 http://nvbugs/4900394 http://nvbugs/5080239 Signed-off-by: Wayne Chang Reviewed-by: HaoTien Hsu Reviewed-by: EJ Hsu Reviewed-by: WK Tsai Signed-off-by: Vishwaroop A Acked-by: Noah Wager Acked-by: Jacob Martin Signed-off-by: Noah Wager --- drivers/usb/gadget/function/u_serial.c | 27 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 729b0472bab0..d3c996921011 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -5,6 +5,7 @@ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com) * Copyright (C) 2008 David Brownell * Copyright (C) 2008 by Nokia Corporation + * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * * This code also borrows from usbserial.c, which is * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) @@ -291,7 +292,11 @@ __acquires(&port->port_lock) break; } - if (do_tty_wake && port->port.tty) + /* race with close() */ + if (!port->port.tty) + return -ESHUTDOWN; + + if (do_tty_wake) tty_wakeup(port->port.tty); return status; } @@ -344,7 +349,7 @@ __acquires(&port->port_lock) if (!port->port_usb) break; } - return port->read_started; + return port->port.tty ? port->read_started : 0; } /* @@ -570,10 +575,11 @@ static int gs_start_io(struct gs_port *port) started = gs_start_rx(port); if (started) { - gs_start_tx(port); + status = gs_start_tx(port); /* Unblock any pending writes into our circular buffer, in case * we didn't in gs_start_tx() */ - tty_wakeup(port->port.tty); + if (!status) + tty_wakeup(port->port.tty); } else { /* Free reqs only if we are still connected */ if (port->port_usb) { @@ -646,9 +652,9 @@ static int gs_open(struct tty_struct *tty, struct file *file) struct gserial *gser = port->port_usb; pr_debug("gs_open: start ttyGS%d\n", port->port_num); - gs_start_io(port); + status = gs_start_io(port); - if (gser->connect) + if (!status && gser->connect) gser->connect(gser); } else { pr_debug("delay start of ttyGS%d\n", port->port_num); @@ -1351,8 +1357,8 @@ int gserial_connect(struct gserial *gser, u8 port_num) */ if (port->port.count) { pr_debug("gserial_connect: start ttyGS%d\n", port->port_num); - gs_start_io(port); - if (gser->connect) + status = gs_start_io(port); + if (!status && gser->connect) gser->connect(gser); } else { if (gser->disconnect) @@ -1453,6 +1459,7 @@ void gserial_resume(struct gserial *gser) { struct gs_port *port; unsigned long flags; + int status = 0; spin_lock_irqsave(&serial_port_lock, flags); port = gser->ioport; @@ -1471,8 +1478,8 @@ void gserial_resume(struct gserial *gser) } pr_debug("delayed start ttyGS%d\n", port->port_num); - gs_start_io(port); - if (gser->connect) + status = gs_start_io(port); + if (!status && gser->connect) gser->connect(gser); port->start_delayed = false; spin_unlock_irqrestore(&port->port_lock, flags);