[PATCH v3] drm/vc4: hdmi: Unregister the ASoC card on unbind
From: Karl Mehltretter
Date: Tue Aug 25 2026 - 02:38:43 EST
vc4_hdmi, including the embedded ASoC card, is DRM-managed and remains
alive until the final reference to the DRM device is dropped. The card is
registered with devm_snd_soc_register_card() on the HDMI platform device
from the component bind callback, so its devres node starts out in the
component's devres group and would ordinarily be released at component
unbind, while vc4_hdmi is still alive.
Whenever an ASoC component is registered, the core retries every card
waiting for components. For a devm-managed card, each retry destroys and
re-adds its devres node. Because snd_soc_bind_card() requeues the card and
converts -EPROBE_DEFER to success, even a retry that still defers can move
the node outside the now-closed component devres group. It is then released
only at HDMI platform driver detach rather than component unbind.
With no DRM file open, as in the reproducer below, aggregate teardown
drops the final DRM reference. This frees vc4_hdmi before the HDMI
platform device's devres release, so snd_soc_unregister_card() accesses
freed memory:
# modprobe vc4; rmmod vc4
BUG: KASAN: slab-use-after-free in snd_soc_unregister_card
Read of size 1 at addr ffff0000456a8450 by task rmmod/262
devm_card_bind_release / devres_release_all / driver_detach
Allocated by task 171: drmm_kmalloc / vc4_hdmi_bind
Freed by task 262 (rmmod): drm_dev_put / component_del
If a DRM file remains open through platform detach, the final DRM release
is deferred until the last close and the ordering is reversed.
Register the card without devm and unregister it from the component unbind
callback, where both the HDMI device resources and vc4_hdmi are alive. This
makes the card lifetime independent of when the final DRM reference is
dropped.
Fixes: 42d99857d6f0 ("ASoC: core: Move all users to deferrable card binding")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Karl Mehltretter <kmehltretter@xxxxxxxxx>
---
v3:
- Clarify that DRM-managed memory is released when the final DRM
reference is dropped, not necessarily at driver unbind. With no open
DRM file this precedes the HDMI platform devres release; with a file
held open through detach the ordering is reversed. No code changes.
v2: https://lore.kernel.org/r/20260823173740.2983-1-kmehltretter@xxxxxxxxx/
- Preserve the existing ASoC component-lifetime comment and document at
the card registration site how deferrable binding can move the card's
devres node outside the component devres group.
- Point Fixes at the deferrable card binding change.
- Add the ASoC maintainers and linux-sound recipients, add the stable
trailer, and update Assisted-by to the current format.
v1: https://lore.kernel.org/r/20260822143218.68764-1-kmehltretter@xxxxxxxxx/
Tested on a Raspberry Pi 400 (BCM2711), v7.2-11658-g26260251022f, with
KASAN: report gone, rmmod/insmod loop clean.
drivers/gpu/drm/vc4/vc4_hdmi.c | 24 +++++++++++++++++++++---
drivers/gpu/drm/vc4/vc4_hdmi.h | 1 +
2 files changed, 22 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 17c8635c5afa..7e312932488a 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2422,12 +2422,18 @@ static int vc4_hdmi_audio_init(struct vc4_hdmi *vc4_hdmi)
* snd_soc_card_get_drvdata() if needed.
*/
snd_soc_card_set_drvdata(card, vc4_hdmi);
- ret = devm_snd_soc_register_card(dev, card);
+
+ /*
+ * Deferred card binding can move a devm registration outside the
+ * component devres group, so unregister the card explicitly at unbind.
+ */
+ ret = snd_soc_register_card(card);
if (ret)
- dev_err_probe(dev, ret, "Could not register sound card\n");
+ return dev_err_probe(dev, ret, "Could not register sound card\n");
- return ret;
+ vc4_hdmi->audio.card_registered = true;
+ return 0;
}
static irqreturn_t vc4_hdmi_hpd_irq_thread(int irq, void *priv)
@@ -3345,8 +3351,20 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data)
return ret;
}
+static void vc4_hdmi_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct vc4_hdmi *vc4_hdmi = dev_get_drvdata(dev);
+
+ if (vc4_hdmi->audio.card_registered) {
+ snd_soc_unregister_card(&vc4_hdmi->audio.card);
+ vc4_hdmi->audio.card_registered = false;
+ }
+}
+
static const struct component_ops vc4_hdmi_ops = {
.bind = vc4_hdmi_bind,
+ .unbind = vc4_hdmi_unbind,
};
static int vc4_hdmi_dev_probe(struct platform_device *pdev)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index 29d461d4ee49..444c73513d86 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -106,6 +106,7 @@ struct vc4_hdmi_audio {
struct snd_soc_dai_link_component platform;
struct snd_dmaengine_dai_dma_data dma_data;
bool streaming;
+ bool card_registered;
};
/* General HDMI hardware state. */
--
2.39.5 (Apple Git-154)