Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ALSA: hda: ext: fix locking in stream_release

The snd_hdac_ext_stream_release() routine uses the bus reg_lock, but
releases it before calling snd_hdac_stream_release() where the bus
reg_lock is taken again.

This creates a timing window where the link stream release could test
an invalid 'opened' boolean status and fail to recouple the host and
link parts.

Fix by exposing a locked version of snd_hdac_stream_release() and use
it without releasing the spinlock.

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://lore.kernel.org/r/20220919121041.43463-8-pierre-louis.bossart@linux.intel.com
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Pierre-Louis Bossart and committed by
Takashi Iwai
ac3467ad 53f4f6b4

+18 -4
+1
include/sound/hdaudio.h
··· 551 551 int idx, int direction, int tag); 552 552 struct hdac_stream *snd_hdac_stream_assign(struct hdac_bus *bus, 553 553 struct snd_pcm_substream *substream); 554 + void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev); 554 555 void snd_hdac_stream_release(struct hdac_stream *azx_dev); 555 556 struct hdac_stream *snd_hdac_get_stream(struct hdac_bus *bus, 556 557 int dir, int stream_tag);
+1 -1
sound/hda/ext/hdac_ext_stream.c
··· 384 384 spin_lock_irq(&bus->reg_lock); 385 385 if (hext_stream->decoupled && !hext_stream->link_locked) 386 386 snd_hdac_ext_stream_decouple_locked(bus, hext_stream, false); 387 + snd_hdac_stream_release_locked(&hext_stream->hstream); 387 388 spin_unlock_irq(&bus->reg_lock); 388 - snd_hdac_stream_release(&hext_stream->hstream); 389 389 break; 390 390 391 391 case HDAC_EXT_STREAM_TYPE_LINK:
+16 -3
sound/hda/hdac_stream.c
··· 366 366 EXPORT_SYMBOL_GPL(snd_hdac_stream_assign); 367 367 368 368 /** 369 + * snd_hdac_stream_release_locked - release the assigned stream 370 + * @azx_dev: HD-audio core stream to release 371 + * 372 + * Release the stream that has been assigned by snd_hdac_stream_assign(). 373 + * The bus->reg_lock needs to be taken at a higher level 374 + */ 375 + void snd_hdac_stream_release_locked(struct hdac_stream *azx_dev) 376 + { 377 + azx_dev->opened = 0; 378 + azx_dev->running = 0; 379 + azx_dev->substream = NULL; 380 + } 381 + EXPORT_SYMBOL_GPL(snd_hdac_stream_release_locked); 382 + 383 + /** 369 384 * snd_hdac_stream_release - release the assigned stream 370 385 * @azx_dev: HD-audio core stream to release 371 386 * ··· 391 376 struct hdac_bus *bus = azx_dev->bus; 392 377 393 378 spin_lock_irq(&bus->reg_lock); 394 - azx_dev->opened = 0; 395 - azx_dev->running = 0; 396 - azx_dev->substream = NULL; 379 + snd_hdac_stream_release_locked(azx_dev); 397 380 spin_unlock_irq(&bus->reg_lock); 398 381 } 399 382 EXPORT_SYMBOL_GPL(snd_hdac_stream_release);