[alsa-devel] race conditions in alsa core

Trent Piepho xyzzy at speakeasy.org
Sun Sep 23 12:14:47 CEST 2007

In the process of trying to discover that kind of locking ALSA provides to a
driver and what the driver must take care of itself, I think I have found some
race conditions inside the alsa core.

I think there are likely quite a few, but this example was one of the easiest
to prove.

ALSA uses unlocked_ioctl, which means ioctl calls aren't protected by the BKL
and may run at the same time.

Some ioctls, for instance HW_PARAMS, don't appear to have any locking done by
ALSA.  It's entirely possible for two hw_params calls to run at the same time
on the same substream, and the ALSA core (and probably many drivers too) can't
handle this.

The following patch helps to create a larger window for the race condition, so
that it can be triggered consistently.

--- a/sound/core/pcm_native.c   Tue Sep 04 09:02:06 2007 +0000
+++ b/sound/core/pcm_native.c   Sat Sep 22 17:12:37 2007 -0700
@@ -411,6 +411,7 @@ static int snd_pcm_hw_params(struct snd_
 	runtime->channels = params_channels(params);
 	runtime->rate = params_rate(params);
 	runtime->period_size = params_period_size(params);
+	{ static unsigned long x=1; if(test_and_change_bit(0, &x)) msleep(10); }
 	runtime->periods = params_periods(params);
 	runtime->buffer_size = params_buffer_size(params);
 	runtime->tick_time = params_tick_time(params);

This causes every other call to snd_pcm_hw_params() to have a 10ms delay
between setting the period size and period count.  If there are two calls
within 10ms, the period count should come from the first call (which slept),
while the period size will be from the second call.

My test program will call the HW_PARAMS ioctl twice from different threads
running at the same time.  The first call will ask for the maximum number of
periods with minimum size, the second call for the minimum number of periods
of maximum size.

When prepare is called, we discover that the runtime is set for the maximum
number of periods of maximum size, which exceeds the buffer size.  Of course
even if it didn't exceed the buffer size, the params should be from one call
or the other, not some random combination of both.

I've attaching a very simple dummy ALSA driver which shows this.  All it does
is print timestamps and a few parameters when the various callback functions
are invoked.  The output looks like this:

as_open - 0.084 us
as_hw_params - 552.864 us periods 128, period size 1024
as_hw_params - 589.996 us periods 1, period size 131072
as_prepare - 11065.307 us periods 128, period size 131072
as_close - 11278.229 us

Notice how in prepare the runtime has parameters that are a combination of the
two hw_params calls.  This test program should trigger the same race with any
other ALSA driver.  Just adjust the period count and size.

The test program doesn't use the ALSA lib.  It does need the headers from the
alsa lib package to compile, since this seems to be the only place where user
space headers for the device interface are provided.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test_hwparams.c
Type: text/x-csrc
Size: 2482 bytes
Url : http://mailman.alsa-project.org/pipermail/alsa-devel/attachments/20070923/01975631/attachment.c 
-------------- next part --------------
A non-text attachment was scrubbed...
Name: alsa-simple.c
Type: text/x-csrc
Size: 3676 bytes
Url : http://mailman.alsa-project.org/pipermail/alsa-devel/attachments/20070923/01975631/attachment-0001.c 

More information about the Alsa-devel mailing list