From 22cbf2cfb211cef2e493c5984d237d0509bf98ce Mon Sep 17 00:00:00 2001 From: Leon Winter Date: Thu, 30 Oct 2014 11:05:03 +0100 Subject: otg: when removing ED from readyQ also set flag The driver keeps track of the "is this endpoint in the list" state with the redundant flag ".is_in_transfer_ready_q". It should therefore always be sync with the .next and .prev of the readyq_list list: struct ed *ed; otg_list_head *qlist = (typeof qlist) ed->readyq_list; (qlist->prev == LIST_POISON2 || qlist->next == LIST_POISON1) == !ed->ed_status.is_in_transfer_ready_q; Should both properties be not in sync, bad things can happen. All code paths in driver rely on the flag and then call list operations. If the flag suggests the entry is in the list, otg_list_pop () will be called. When the entry however is not in the list, the members .prev and .next are poisoned and an attempt to do list operations on them results into a NULL (or rather an invalid address which is the poison) pointer dereference. Such a fault would then trigger a kernel panic and the device rebooting. In real life this happens when disconnecting USB devices frequently, especially when in operation (transfering data while detaching). While in most positions in the code, it was taken care to keep both properties, the flag and the list entry state, consistent, one position was left out, which is addressed with this patch. Extensive testing shows that the device would crash easily and in a reproducable manner without the patch but does not show any faults with the patch applied. Change-Id: I80f3a8e7d866c699ddcd1c61b04d956e39d2197c --- drivers/usb/host/shost/shost_readyq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/host/shost/shost_readyq.c b/drivers/usb/host/shost/shost_readyq.c index 9e41da5..b2ccea0 100644 --- a/drivers/usb/host/shost/shost_readyq.c +++ b/drivers/usb/host/shost/shost_readyq.c @@ -202,6 +202,7 @@ static int get_ed_from_ready_q(struct ed **get_ed, bool is_periodic) } else { otg_list_pop(qlist); periodic_trans_ready_q.entity_num--; + get_ed[0]->ed_status.is_in_transfer_ready_q = false; } return USB_ERR_SUCCESS; @@ -230,6 +231,7 @@ static int get_ed_from_ready_q(struct ed **get_ed, bool is_periodic) } else { otg_list_pop(qlist); nonperiodic_trans_ready_q.entity_num--; + get_ed[0]->ed_status.is_in_transfer_ready_q = false; } return USB_ERR_SUCCESS; } else -- cgit v1.1