fix list_del corruption warning on USB audio with twinkle (rhbz 871078)

This commit is contained in:
Justin M. Forbes 2012-11-12 12:23:45 -06:00
parent 123d75be63
commit 02c38dbef5
4 changed files with 257 additions and 1 deletions

View File

@ -0,0 +1,72 @@
commit 2656a9abcf1ec8dd5fee6a75d6997a0f2fa0094e
Author: Alan Stern <stern@rowland.harvard.edu>
Date: Thu Nov 8 10:17:01 2012 -0500
USB: EHCI: bugfix: urb->hcpriv should not be NULL
This patch (as1632b) fixes a bug in ehci-hcd. The USB core uses
urb->hcpriv to determine whether or not an URB is active; host
controller drivers are supposed to set this pointer to a non-NULL
value when an URB is queued. However ehci-hcd sets it to NULL for
isochronous URBs, which defeats the check in usbcore.
In itself this isn't a big deal. But people have recently found that
certain sequences of actions will cause the snd-usb-audio driver to
reuse URBs without waiting for them to complete. In the absence of
proper checking by usbcore, the URBs get added to their endpoint list
twice. This leads to list corruption and a system freeze.
The patch makes ehci-hcd assign a meaningful value to urb->hcpriv for
isochronous URBs. Improving robustness always helps.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Reported-by: Artem S. Tashkinov <t.artem@lycos.com>
Reported-by: Christof Meerwald <cmeerw@cmeerw.org>
CC: <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 4b66374..3d98902 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -264,15 +264,9 @@ ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
__releases(ehci->lock)
__acquires(ehci->lock)
{
- if (likely (urb->hcpriv != NULL)) {
- struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv;
-
- /* S-mask in a QH means it's an interrupt urb */
- if ((qh->hw->hw_info2 & cpu_to_hc32(ehci, QH_SMASK)) != 0) {
-
- /* ... update hc-wide periodic stats (for usbfs) */
- ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
- }
+ if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+ /* ... update hc-wide periodic stats */
+ ehci_to_hcd(ehci)->self.bandwidth_int_reqs--;
}
if (unlikely(urb->unlinked)) {
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 2e14714..69ebee7 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1630,7 +1630,7 @@ static void itd_link_urb(
/* don't need that schedule data any more */
iso_sched_free (stream, iso_sched);
- urb->hcpriv = NULL;
+ urb->hcpriv = stream;
++ehci->isoc_count;
enable_periodic(ehci);
@@ -2029,7 +2029,7 @@ static void sitd_link_urb(
/* don't need that schedule data any more */
iso_sched_free (stream, sched);
- urb->hcpriv = NULL;
+ urb->hcpriv = stream;
++ehci->isoc_count;
enable_periodic(ehci);

View File

@ -0,0 +1,46 @@
commit 2f02bc8af3abb846823811af65ec6cc46a4d525d
Author: Alan Stern <stern@rowland.harvard.edu>
Date: Wed Nov 7 16:35:00 2012 -0500
USB: report submission of active URBs
This patch (as1633) changes slightly the way usbcore handled
submissions of URBs that are already active. It will now return
-EBUSY rather than -EINVAL, and it will call WARN_ONCE to draw
people's attention to the bug.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt
index 8d1e2a9..9c3eb84 100644
--- a/Documentation/usb/error-codes.txt
+++ b/Documentation/usb/error-codes.txt
@@ -21,6 +21,8 @@ Non-USB-specific:
USB-specific:
+-EBUSY The URB is already active.
+
-ENODEV specified USB-device or bus doesn't exist
-ENOENT specified interface or endpoint does not exist or
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 3662287..e0d9d94 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -321,8 +321,13 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
struct usb_host_endpoint *ep;
int is_out;
- if (!urb || urb->hcpriv || !urb->complete)
+ if (!urb || !urb->complete)
return -EINVAL;
+ if (urb->hcpriv) {
+ WARN_ONCE(1, "URB %p submitted while active\n", urb);
+ return -EBUSY;
+ }
+
dev = urb->dev;
if ((!dev) || (dev->state < USB_STATE_UNAUTHENTICATED))
return -ENODEV;

View File

@ -54,7 +54,7 @@ Summary: The Linux kernel
# For non-released -rc kernels, this will be appended after the rcX and
# gitX tags, so a 3 here would become part of release "0.rcX.gitX.3"
#
%global baserelease 1
%global baserelease 2
%global fedora_build %{baserelease}
# base_sublevel is the kernel version we're starting with and patching
@ -705,6 +705,11 @@ Patch22092: net-fix-divide-by-zero-in-tcp-algorithm-illinois.patch
Patch30000: weird-root-dentry-name-debug.patch
Patch30010: debug-808990.patch
#rhbz 871078
Patch22110: usb-audio-fix-crash-at-re-preparing-the-PCM-stream.patch
Patch22111: USB-EHCI-urb-hcpriv-should-not-be-NULL.patch
Patch22112: USB-report-submission-of-active-URBs.patch
# END OF PATCH DEFINITIONS
%endif
@ -1327,6 +1332,11 @@ ApplyPatch 0012-ext4-serialize-fallocate-with-ext4_convert_unwritten.patch
#rhbz 871923 871848 CVE-2012-4565
ApplyPatch net-fix-divide-by-zero-in-tcp-algorithm-illinois.patch
#rhbz 871078
ApplyPatch usb-audio-fix-crash-at-re-preparing-the-PCM-stream.patch
ApplyPatch USB-EHCI-urb-hcpriv-should-not-be-NULL.patch
ApplyPatch USB-report-submission-of-active-URBs.patch
# END OF PATCH APPLICATIONS
%endif
@ -2027,6 +2037,9 @@ fi
# and build.
%changelog
* Mon Nov 12 2012 Justin M. Forbes <jforbes@redhat.com>
- fix list_del corruption warning on USB audio with twinkle (rhbz 871078)
* Mon Nov 05 2012 Justin M. Forbes <jforbes@redhat.com> 3.6.6-1
- Linux 3.6.6

View File

@ -0,0 +1,125 @@
At Thu, 08 Nov 2012 08:31:35 +0100,
Daniel Mack wrote:
(snip)
> >> We can't simply stop both endpoints in the prepare callback.
> >
> > The new function doesn't stop the stream by itself but it just syncs
> > if the stream is being stopped beforehand. So, it's safe to call it
> > there.
> >
> > Maybe the name was confusing. It should have been like
> > snd_usb_endpoint_sync_pending_stop() or such.
>
> Ah, right. I was errornously looking closer to Alan's patch but then
> replied to yours. Alright then - thanks for explaining :)
OK, thanks for checking.
FWIW, below is the patch I applied now to for-linus branch.
Renamed the function, added the comment and put NULL check to the
function to simplify.
Takashi
---
From: Takashi Iwai <tiwai@suse.de>
Subject: [PATCH] ALSA: usb-audio: Fix crash at re-preparing the PCM stream
There are bug reports of a crash with USB-audio devices when PCM
prepare is performed immediately after the stream is stopped via
trigger callback. It turned out that the problem is that we don't
wait until all URBs are killed.
This patch adds a new function to synchronize the pending stop
operation on an endpoint, and calls in the prepare callback for
avoiding the crash above.
Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=49181
Reported-and-tested-by: Artem S. Tashkinov <t.artem@lycos.com>
Cc: <stable@vger.kernel.org> [v3.6]
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
sound/usb/endpoint.c | 13 +++++++++++++
sound/usb/endpoint.h | 1 +
sound/usb/pcm.c | 3 +++
3 files changed, 17 insertions(+)
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 7f78c6d..34de6f2 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -35,6 +35,7 @@
#define EP_FLAG_ACTIVATED 0
#define EP_FLAG_RUNNING 1
+#define EP_FLAG_STOPPING 2
/*
* snd_usb_endpoint is a model that abstracts everything related to an
@@ -502,10 +503,20 @@ static int wait_clear_urbs(struct snd_usb_endpoint *ep)
if (alive)
snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
alive, ep->ep_num);
+ clear_bit(EP_FLAG_STOPPING, &ep->flags);
return 0;
}
+/* sync the pending stop operation;
+ * this function itself doesn't trigger the stop operation
+ */
+void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep)
+{
+ if (ep && test_bit(EP_FLAG_STOPPING, &ep->flags))
+ wait_clear_urbs(ep);
+}
+
/*
* unlink active urbs.
*/
@@ -918,6 +929,8 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
if (wait)
wait_clear_urbs(ep);
+ else
+ set_bit(EP_FLAG_STOPPING, &ep->flags);
}
}
diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h
index 6376ccf..3d4c970 100644
--- a/sound/usb/endpoint.h
+++ b/sound/usb/endpoint.h
@@ -19,6 +19,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep);
void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
int force, int can_sleep, int wait);
+void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
void snd_usb_endpoint_free(struct list_head *head);
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index 37428f7..5c12a3f 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -552,6 +552,9 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
if (snd_BUG_ON(!subs->data_endpoint))
return -EIO;
+ snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint);
+ snd_usb_endpoint_sync_pending_stop(subs->data_endpoint);
+
/* some unit conversions in runtime */
subs->data_endpoint->maxframesize =
bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
--
1.8.0
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/