Skip to content

Commit 8c13a73

Browse files
matnymangregkh
authored andcommitted
xhci: sideband: Fix race condition in sideband unregister
Uttkarsh Aggarwal observed a kernel panic during sideband un-register and found it was caused by a race condition between sideband unregister, and creating sideband interrupters. The issue occurrs when thread T1 runs uaudio_disconnect() and released sb->xhci via sideband_unregister, while thread T2 simultaneously accessed the now-NULL sb->xhci in xhci_sideband_create_interrupter() resulting in a crash. Ensure new endpoints or interrupter can't be added to a sidenband after xhci_sideband_unregister() cleared the existing ones, and unlocked the sideband mutex. Reorganize code so that mutex is only taken and released once in xhci_sideband_unregister(), and clear sb->vdev while mutex is taken. Use mutex guards to reduce human unlock errors in code Refuse to add endpoints or interrupter if sb->vdev is not set. sb->vdev is set when sideband is created and registered. Reported-by: Uttkarsh Aggarwal <uttkarsh.aggarwal@oss.qualcomm.com> Closes: https://lore.kernel.org/linux-usb/20251028080043.27760-1-uttkarsh.aggarwal@oss.qualcomm.com Fixes: de66754 ("xhci: sideband: add initial api to register a secondary interrupter entity") Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://patch.msgid.link/20251107162819.1362579-4-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent f6bb3b6 commit 8c13a73

1 file changed

Lines changed: 58 additions & 44 deletions

File tree

drivers/usb/host/xhci-sideband.c

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,12 @@ xhci_ring_to_sgtable(struct xhci_sideband *sb, struct xhci_ring *ring)
7373
return NULL;
7474
}
7575

76+
/* Caller must hold sb->mutex */
7677
static void
7778
__xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *ep)
7879
{
80+
lockdep_assert_held(&sb->mutex);
81+
7982
/*
8083
* Issue a stop endpoint command when an endpoint is removed.
8184
* The stop ep cmd handler will handle the ring cleanup.
@@ -86,6 +89,25 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
8689
sb->eps[ep->ep_index] = NULL;
8790
}
8891

92+
/* Caller must hold sb->mutex */
93+
static void
94+
__xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
95+
{
96+
struct usb_device *udev;
97+
98+
lockdep_assert_held(&sb->mutex);
99+
100+
if (!sb->ir)
101+
return;
102+
103+
xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
104+
sb->ir = NULL;
105+
udev = sb->vdev->udev;
106+
107+
if (udev->state != USB_STATE_NOTATTACHED)
108+
usb_offload_put(udev);
109+
}
110+
89111
/* sideband api functions */
90112

91113
/**
@@ -131,14 +153,16 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
131153
struct xhci_virt_ep *ep;
132154
unsigned int ep_index;
133155

134-
mutex_lock(&sb->mutex);
156+
guard(mutex)(&sb->mutex);
157+
158+
if (!sb->vdev)
159+
return -ENODEV;
160+
135161
ep_index = xhci_get_endpoint_index(&host_ep->desc);
136162
ep = &sb->vdev->eps[ep_index];
137163

138-
if (ep->ep_state & EP_HAS_STREAMS) {
139-
mutex_unlock(&sb->mutex);
164+
if (ep->ep_state & EP_HAS_STREAMS)
140165
return -EINVAL;
141-
}
142166

143167
/*
144168
* Note, we don't know the DMA mask of the audio DSP device, if its
@@ -148,14 +172,11 @@ xhci_sideband_add_endpoint(struct xhci_sideband *sb,
148172
* and let this function add the endpoint and allocate the ring buffer
149173
* with the smallest common DMA mask
150174
*/
151-
if (sb->eps[ep_index] || ep->sideband) {
152-
mutex_unlock(&sb->mutex);
175+
if (sb->eps[ep_index] || ep->sideband)
153176
return -EBUSY;
154-
}
155177

156178
ep->sideband = sb;
157179
sb->eps[ep_index] = ep;
158-
mutex_unlock(&sb->mutex);
159180

160181
return 0;
161182
}
@@ -180,18 +201,16 @@ xhci_sideband_remove_endpoint(struct xhci_sideband *sb,
180201
struct xhci_virt_ep *ep;
181202
unsigned int ep_index;
182203

183-
mutex_lock(&sb->mutex);
204+
guard(mutex)(&sb->mutex);
205+
184206
ep_index = xhci_get_endpoint_index(&host_ep->desc);
185207
ep = sb->eps[ep_index];
186208

187-
if (!ep || !ep->sideband || ep->sideband != sb) {
188-
mutex_unlock(&sb->mutex);
209+
if (!ep || !ep->sideband || ep->sideband != sb)
189210
return -ENODEV;
190-
}
191211

192212
__xhci_sideband_remove_endpoint(sb, ep);
193213
xhci_initialize_ring_info(ep->ring);
194-
mutex_unlock(&sb->mutex);
195214

196215
return 0;
197216
}
@@ -316,28 +335,25 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
316335
if (!sb || !sb->xhci)
317336
return -ENODEV;
318337

