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: Id5ec904da0c9db427feff5a0165f98660aed0dee
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 5a3dfa3..d96de29 100644
--- a/drivers/usb/misc/ks_bridge.c
+++ b/drivers/usb/misc/ks_bridge.c
@@ -148,7 +148,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:
@@ -169,10 +169,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);
@@ -180,26 +182,32 @@
if (ret) {
pr_err("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);