[PATCH] ASoC: topology: Guard against out-of-range mixer control shift
From: Daniel Golle
Date: Sat Jul 18 2026 - 13:35:00 EST
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@xxxxxxxxxxxxxxx
Signed-off-by: Daniel Golle <daniel@xxxxxxxxxxxxxx>
---
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