USB: ks_bridge: Prevent freeing buffers while copy is in progress
The completed read buffers are added to to_ks_list in Rx URB
completion handler. The buffer is deleted from this list by
reader process after copying to user space buffer. If disconnect
happens during this copy, the buffer is freed from disconnect
context. Hence detach the buffer from to_ks_list before copying
to user space buffer.
CRs-Fixed: 448142
Change-Id: Ia4484b2709065162b0d32782a30e2b2fd36f5fda
Signed-off-by: Pavankumar Kondeti <pkondeti@codeaurora.org>
diff --git a/drivers/usb/misc/ks_bridge.c b/drivers/usb/misc/ks_bridge.c
index 2a9536a..33280db 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -166,7 +166,7 @@
int ret;
unsigned long flags;
struct ks_bridge *ksb = fp->private_data;
- struct data_pkt *pkt;
+ struct data_pkt *pkt = NULL;
size_t space, copied;
read_start:
@@ -187,10 +187,12 @@
space = count;
copied = 0;
- while (!list_empty(&ksb->to_ks_list) && space) {
+ while (!list_empty(&ksb->to_ks_list) && space &&
+ test_bit(USB_DEV_CONNECTED, &ksb->flags)) {
size_t len;
pkt = list_first_entry(&ksb->to_ks_list, struct data_pkt, list);
+ list_del_init(&pkt->list);
len = min_t(size_t, space, pkt->len - pkt->n_read);
spin_unlock_irqrestore(&ksb->lock, flags);
@@ -199,26 +201,32 @@
dev_err(ksb->fs_dev.this_device,
"copy_to_user failed err:%d\n", ret);
ksb_free_data_pkt(pkt);
- return ret;
+ return -EFAULT;
}
pkt->n_read += len;
space -= len;
copied += len;
- spin_lock_irqsave(&ksb->lock, flags);
if (pkt->n_read == pkt->len) {
/*
* re-init the packet and queue it
* for more data.
*/
- list_del_init(&pkt->list);
pkt->n_read = 0;
pkt->len = MAX_DATA_PKT_SIZE;
- spin_unlock_irqrestore(&ksb->lock, flags);
submit_one_urb(ksb, GFP_KERNEL, pkt);
- spin_lock_irqsave(&ksb->lock, flags);
+ pkt = NULL;
}
+ spin_lock_irqsave(&ksb->lock, flags);
+ }
+
+ /* put the partial packet back in the list */
+ if (!space && pkt && pkt->n_read != pkt->len) {
+ if (test_bit(USB_DEV_CONNECTED, &ksb->flags))
+ list_add(&pkt->list, &ksb->to_ks_list);
+ else
+ ksb_free_data_pkt(pkt);
}
spin_unlock_irqrestore(&ksb->lock, flags);