Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 46ed8f00 authored by Takashi Iwai's avatar Takashi Iwai Committed by Sarah Sharp
Browse files

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 839c817c "xhci: Store
information about roothubs and TTs."

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Reviewed-by: default avatarOliver Neukum <oneukum@suse.de>
Cc: <stable@vger.kernel.org>
parent e25e62ae
Loading
Loading
Loading
Loading
+10 −29
Original line number Original line Diff line number Diff line
@@ -793,10 +793,9 @@ static void xhci_free_tt_info(struct xhci_hcd *xhci,
		struct xhci_virt_device *virt_dev,
		struct xhci_virt_device *virt_dev,
		int slot_id)
		int slot_id)
{
{
	struct list_head *tt;
	struct list_head *tt_list_head;
	struct list_head *tt_list_head;
	struct list_head *tt_next;
	struct xhci_tt_bw_info *tt_info, *next;
	struct xhci_tt_bw_info *tt_info;
	bool slot_found = false;


	/* If the device never made it past the Set Address stage,
	/* If the device never made it past the Set Address stage,
	 * it may not have the real_port set correctly.
	 * it may not have the real_port set correctly.
@@ -808,34 +807,16 @@ static void xhci_free_tt_info(struct xhci_hcd *xhci,
	}
	}


	tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts);
	tt_list_head = &(xhci->rh_bw[virt_dev->real_port - 1].tts);
	if (list_empty(tt_list_head))
	list_for_each_entry_safe(tt_info, next, tt_list_head, tt_list) {
		return;

	list_for_each(tt, tt_list_head) {
		tt_info = list_entry(tt, struct xhci_tt_bw_info, tt_list);
		if (tt_info->slot_id == slot_id)
			break;
	}
	/* Cautionary measure in case the hub was disconnected before we
	 * stored the TT information.
	 */
	if (tt_info->slot_id != slot_id)
		return;

	tt_next = tt->next;
	tt_info = list_entry(tt, struct xhci_tt_bw_info,
			tt_list);
		/* Multi-TT hubs will have more than one entry */
		/* Multi-TT hubs will have more than one entry */
	do {
		if (tt_info->slot_id == slot_id) {
		list_del(tt);
			slot_found = true;
			list_del(&tt_info->tt_list);
			kfree(tt_info);
			kfree(tt_info);
		tt = tt_next;
		} else if (slot_found) {
		if (list_empty(tt_list_head))
			break;
			break;
		tt_next = tt->next;
		}
		tt_info = list_entry(tt, struct xhci_tt_bw_info,
	}
				tt_list);
	} while (tt_info->slot_id == slot_id);
}
}


int xhci_alloc_tt_info(struct xhci_hcd *xhci,
int xhci_alloc_tt_info(struct xhci_hcd *xhci,