usb: smd: Fix crash during reset when RmNet is connected

When a QMI/RMNET control connection is established, a diag
reset causes a crash due to a NULL pointer dereference. The
SMD channel pointer is set to NULL upon USB cable disconnect,
but a queued work function might still try to access that pointer.

Fix the crash by checking for NULL pointers in both smd_* and in
USB gadget transport functions that could pass a closed handle.

CRs-fixed: 336850
Change-Id: I87cb109f3e7007efe15b5acc81180151dd2ef023
Signed-off-by: Jack Pham <jackp@codeaurora.org>
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index 8671669..0256a75 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -108,14 +108,17 @@
 	void *buf;
 	unsigned long flags;
 
-	while (1) {
+	spin_lock_irqsave(&port->port_lock, flags);
+	while (c->ch) {
 		sz = smd_cur_packet_size(c->ch);
-		if (sz == 0)
+		if (sz <= 0)
 			break;
 
 		if (smd_read_avail(c->ch) < sz)
 			break;
 
+		spin_unlock_irqrestore(&port->port_lock, flags);
+
 		buf = kmalloc(sz, GFP_KERNEL);
 		if (!buf)
 			return;
@@ -130,8 +133,8 @@
 			c->to_host++;
 		}
 		kfree(buf);
-		spin_unlock_irqrestore(&port->port_lock, flags);
 	}
+	spin_unlock_irqrestore(&port->port_lock, flags);
 }
 
 static void grmnet_ctrl_smd_write_w(struct work_struct *w)
@@ -143,7 +146,7 @@
 	int ret;
 
 	spin_lock_irqsave(&port->port_lock, flags);
-	while (1) {
+	while (c->ch) {
 		if (list_empty(&c->tx_q))
 			break;
 
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index e74b2e1..a5ceaff 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -209,6 +209,7 @@
 static void gsmd_rx_push(struct work_struct *w)
 {
 	struct gsmd_port *port = container_of(w, struct gsmd_port, push);
+	struct smd_port_info *pi = port->pi;
 	struct list_head *q;
 
 	pr_debug("%s: port:%p port#%d", __func__, port, port->port_num);
@@ -216,10 +217,9 @@
 	spin_lock_irq(&port->port_lock);
 
 	q = &port->read_queue;
-	while (!list_empty(q)) {
+	while (pi->ch && !list_empty(q)) {
 		struct usb_request *req;
 		int avail;
-		struct smd_port_info *pi = port->pi;
 
 		req = list_first_entry(q, struct usb_request, list);
 
@@ -296,21 +296,24 @@
 {
 	struct gsmd_port *port = container_of(w, struct gsmd_port, pull);
 	struct list_head *pool = &port->write_pool;
+	struct smd_port_info *pi = port->pi;
+	struct usb_ep *in;
 
 	pr_debug("%s: port:%p port#%d pool:%p\n", __func__,
 			port, port->port_num, pool);
 
+	spin_lock_irq(&port->port_lock);
+
 	if (!port->port_usb) {
 		pr_debug("%s: usb is disconnected\n", __func__);
+		spin_unlock_irq(&port->port_lock);
 		gsmd_read_pending(port);
 		return;
 	}
 
-	spin_lock_irq(&port->port_lock);
-	while (!list_empty(pool)) {
+	in = port->port_usb->in;
+	while (pi->ch && !list_empty(pool)) {
 		struct usb_request *req;
-		struct usb_ep *in = port->port_usb->in;
-		struct smd_port_info *pi = port->pi;
 		int avail;
 		int ret;