diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-06-01 04:06:23 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2012-06-13 19:37:28 -0400 |
commit | 46ed8f00d8982e49f8fe2c1a9cea192f640cb3ba (patch) | |
tree | 222dcd651ee6e84e1877da9881f28f4cd91964d0 /drivers/usb/host/xhci-mem.c | |
parent | e25e62aecac42379e113c63a674a86ae3ebbec8d (diff) |
xhci: Fix invalid loop check in xhci_free_tt_info()
xhci_free_tt_info() may access the invalid memory when it removes the
last entry but the list is not empty. Then tt_next reaches to the
list head but it still tries to check the tt_info of that entry.
This patch fixes the bug and cleans up the messy code by rewriting
with a simple list_for_each_entry_safe().
This patch should be backported to kernels as old as 3.2, that contain
the commit 839c817ce67178ca3c7c7ad534c571bba1e69ebe "xhci: Store
information about roothubs and TTs."
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Reviewed-by: Oliver Neukum <oneukum@suse.de>
Cc: <stable@vger.kernel.org>
Diffstat (limited to 'drivers/usb/host/xhci-mem.c')
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 39 |
1 files changed, 10 insertions, 29 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index ec4338eec826..898dfc8bc520 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c | |||
@@ -793,10 +793,9 @@ static void xhci_free_tt_info(struct xhci_hcd *xhci, | |||
793 | struct xhci_virt_device *virt_dev, | 793 | struct xhci_virt_device *virt_dev, |
794 | int slot_id) | 794 | int slot_id) |
795 | { | 795 | { |
796 | struct list_head *tt; | ||
797 | struct list_head *tt_list_head; | 796 | struct list_head *tt_list_head; |
798 | struct list_head *tt_next; | 797 | struct xhci_tt_bw_info *tt_info, *next; |
799 | struct xhci_tt_bw_info *tt_info; | 798 | bool slot_found = false; |
800 | 799 | ||
801 | /* If the device never made it past the Set Address stage, | 800 | /* If the device never made it past the Set Address stage, |
802 | * it may not have the real_port set correctly. | 801 | * it may not have the real_port set correctly. |
@@ -808,34 +807,16 @@ static void xhci_free_tt_info(struct xhci_hcd *xhci, | |||
808 | } | 807 | } |
809 | 808 | ||
810 | tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts); | 809 | tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts); |
811 | if (list_empty(tt_list_head)) | 810 | list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) { |
812 | return; | 811 | /* Multi-TT hubs will have more than one entry */ |
813 | 812 | if (tt_info->slot_id == slot_id) { | |
814 | list_for_each(tt, tt_list_head) { | 813 | slot_found = true; |
815 | tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list); | 814 | list_del(&tt_info->tt_list); |
816 | if (tt_info->slot_id == slot_id) | 815 | kfree(tt_info); |
816 | } else if (slot_found) { | ||
817 | break; | 817 | break; |
818 | } | ||
818 | } | 819 | } |
819 | /* Cautionary measure in case the hub was disconnected before we | ||
820 | * stored the TT information. | ||
821 | */ | ||
822 | if (tt_info->slot_id != slot_id) | ||
823 | return; | ||
824 | |||
825 | tt_next = tt->next; | ||
826 | tt_info = list_entry(tt, struct xhci_tt_bw_info, | ||
827 | tt_list); | ||
828 | /* Multi-TT hubs will have more than one entry */ | ||
829 | do { | ||
830 | list_del(tt); | ||
831 | kfree(tt_info); | ||
832 | tt = tt_next; | ||
833 | if (list_empty(tt_list_head)) | ||
834 | break; | ||
835 | tt_next = tt->next; | ||
836 | tt_info = list_entry(tt, struct xhci_tt_bw_info, | ||
837 | tt_list); | ||
838 | } while (tt_info->slot_id == slot_id); | ||
839 | } | 820 | } |
840 | 821 | ||
841 | int xhci_alloc_tt_info(struct xhci_hcd *xhci, | 822 | int xhci_alloc_tt_info(struct xhci_hcd *xhci, |