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

Commit 026672d0 authored by Pavel Machek's avatar Pavel Machek Committed by David S. Miller
Browse files

hci_usb.h: fix hard-to-trigger race



If someone tries to _urb_unlink while _urb_queue_head is running, he'll see
_urb->queue == NULL and fail to do any locking.  Prevent that from happening
by strategically placed barriers.

Signed-off-by: default avatarPavel Machek <pavel@suse.cz>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 84994e16
Loading
Loading
Loading
Loading
+13 −8
Original line number Diff line number Diff line
@@ -70,7 +70,8 @@ static inline void _urb_queue_head(struct _urb_queue *q, struct _urb *_urb)
{
	unsigned long flags;
	spin_lock_irqsave(&q->lock, flags);
	list_add(&_urb->list, &q->head); _urb->queue = q;
	/* _urb_unlink needs to know which spinlock to use, thus mb(). */
	_urb->queue = q; mb(); list_add(&_urb->list, &q->head);
	spin_unlock_irqrestore(&q->lock, flags);
}

@@ -78,20 +79,24 @@ static inline void _urb_queue_tail(struct _urb_queue *q, struct _urb *_urb)
{
	unsigned long flags;
	spin_lock_irqsave(&q->lock, flags);
	list_add_tail(&_urb->list, &q->head); _urb->queue = q;
	/* _urb_unlink needs to know which spinlock to use, thus mb(). */
	_urb->queue = q; mb(); list_add_tail(&_urb->list, &q->head);
	spin_unlock_irqrestore(&q->lock, flags);
}

static inline void _urb_unlink(struct _urb *_urb)
{
	struct _urb_queue *q = _urb->queue;
	struct _urb_queue *q;
	unsigned long flags;
	if (q) {

	mb();
	q = _urb->queue;
	/* If q is NULL, it will die at easy-to-debug NULL pointer dereference.
	   No need to BUG(). */
	spin_lock_irqsave(&q->lock, flags);
	list_del(&_urb->list); _urb->queue = NULL;
	spin_unlock_irqrestore(&q->lock, flags);
}
}

struct hci_usb {
	struct hci_dev		*hdev;