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:
[<ffffff8008695598>] tty_wakeup+0x18/0x90
[<ffffff8008abf7b4>] gs_start_io+0xb4/0x128
[<ffffff8008abf8e4>] gserial_connect+0xbc/0x188
[<ffffff8008abe610>] acm_set_alt+0x70/0x1a0
[<ffffff8008aae224>] composite_setup+0xddc/0x1a48
[<ffffff8008ab1438>] android_setup+0xc0/0x148
[<ffffff8008ab7090>] tegra_xudc_ep0_delegate_req+0x40/0x60
[<ffffff8008ab9b80>] tegra_xudc_handle_ep0_setup_packet+0xa8/0x6c0
[<ffffff8008abbc58>] tegra_xudc_irq+0x368/0x720

http://nvbugs/3310910
http://nvbugs/4900394
http://nvbugs/5080239

Signed-off-by: Wayne Chang <waynec@nvidia.com>
Reviewed-by: HaoTien Hsu <haotienh@nvidia.com>
Reviewed-by: EJ Hsu <ejh@nvidia.com>
Reviewed-by: WK Tsai <wtsai@nvidia.com>
Signed-off-by: Vishwaroop A <va@nvidia.com>
Acked-by: Noah Wager <noah.wager@canonical.com>
Acked-by: Jacob Martin <jacob.martin@canonical.com>
Signed-off-by: Noah Wager <noah.wager@canonical.com>
This commit is contained in:
Wayne Chang
2021-05-20 15:33:44 +08:00
committed by Noah Wager
parent 5094618371
commit d37cfa3d29
+17 -10
View File
@@ -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);