ALSA: hda/intel: Make sure to cancel irq-pending work at closing PCM stream

The pending irq work might be still floating while the assigned stream
has been already closed, which may lead to UAF, especially when
another async work for fasync is involved.

For addressing this, extend the hda_controller_ops for allowing the
extra cleanup procedure that is specific to the controller driver, and
make sure to cancel and sync the pending irq work at each PCM close
before releasing the resources.

Reported-by: Jake Lamberson <lamberson.jake@gmail.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Link: https://patch.msgid.link/20260519121157.28477-2-tiwai@suse.de
This commit is contained in:
Takashi Iwai 2026-05-19 14:11:53 +02:00
parent e36a88b33c
commit 33d3b6f0d8
3 changed files with 20 additions and 4 deletions

View File

@ -97,6 +97,8 @@ static int azx_pcm_close(struct snd_pcm_substream *substream)
trace_azx_pcm_close(chip, azx_dev);
scoped_guard(mutex, &chip->open_mutex) {
if (chip->ops->pcm_close)
chip->ops->pcm_close(chip, azx_dev);
azx_release_device(azx_dev);
if (hinfo->ops.close)
hinfo->ops.close(hinfo, apcm->codec, substream);

View File

@ -78,6 +78,8 @@ struct hda_controller_ops {
int (*position_check)(struct azx *chip, struct azx_dev *azx_dev);
/* enable/disable the link power */
int (*link_power)(struct azx *chip, bool enable);
/* additional hook for PCM */
void (*pcm_close)(struct azx *chip, struct azx_dev *azx_dev);
};
struct azx_pcm {

View File

@ -761,16 +761,27 @@ static void azx_irq_pending_work(struct work_struct *work)
}
/* clear irq_pending flags and assure no on-going workq */
static void hda_intel_stream_clear_irq_pending(struct azx_dev *azx_dev)
{
struct hda_intel_stream *istream = azx_dev_to_istream(azx_dev);
istream->irq_pending = false;
cancel_work_sync(&istream->irq_pending_work);
}
/* called at PCM close */
static void hda_intel_pcm_close(struct azx *chip, struct azx_dev *azx_dev)
{
hda_intel_stream_clear_irq_pending(azx_dev);
}
static void azx_clear_irq_pending(struct azx *chip)
{
struct hdac_bus *bus = azx_bus(chip);
struct hdac_stream *s;
list_for_each_entry(s, &bus->stream_list, list) {
struct azx_dev *azx_dev = stream_to_azx_dev(s);
struct hda_intel_stream *istream = azx_dev_to_istream(azx_dev);
istream->irq_pending = false;
cancel_work_sync(&istream->irq_pending_work);
hda_intel_stream_clear_irq_pending(stream_to_azx_dev(s));
}
}
@ -2131,6 +2142,7 @@ static const struct dmi_system_id driver_denylist_dmi[] = {
static const struct hda_controller_ops pci_hda_ops = {
.disable_msi_reset_irq = disable_msi_reset_irq,
.position_check = azx_position_check,
.pcm_close = hda_intel_pcm_close,
};
static DECLARE_BITMAP(probed_devs, SNDRV_CARDS);