Skip to content

Commit e794e17

Browse files
tiwaigregkh
authored andcommitted
ALSA: core: Fix potential data race at fasync handling
commit 8146cd3 upstream. In snd_fasync_work_fn(), which is the offload work for traversing and processing the pending fasync list, the call of kill_fasync() is done outside the snd_fasync_lock for avoiding deadlocks. The problem is that its the references of fasync->on, fasync->signal and fasync->poll are done there also outside the lock. Since these may be modified by snd_kill_fasync() call concurrently from other process, inconsistent values might be passed to kill_fasync(). Although there shouldn't be critical UAF, it's still better to be addressed. This patch moves the kill_fasync() argument evaluations inside the snd_fasync_lock for avoiding the data races above. The handling in fasync->on flag is optimized in the loop to skip directly. Also, for more clarity, snd_fasync_free() takes the lock and unlink the pending entry more directly instead of clearing fasync->on flag. Reported-by: Jake Lamberson <lamberson.jake@gmail.com> Fixes: ef34a0a ("ALSA: core: Add async signal helpers") Cc: <stable@vger.kernel.org> Link: https://patch.msgid.link/20260420061721.3253644-1-tiwai@suse.de Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent fafab8b commit e794e17

1 file changed

Lines changed: 10 additions & 3 deletions

File tree

sound/core/misc.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,18 @@ static LIST_HEAD(snd_fasync_list);
171171
static void snd_fasync_work_fn(struct work_struct *work)
172172
{
173173
struct snd_fasync *fasync;
174+
int signal, poll;
174175

175176
spin_lock_irq(&snd_fasync_lock);
176177
while (!list_empty(&snd_fasync_list)) {
177178
fasync = list_first_entry(&snd_fasync_list, struct snd_fasync, list);
178179
list_del_init(&fasync->list);
180+
if (!fasync->on)
181+
continue;
182+
signal = fasync->signal;
183+
poll = fasync->poll;
179184
spin_unlock_irq(&snd_fasync_lock);
180-
if (fasync->on)
181-
kill_fasync(&fasync->fasync, fasync->signal, fasync->poll);
185+
kill_fasync(&fasync->fasync, signal, poll);
182186
spin_lock_irq(&snd_fasync_lock);
183187
}
184188
spin_unlock_irq(&snd_fasync_lock);
@@ -234,7 +238,10 @@ void snd_fasync_free(struct snd_fasync *fasync)
234238
{
235239
if (!fasync)
236240
return;
237-
fasync->on = 0;
241+
242+
scoped_guard(spinlock_irq, &snd_fasync_lock)
243+
list_del_init(&fasync->list);
244+
238245
flush_work(&snd_fasync_work);
239246
kfree(fasync);
240247
}

0 commit comments

Comments
 (0)