NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
kern/45909: Use of MIDI over USB interfaces crashes NetBSD
>Number: 45909
>Category: kern
>Synopsis: Use of MIDI over USB interfaces crashes NetBSD
>Confidential: no
>Severity: non-critical
>Priority: medium
>Responsible: kern-bug-people
>State: open
>Class: sw-bug
>Submitter-Id: net
>Arrival-Date: Wed Feb 01 15:10:00 +0000 2012
>Originator: Tom Ivar Helbekkmo
>Release: NetBSD 5.99.60 as per 2012-01-24
>Organization:
>Environment:
System: NetBSD athene.hamartun.priv.no 5.99.60 NetBSD 5.99.60 (ATHENE) #31: Tue
Jan 31 21:40:41 CET 2012
tih%athene.hamartun.priv.no@localhost:/usr/obj/sys/arch/i386/compile.i386/ATHENE
i386
Architecture: i386
Machine: i386
>Description:
There are problems with locking in the current version of
/sys/dev/usb/umidi.c, causing NetBSD to crash if an attempt is made to
read from a USB MIDI device. The reason turns out to be that
assumptions about the kernel lock being taken before certain
operations, and about the taking and releasing of mutex spin locks,
are not being correctly upheld by umidi.c.
Specifically, there are four problems:
1: the kernel lock needs to be held while an input transfer is
initiated (the KASSERT(KERNEL_LOCKED_P()) is found in the function
usbd_transfer() in /sys/dev/usb/usbdi.c). I added code to grab and
release the kernel lock to open_in_jack() in /sys/dev/usb/umidi.c,
around the call to start_input_transfer().
2: the kernel lock must, again, be held while a transfer is aborted
(by calling usbd_abort_xfer()). This happens while closing a MIDI
interface that was open for reading, and midiclose(), in
/sys/dev/midi.c, does not itself grab the lock. Again, a KASSERT()
fails for this situation. I added code to grab and release the
kernel lock to close_in_jack() in /sys/dev/usb/umidi.c.
3: The softc spinlock mutex must *not* be held during the above
mentioned call to usbd_abort_xfer(), but midiclose() does hold it
while calling the umidi driver's closing function. This fails
because usbd_abort_xfer() wants to relinquish the CPU to allow the
hardware time to catch up, and a context switch is then attempted
with the mutex held. I added code to release and re-grab the mutex
around the call to usbd_abort_xfer() in /sys/dev/usb/umidi.c.
4: After a MIDI device that was open for read has been closed (with
the above fixes in place), reopening it for read fails because of
the state it is left in by usbd_abort_xfer(). Specifically, the
first attempt to start an input transfer on it returns
USBD_CANCELLED, reflecting the aborted transfer. This is benign,
and I simply added that return code to the set of those that are
accepted as non-errors by the umidi driver.
>How-To-Repeat:
Attempt to use a USB MIDI interface on -current, reading and writing
it both directly (/dev/rmidi?) and through the sequencer (/dev/music).
Observe that writing works, but reading crashes NetBSD.
>Fix:
Apply the following patch (thoroughly tested with two Roland UM-ONE):
Index: umidi.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/umidi.c,v
retrieving revision 1.55
diff -u -r1.55 umidi.c
--- umidi.c 23 Dec 2011 00:51:47 -0000 1.55
+++ umidi.c 1 Feb 2012 13:31:11 -0000
@@ -64,7 +64,7 @@
#define DPRINTFN(n,x) if (umididebug >= (n)) printf x
#include <sys/time.h>
static struct timeval umidi_tv;
-int umididebug = 0;
+int umididebug = 1;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
@@ -255,6 +255,7 @@
error:
aprint_error_dev(self, "disabled.\n");
sc->sc_dying = 1;
+ KERNEL_UNLOCK_ONE(curlwp);
return;
}
@@ -349,7 +350,8 @@
if ((mididev->flags & FREAD) && mididev->in_jack) {
err = open_in_jack(mididev->in_jack, arg, iintr);
if ( err != USBD_NORMAL_COMPLETION
- && err != USBD_IN_PROGRESS )
+ && err != USBD_IN_PROGRESS
+ && err != USBD_CANCELLED )
goto bad;
}
@@ -1125,11 +1127,14 @@
jack->u.in.intr = intr;
jack->opened = 1;
if (ep->num_open++ == 0 && UE_GET_DIR(ep->addr)==UE_DIR_IN) {
+ KERNEL_LOCK(1, curlwp);
err = start_input_transfer(ep);
if (err != USBD_NORMAL_COMPLETION &&
- err != USBD_IN_PROGRESS) {
+ err != USBD_IN_PROGRESS &&
+ err != USBD_CANCELLED) {
ep->num_open--;
}
+ KERNEL_UNLOCK_ONE(curlwp);
}
return err;
@@ -1165,10 +1170,20 @@
static void
close_in_jack(struct umidi_jack *jack)
{
+ struct umidi_endpoint *ep;
+ struct umidi_softc *sc;
+
if (jack->opened) {
+ ep = jack->endpoint;
+ sc = ep->sc;
+ KASSERT(mutex_owned(&sc->sc_lock));
jack->opened = 0;
if (--jack->endpoint->num_open == 0) {
- usbd_abort_pipe(jack->endpoint->pipe);
+ KERNEL_LOCK(1, curlwp);
+ mutex_exit(&sc->sc_lock);
+ usbd_abort_pipe(ep->pipe);
+ mutex_enter(&sc->sc_lock);
+ KERNEL_UNLOCK_ONE(curlwp);
}
}
}
@@ -1415,12 +1430,17 @@
static usbd_status
start_input_transfer(struct umidi_endpoint *ep)
{
+ usbd_status rv;
+
+ DPRINTFN(200,("umidi in transfer: start %p length %u\n",
+ ep->buffer, ep->buffer_size));
usbd_setup_xfer(ep->xfer, ep->pipe,
(usbd_private_handle)ep,
ep->buffer, ep->buffer_size,
USBD_SHORT_XFER_OK | USBD_NO_COPY,
USBD_NO_TIMEOUT, in_intr);
- return usbd_transfer(ep->xfer);
+ rv = usbd_transfer(ep->xfer);
+ return rv;
}
static usbd_status
Home |
Main Index |
Thread Index |
Old Index