From: Aohan Mei <ljp1205831794@gmail.com>
To: tiwai@suse.com
Cc: perex@perex.cz, linux-sound@vger.kernel.org,
Aohan Mei <henrymei@tencent.com>,
stable@vger.kernel.org
Subject: [PATCH] ALSA: pcm: Fix race between concurrent START and hw_params/hw_free
Date: Fri, 11 Sep 2026 13:15:59 +0800 [thread overview]
Message-ID: <20260911051605.3448607-1-ljp1205831794@gmail.com> (raw)
From: Aohan Mei <henrymei@tencent.com>
snd_pcm_hw_params() and snd_pcm_hw_free() validate the stream state
only once, under the stream lock at entry, and then free/realloc the
DMA buffer (snd_pcm_lib_free_pages() -> snd_pcm_set_runtime_buffer(NULL),
followed by the sleeping allocation in snd_pcm_lib_malloc_pages()) and
update the runtime fields without holding the stream lock. The state,
however, stays SNDRV_PCM_STATE_PREPARED until the final
snd_pcm_set_state() at the very end of the operation.
A concurrent SNDRV_PCM_IOCTL_START takes only the stream lock and
merely requires state == PREPARED in snd_pcm_pre_start(), so it can
slip into that window: snd_pcm_post_start() sets the state RUNNING,
arms the driver data plane and fills the initial silence through
snd_pcm_playback_silence(). The data plane then keeps operating on
the buffer that hw_params/hw_free is tearing down concurrently; once
snd_pcm_set_runtime_buffer(NULL) has cleared runtime->dma_area, the
silence fill in fill_silence() -> get_dma_ptr() dereferences a NULL
pointer, and on real hardware the device may additionally keep
DMA-ing into the freed pages.
Close the race by leaving the PREPARED state atomically with the
entry state check, inside the same stream lock critical section: a
START that already completed makes the hw_params/hw_free state check
fail with -EBADFD, and a later START observes SETUP and fails in
snd_pcm_pre_start() with -EBADFD as well, so the data plane can never
be armed while the buffer is being freed or reallocated. Both
functions impose SETUP as their resulting state anyway, hence this
does not change the state machine semantics visible to user space.
The buffer_mutex/buffer_accessing serialization introduced by the
earlier fixes for the prepare-vs-hw_params races cannot simply be
extended to the START path: the trigger action has to run under the
IRQ-off stream spinlock for atomic PCMs, and failing START with
-EBUSY whenever a read/write transfer is in flight would be a user
visible regression. Transitioning the state atomically at entry
avoids both problems.
Cc: stable@vger.kernel.org
Assisted-by: CodeBuddy:Kimi-K3
Signed-off-by: Aohan Mei <henrymei@tencent.com>
---
sound/core/pcm_native.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 62324282fcae..6040eb752873 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -805,6 +805,17 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
err = -EBADFD;
break;
}
+ /*
+ * The buffer free/realloc and the runtime field updates
+ * below run without the stream lock, while a concurrent
+ * SNDRV_PCM_IOCTL_START only requires state == PREPARED
+ * under that lock. Leave the PREPARED state atomically
+ * with the check above so that a racing START (and the
+ * data plane it arms) can no longer slip in and operate
+ * on the buffer while it is being freed or reallocated.
+ */
+ if (!err && runtime->state == SNDRV_PCM_STATE_PREPARED)
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP);
}
if (err)
goto unlock;
@@ -966,6 +977,13 @@ static int snd_pcm_hw_free(struct snd_pcm_substream *substream)
result = -EBADFD;
break;
}
+ /* Same race as in snd_pcm_hw_params(): leave the PREPARED
+ * state atomically with the check above, so that a racing
+ * START cannot arm the data plane while do_hw_free() tears
+ * down the buffer without the stream lock.
+ */
+ if (!result && runtime->state == SNDRV_PCM_STATE_PREPARED)
+ __snd_pcm_set_state(runtime, SNDRV_PCM_STATE_SETUP);
}
if (result)
goto unlock;
--
2.43.7
next reply other threads:[~2026-09-11 5:16 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-11 5:15 Aohan Mei [this message]
2026-09-11 6:39 ` [PATCH] ALSA: pcm: Fix race between concurrent START and hw_params/hw_free Takashi Iwai
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260911051605.3448607-1-ljp1205831794@gmail.com \
--to=ljp1205831794@gmail.com \
--cc=henrymei@tencent.com \
--cc=linux-sound@vger.kernel.org \
--cc=perex@perex.cz \
--cc=stable@vger.kernel.org \
--cc=tiwai@suse.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox