Skip to content

Commit 2712ab5

Browse files
ujfalusilgirdwood
authored andcommitted
ASoC: SOF: sof-audio: harden recursive widget free walk
During widget FREE traversal, SOF walks DAPM sink paths recursively while widgets and paths can be torn down. This can lead to stale pointer usage in sof_free_widgets_in_path() when path entries disappear during recursion. Harden the FREE path by: - validating scheduler/pipeline pointers before recursive free - using a safe DAPM path iterator for sink traversal - carrying a stable widget-list snapshot through recursion - skipping NULL sink edges during traversal The safe traversal can leave path->walking set on surviving edges after FREE, which may short-circuit later DAPM walks and break consecutive playback open/stop cycles. Reset walking flags for widgets in the current DAPM list after FREE walks (including error paths) to keep subsequent traversals clean. This keeps teardown robust for module-remove race scenarios while preserving normal consecutive playback behavior. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
1 parent 78aaacf commit 2712ab5

1 file changed

Lines changed: 50 additions & 10 deletions

File tree

sound/soc/sof/sof-audio.c

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
103103
* decrement ref count for cores associated with all modules in the pipeline and clear
104104
* the complete flag
105105
*/
106-
if (swidget->id == snd_soc_dapm_scheduler) {
106+
if (swidget->id == snd_soc_dapm_scheduler && spipe) {
107107
int i;
108108

109109
for_each_set_bit(i, &spipe->core_mask, sdev->num_cores) {
@@ -115,16 +115,16 @@ static int sof_widget_free_unlocked(struct snd_sof_dev *sdev,
115115
err = ret;
116116
}
117117
}
118-
swidget->spipe->complete = 0;
118+
spipe->complete = 0;
119119
}
120120

121121
/*
122122
* free the scheduler widget (same as pipe_widget) associated with the current swidget.
123123
* skip for static pipelines
124124
*/
125-
if (swidget->spipe && swidget->dynamic_pipeline_widget &&
125+
if (spipe && spipe->pipe_widget && swidget->dynamic_pipeline_widget &&
126126
swidget->id != snd_soc_dapm_scheduler) {
127-
ret = sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget);
127+
ret = sof_widget_free_unlocked(sdev, spipe->pipe_widget);
128128
if (ret < 0 && !err)
129129
err = ret;
130130
}
@@ -548,15 +548,20 @@ sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget
548548
* free all widgets in the sink path starting from the source widget
549549
* (DAI type for capture, AIF type for playback)
550550
*/
551-
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget,
552-
int dir, struct snd_sof_pcm *spcm)
551+
static int sof_free_widgets_in_path_internal(struct snd_sof_dev *sdev,
552+
struct snd_soc_dapm_widget *widget,
553+
int dir, struct snd_sof_pcm *spcm,
554+
struct snd_soc_dapm_widget_list *list)
553555
{
554-
struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list;
555556
struct snd_sof_widget *swidget = widget->dobj.private;
556557
struct snd_soc_dapm_path *p;
558+
struct snd_soc_dapm_path *next_p;
557559
int err;
558560
int ret = 0;
559561

562+
if (!list)
563+
return 0;
564+
560565
if (is_virtual_widget(sdev, widget, __func__))
561566
return 0;
562567

@@ -576,23 +581,52 @@ static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dap
576581
ret = err;
577582
sink_free:
578583
/* free all widgets in the sink paths even in case of error to keep use counts balanced */
579-
snd_soc_dapm_widget_for_each_sink_path(widget, p) {
584+
snd_soc_dapm_widget_for_each_path_safe(widget, SND_SOC_DAPM_DIR_IN, p, next_p) {
580585
if (!p->walking) {
586+
if (!p->sink)
587+
continue;
588+
581589
if (!widget_in_list(list, p->sink))
582590
continue;
583591

584592
p->walking = true;
585593

586-
err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm);
594+
err = sof_free_widgets_in_path_internal(sdev, p->sink,
595+
dir, spcm, list);
587596
if (err < 0)
588597
ret = err;
589-
p->walking = false;
590598
}
591599
}
592600

593601
return ret;
594602
}
595603

604+
static int sof_free_widgets_in_path(struct snd_sof_dev *sdev,
605+
struct snd_soc_dapm_widget *widget,
606+
int dir, struct snd_sof_pcm *spcm)
607+
{
608+
return sof_free_widgets_in_path_internal(sdev, widget, dir, spcm,
609+
spcm->stream[dir].list);
610+
}
611+
612+
static void sof_reset_path_walking_flags(struct snd_soc_dapm_widget_list *list)
613+
{
614+
struct snd_soc_dapm_widget *widget;
615+
struct snd_soc_dapm_path *p;
616+
int i;
617+
618+
if (!list)
619+
return;
620+
621+
for_each_dapm_widgets(list, i, widget) {
622+
snd_soc_dapm_widget_for_each_sink_path(widget, p)
623+
p->walking = false;
624+
625+
snd_soc_dapm_widget_for_each_source_path(widget, p)
626+
p->walking = false;
627+
}
628+
}
629+
596630
/*
597631
* set up all widgets in the sink path starting from the source widget
598632
* (DAI type for capture, AIF type for playback).
@@ -727,11 +761,17 @@ sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm,
727761
return -EINVAL;
728762
}
729763
if (ret < 0) {
764+
if (op == SOF_WIDGET_FREE)
765+
sof_reset_path_walking_flags(list);
766+
730767
dev_err(sdev->dev, "Failed to %s connected widgets\n", str);
731768
return ret;
732769
}
733770
}
734771

772+
if (op == SOF_WIDGET_FREE)
773+
sof_reset_path_walking_flags(list);
774+
735775
return 0;
736776
}
737777

0 commit comments

Comments
 (0)