319-
mutex_lock(&sb->mutex);
320-
if (sb->ir) {
321-
ret = -EBUSY;
322-
goto out;
323-
}
338+
guard(mutex)(&sb->mutex);
339+
340+
if (!sb->vdev)
341+
return -ENODEV;
342+
343+
if (sb->ir)
344+
return -EBUSY;
324345

325346
sb->ir = xhci_create_secondary_interrupter(xhci_to_hcd(sb->xhci),
326347
num_seg, imod_interval,
327348
intr_num);
328-
if (!sb->ir) {
329-
ret = -ENOMEM;
330-
goto out;
331-
}
349+
if (!sb->ir)
350+
return -ENOMEM;
332351

333352
udev = sb->vdev->udev;
334353
ret = usb_offload_get(udev);
335354

336355
sb->ir->ip_autoclear = ip_autoclear;
337356

338-
out:
339-
mutex_unlock(&sb->mutex);
340-
341357
return ret;
342358
}
343359
EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
@@ -352,21 +368,12 @@ EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);
352368
void
353369
xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
354370
{
355-
struct usb_device *udev;
356-
357-
if (!sb || !sb->ir)
371+
if (!sb)
358372
return;
359373

360-
mutex_lock(&sb->mutex);
361-
xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
362-
363-
sb->ir = NULL;
364-
udev = sb->vdev->udev;
374+
guard(mutex)(&sb->mutex);
365375

366-
if (udev->state != USB_STATE_NOTATTACHED)
367-
usb_offload_put(udev);
368-
369-
mutex_unlock(&sb->mutex);
376+
__xhci_sideband_remove_interrupter(sb);
370377
}
371378
EXPORT_SYMBOL_GPL(xhci_sideband_remove_interrupter);
372379

@@ -465,6 +472,7 @@ EXPORT_SYMBOL_GPL(xhci_sideband_register);
465472
void
466473
xhci_sideband_unregister(struct xhci_sideband *sb)
467474
{
475+
struct xhci_virt_device *vdev;
468476
struct xhci_hcd *xhci;
469477
int i;
470478

@@ -473,17 +481,23 @@ xhci_sideband_unregister(struct xhci_sideband *sb)
473481

474482
xhci = sb->xhci;
475483

476-
mutex_lock(&sb->mutex);
477-
for (i = 0; i < EP_CTX_PER_DEV; i++)
478-
if (sb->eps[i])
479-
__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
480-
mutex_unlock(&sb->mutex);
484+
scoped_guard(mutex, &sb->mutex) {
485+
vdev = sb->vdev;
486+
if (!vdev)
487+
return;
488+
489+
for (i = 0; i < EP_CTX_PER_DEV; i++)
490+
if (sb->eps[i])
491+
__xhci_sideband_remove_endpoint(sb, sb->eps[i]);
481492

482-
xhci_sideband_remove_interrupter(sb);
493+
__xhci_sideband_remove_interrupter(sb);
494+
495+
sb->vdev = NULL;
496+
}
483497

484498
spin_lock_irq(&xhci->lock);
485499
sb->xhci = NULL;
486-
sb->vdev->sideband = NULL;
500+
vdev->sideband = NULL;
487501
spin_unlock_irq(&xhci->lock);
488502

489503
kfree(sb);

0 commit comments

Comments
 (0)