[alsa-devel] [PATCH v2 2/2] ASoC: STI: Fix null ptr deference in IRQ handler
Takashi Iwai
tiwai at suse.de
Thu Mar 23 20:02:53 CET 2017
On Thu, 23 Mar 2017 19:39:55 +0100,
Arnaud Pouliquen wrote:
>
> With RTlinux a race condition has been found that leads to NULL ptr crash:
> - On CPU 0: uni_player_irq_handler is called to treat XRUN
> "(player->state == UNIPERIF_STATE_STOPPED)" is FALSE so status is checked,
> dev_err(player->dev, "FIFO underflow error detected") is printed
> and then snd_pcm_stream_lock should be called to lock stream for stopping.
> - On CPU 1: application stop and close the stream.
> Issue is that the stop and shutdown functions are executed while
> "FIFO underflow error detected" is printed.
> So when CPU 0 calls snd_pcm_stream_lock, player->substream is already null.
>
> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen at st.com>
>
> ---
> V2: Add spinlock to protect player/reader->substream
> ---
> sound/soc/sti/uniperif.h | 1 +
> sound/soc/sti/uniperif_player.c | 34 +++++++++++++++++++++++-----------
> sound/soc/sti/uniperif_reader.c | 24 ++++++++++++++++++++----
> 3 files changed, 44 insertions(+), 15 deletions(-)
>
> diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h
> index d487dd2..cfcb0ea 100644
> --- a/sound/soc/sti/uniperif.h
> +++ b/sound/soc/sti/uniperif.h
> @@ -1299,6 +1299,7 @@ struct uniperif {
> int ver; /* IP version, used by register access macros */
> struct regmap_field *clk_sel;
> struct regmap_field *valid_sel;
> + spinlock_t irq_lock; /* used to prevent race condition with IRQ */
>
> /* capabilities */
> const struct snd_pcm_hardware *hw;
> diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
> index 60ae31a..8f3a582 100644
> --- a/sound/soc/sti/uniperif_player.c
> +++ b/sound/soc/sti/uniperif_player.c
> @@ -65,10 +65,13 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
> unsigned int status;
> unsigned int tmp;
>
> - if (player->state == UNIPERIF_STATE_STOPPED) {
> - /* Unexpected IRQ: do nothing */
> - return IRQ_NONE;
> - }
> + spin_lock(&player->irq_lock);
> + if (!player->substream)
> + goto IRQ_END;
This will be NULL-dereference, as it calls snd_pcm_stream_unlock() there.
Also, use lower letters for labels.
> +
> + snd_pcm_stream_lock(player->substream);
Actually we don't need to take this lock here any longer since we have
irq_lock. Better to keep the stream lock only around the stop.
> + if (player->state == UNIPERIF_STATE_STOPPED)
> + goto IRQ_END;
>
> /* Get interrupt status & clear them immediately */
> status = GET_UNIPERIF_ITS(player);
> @@ -88,9 +91,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
> SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player);
>
> /* Stop the player */
> - snd_pcm_stream_lock(player->substream);
> snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
> - snd_pcm_stream_unlock(player->substream);
BTW, these three calls can be simplified with snd_pcm_stop_xrun().
Takashi
More information about the Alsa-devel
mailing list