[PATCH] ASoC: topology: Guard against out-of-range mixer control shift

Daniel Golle daniel at makrotopia.org
Sat Jul 18 10:34:42 PDT 2026


soc_tplg_control_dmixer_create() copies the per-channel "shift" field from
the topology file verbatim into struct soc_mixer_control. That value is
later used as a shift amount in soc_get_volsw() / soc_put_volsw() (e.g.
"reg_val >> shift" and "mask << shift"). Topology data is loaded from an
untrusted firmware file, and a shift >= 32 (the width of the register
value) is undefined behaviour when applied.

This is not hypothetical: mediatek/sof-tplg/sof-mt8186.tplg from
linux-firmware contains a control whose channel shift is 0x01000000, so
every read of that control (e.g. from alsamixer) emits:

  UBSAN: shift-out-of-bounds in sound/soc/soc-ops.c:117:21
  shift exponent 16777216 is too large for 32-bit type 'unsigned int'

tplg_chan_get_shift() can also return -EINVAL when no matching channel is
found, which becomes a large value once stored in the unsigned shift
field; the same guard covers that case.

Clamp an out-of-range shift to 0 and warn, so the rest of the card stays
usable instead of failing the whole topology load.

Fixes: 8a9782346dcc ("ASoC: topology: Add topology core")
Cc: stable at vger.kernel.org
Signed-off-by: Daniel Golle <daniel at makrotopia.org>
---
 sound/soc/soc-topology.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 35cbe29d2275..dd94eb9460d3 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -672,6 +672,16 @@ static int soc_tplg_control_dmixer_create(struct soc_tplg *tplg, struct snd_kcon
 	sm->shift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FL);
 	sm->rshift = tplg_chan_get_shift(tplg, mc->channel, SNDRV_CHMAP_FR);
 
+	if (sm->shift >= 32 || sm->rshift >= 32) {
+		dev_warn(tplg->dev,
+			 "ASoC: out-of-range shift for control %s, clamping to 0\n",
+			 mc->hdr.name);
+		if (sm->shift >= 32)
+			sm->shift = 0;
+		if (sm->rshift >= 32)
+			sm->rshift = 0;
+	}
+
 	sm->max = le32_to_cpu(mc->max);
 	sm->min = le32_to_cpu(mc->min);
 	sm->invert = le32_to_cpu(mc->invert);

base-commit: 0718283ab28bc3907e10b61a6b4be6fefa1cbb2f
-- 
2.55.0



More information about the Linux-mediatek mailing list