* [PATCH v6 1/4] ASoC: rt5668: cancel delayed works on component removal
2026-11-02 16:53 [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
@ 2026-11-02 16:53 ` Tristan Madani
2026-11-05 15:55 ` sashiko-bot
2026-11-02 16:53 ` [PATCH v6 2/4] ASoC: rt5659: cancel jack_detect_work " Tristan Madani
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Tristan Madani @ 2026-11-02 16:53 UTC (permalink / raw)
To: Mark Brown, Liam Girdwood
Cc: Charles Keepax, Oder Chiou, Bard Liao, Jaroslav Kysela,
Takashi Iwai, Jorge Eduardo Candelaria, Margarita Olaya Cabrera,
Misael Lopez Cruz, patches, linux-sound, linux-kernel, stable,
tristan
From: Tristan Madani <tristan@talencesecurity.com>
rt5668_remove() does not disable the IRQ or cancel the pending
jack_detect_work and jd_check_work delayed works. If the component is
unbound while work is pending, the handlers access freed memory.
Track IRQ acquisition in rt5668_i2c_probe() via an irq_acquired flag.
In rt5668_remove(), disable the IRQ (if acquired) and cancel both
delayed works before resetting the codec. Re-enable the IRQ in
rt5668_probe() when a sound card rebinds to prevent permanent IRQ
silencing across card unbind/rebind cycles.
Fixes: d59fb2856223 ("ASoC: rt5668: add rt5668B codec driver")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/rt5668.c | 20 +++++++++++++++++++-
1 file changed, 19 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index cd6245d6b91af..29d70802a046a 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -62,6 +62,9 @@ struct rt5668_priv {
int pll_out;
int jack_type;
+
+ bool irq_acquired;
+ bool irq_disabled;
};
static const struct reg_default rt5668_reg[] = {
@@ -2216,12 +2219,26 @@ static int rt5668_probe(struct snd_soc_component *component)
rt5668->component = component;
+ if (rt5668->irq_disabled) {
+ enable_irq(to_i2c_client(component->dev)->irq);
+ rt5668->irq_disabled = false;
+ }
+
return 0;
}
static void rt5668_remove(struct snd_soc_component *component)
{
struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+
+ if (rt5668->irq_acquired) {
+ disable_irq(i2c->irq);
+ rt5668->irq_disabled = true;
+ }
+
+ cancel_delayed_work_sync(&rt5668->jack_detect_work);
+ cancel_delayed_work_sync(&rt5668->jd_check_work);
rt5668_reset(rt5668->regmap);
}
@@ -2555,7 +2572,8 @@ static int rt5668_i2c_probe(struct i2c_client *i2c)
| IRQF_ONESHOT, "rt5668", rt5668);
if (ret)
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
-
+ else
+ rt5668->irq_acquired = true;
}
return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668,
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v6 1/4] ASoC: rt5668: cancel delayed works on component removal
2026-11-02 16:53 ` [PATCH v6 1/4] ASoC: rt5668: cancel delayed works on component removal Tristan Madani
@ 2026-11-05 15:55 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-05 15:55 UTC (permalink / raw)
To: Tristan Madani; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] ASoC: rt5668: uncancelled delayed work on I2C unbind in rt5668_remove()
- [High] ASoC: rt5668: stale jack and regcache state across rebind in rt5668_remove()
--
commit 9f28175a1bd0b2b45e9d47c7fba83c88de116699
Author: Tristan Madani <tristan@talencesecurity.com>
ASoC: rt5668: cancel delayed works on component removal
This patch tracks IRQ acquisition in rt5668_i2c_probe(), disables the IRQ
and cancels the pending jack_detect_work and jd_check_work delayed works in
rt5668_remove(), and re-enables the IRQ in rt5668_probe() on sound card
rebind.
> diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
> index cd6245d6b91af..29d70802a046a 100644
> --- a/sound/soc/codecs/rt5668.c
> +++ b/sound/soc/codecs/rt5668.c
[ ... ]
> @@ -2216,12 +2219,26 @@ static int rt5668_probe(struct snd_soc_component *component)
>
> rt5668->component = component;
>
> + if (rt5668->irq_disabled) {
> + enable_irq(to_i2c_client(component->dev)->irq);
> + rt5668->irq_disabled = false;
> + }
> +
> return 0;
> }
[Severity: High]
As reported by Sashiko on v5 [1], when a sound card unbinds, rt5668_remove()
calls rt5668_reset(rt5668->regmap), which writes 0 to RT5668_RESET and
resets all codec hardware registers to power-on defaults, without resetting
or dirtying the REGCACHE_MAPLE regmap cache and without clearing
rt5668->hs_jack, rt5668->jack_type, or rt5668->component.
When a sound card subsequently binds and rt5668_probe() re-enables the IRQ,
can this leave the driver and hardware out of sync in three ways?
1. If the previous sound card unbound and freed its snd_soc_jack without
calling snd_soc_component_set_jack(component, NULL, NULL), rt5668->hs_jack
remains a dangling pointer. Even if set_jack was called with NULL,
rt5668_set_jack_detect() does not check for !hs_jack before enabling JD1 and
queueing jack_detect_work. When rt5668_probe() calls enable_irq() on rebind,
if the new card does not call set_jack or if an interrupt queues
jack_detect_work before set_jack updates hs_jack, could
rt5668_jack_detect_handler() pass the dangling rt5668->hs_jack pointer to
snd_soc_jack_report()?
2. When the rebound card calls rt5668_set_jack_detect(),
snd_soc_component_update_bits() and regmap_update_bits() on the non-volatile
cached registers RT5668_CBJ_CTRL_3, RT5668_SAR_IL_CMD_1, RT5668_GPIO_CTRL_1
(RT5668_GP1_PIN_IRQ), RT5668_RC_CLK_CTRL, RT5668_PWR_ANLG_2, and
RT5668_IRQ_CTRL_2 (RT5668_JD1_EN) will see those bits already set in the
stale regmap cache from the first bind and skip writing them to the reset
hardware, leaving the physical IRQ pin and jack detection disabled.
3. If a headset was plugged in when the card unbound (rt5668->jack_type != 0),
rt5668_jack_detect_handler() after rebind will see rt5668->jack_type != 0 and
skip the initial rt5668_headset_detect(rt5668->component, 1) configuration on
the reset hardware:
sound/soc/codecs/rt5668.c:rt5668_jack_detect_handler() {
...
if (rt5668->jack_type == 0) {
/* jack was out, report jack type */
rt5668->jack_type =
rt5668_headset_detect(rt5668->component, 1);
} else {
/* jack is already in, report button event */
rt5668->jack_type = SND_JACK_HEADSET;
btn_type = rt5668_button_detect(rt5668->component);
...
}
Should rt5668_remove() also clear rt5668->hs_jack, rt5668->jack_type, and
rt5668->component, and reinitialize or mark the regmap cache dirty after
rt5668_reset()?
> static void rt5668_remove(struct snd_soc_component *component)
> {
> struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
> + struct i2c_client *i2c = to_i2c_client(component->dev);
> +
> + if (rt5668->irq_acquired) {
> + disable_irq(i2c->irq);
> + rt5668->irq_disabled = true;
> + }
> +
> + cancel_delayed_work_sync(&rt5668->jack_detect_work);
> + cancel_delayed_work_sync(&rt5668->jd_check_work);
>
> rt5668_reset(rt5668->regmap);
> }
[Severity: High]
As reported by Sashiko on v2 [2], in rt5668_i2c_probe(), the driver
initializes rt5668->jack_detect_work and rt5668->jd_check_work and requests
i2c->irq via devm_request_threaded_irq() before calling
devm_snd_soc_register_component() and before any sound card binds.
If i2c->irq fires while no sound card is bound or instantiated (or if
devm_snd_soc_register_component() fails), rt5668_irq() queues
rt5668->jack_detect_work, which then re-arms itself every 15 ms in
rt5668_jack_detect_handler():
sound/soc/codecs/rt5668.c:rt5668_jack_detect_handler() {
...
if (!rt5668->component ||
!snd_soc_card_is_instantiated(rt5668->component->card)) {
/* card not yet ready, try later */
mod_delayed_work(system_power_efficient_wq,
&rt5668->jack_detect_work, msecs_to_jiffies(15));
return;
}
...
}
Because rt5668_remove() is the snd_soc_component_driver.remove callback, the
ASoC core only invokes it when a sound card was bound to the component
(snd_soc_del_component_unlocked() skips snd_soc_unbind_card() when card is
NULL, and snd_soc_component_remove() returns early when !component->card).
Meanwhile, rt5668_i2c_driver has no .remove callback and registers no devm
action to disable the IRQ or cancel the delayed works.
If the I2C device is unbound while no sound card is bound (or if
devm_snd_soc_register_component() fails after an IRQ fires), won't
rt5668_remove() be skipped, allowing devres to free struct rt5668_priv while
rt5668->jack_detect_work remains pending and self-rearming?
[1] https://lore.kernel.org/r/20261030174837.1839402-2-tristan@talencesecurity.com
[2] https://lore.kernel.org/r/sashiko-outbox-190014@kernel.org
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102165340.1558853-1-tristmd@gmail.com?part=1
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v6 2/4] ASoC: rt5659: cancel jack_detect_work on component removal
2026-11-02 16:53 [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
2026-11-02 16:53 ` [PATCH v6 1/4] ASoC: rt5668: cancel delayed works on component removal Tristan Madani
@ 2026-11-02 16:53 ` Tristan Madani
2026-11-05 16:08 ` sashiko-bot
2026-11-02 16:53 ` [PATCH v6 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
` (2 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Tristan Madani @ 2026-11-02 16:53 UTC (permalink / raw)
To: Mark Brown, Liam Girdwood
Cc: Charles Keepax, Oder Chiou, Bard Liao, Jaroslav Kysela,
Takashi Iwai, Jorge Eduardo Candelaria, Margarita Olaya Cabrera,
Misael Lopez Cruz, patches, linux-sound, linux-kernel, stable,
tristan
From: Tristan Madani <tristan@talencesecurity.com>
rt5659_remove() resets the codec via regmap_write() but does not cancel
the pending jack_detect_work. Since the IRQ is registered with
devm_request_threaded_irq(), it remains active until after the component
remove callback returns. The IRQ handler (rt5659_irq) schedules
jack_detect_work, so a late interrupt can queue work that runs against a
reset codec.
Track successful IRQ acquisition with a flag so that disable_irq()
only quiesces a line this driver actually owns. Guard the
cancel_delayed_work_sync() call with a flag that tracks whether the
work was initialized, since INIT_DELAYED_WORK is only called for
specific jd_src configurations (JD3, JD_HDA_HEADER) and cancelling
an uninitialized work is undefined behavior.
On component remove, disable the IRQ to synchronize with any running
handler and prevent new enqueues, then drain the residual
jack_detect_work synchronously before resetting the hardware. On the
next component probe (card rebind), re-enable the IRQ to restore jack
detection.
Fixes: d3cb2de2479b ("ASoC: rt5659: add rt5659 codec driver")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/rt5659.c | 20 +++++++++++++++++++-
sound/soc/codecs/rt5659.h | 3 +++
2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index af8051fa69f89..68f7d5673e190 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3667,6 +3667,11 @@ static int rt5659_probe(struct snd_soc_component *component)
rt5659->component = component;
+ if (rt5659->irq_disabled) {
+ enable_irq(to_i2c_client(component->dev)->irq);
+ rt5659->irq_disabled = false;
+ }
+
switch (rt5659->pdata.jd_src) {
case RT5659_JD_HDA_HEADER:
break;
@@ -3684,6 +3689,15 @@ static int rt5659_probe(struct snd_soc_component *component)
static void rt5659_remove(struct snd_soc_component *component)
{
struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+ struct i2c_client *i2c = to_i2c_client(component->dev);
+
+ if (rt5659->irq_acquired) {
+ disable_irq(i2c->irq);
+ rt5659->irq_disabled = true;
+ }
+
+ if (rt5659->jack_detect_work_inited)
+ cancel_delayed_work_sync(&rt5659->jack_detect_work);
regmap_write(rt5659->regmap, RT5659_RESET, 0);
}
@@ -4283,6 +4297,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_2, 0x0040);
INIT_DELAYED_WORK(&rt5659->jack_detect_work,
rt5659_jack_detect_work);
+ rt5659->jack_detect_work_inited = true;
break;
case RT5659_JD_HDA_HEADER:
regmap_write(rt5659->regmap, RT5659_GPIO_CTRL_3, 0x8000);
@@ -4292,6 +4307,7 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
regmap_write(rt5659->regmap, RT5659_IRQ_CTRL_1, 0x0040);
INIT_DELAYED_WORK(&rt5659->jack_detect_work,
rt5659_jack_detect_intel_hd_header);
+ rt5659->jack_detect_work_inited = true;
rt5659_intel_hd_header_probe_setup(rt5659);
break;
default:
@@ -4302,7 +4318,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c)
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5659", rt5659);
- if (ret)
+ if (!ret)
+ rt5659->irq_acquired = true;
+ else
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
/* Enable IRQ output for GPIO1 pin any way */
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index b49fd8baf4e7e..e10b3844501b9 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1813,6 +1813,9 @@ struct rt5659_priv {
int jack_type;
bool hda_hp_plugged;
bool hda_mic_plugged;
+ bool irq_acquired;
+ bool irq_disabled;
+ bool jack_detect_work_inited;
};
int rt5659_set_jack_detect(struct snd_soc_component *component,
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v6 2/4] ASoC: rt5659: cancel jack_detect_work on component removal
2026-11-02 16:53 ` [PATCH v6 2/4] ASoC: rt5659: cancel jack_detect_work " Tristan Madani
@ 2026-11-05 16:08 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-11-05 16:08 UTC (permalink / raw)
To: Tristan Madani; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] ASoC: rt5659: uncancelled jack_detect_work on I2C teardown without card
- [High] ASoC: rt5659: broken jack detect and dangling hs_jack in rt5659_probe()
--
commit 7e33f0dd93e7476a57b77262485f2364ebdfe711
Author: Tristan Madani <tristan@talencesecurity.com>
ASoC: rt5659: cancel jack_detect_work on component removal
This commit disables the codec IRQ and cancels pending jack_detect_work
in rt5659_remove() before resetting the codec, and re-enables the IRQ
in rt5659_probe().
> diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
> index af8051fa69f89..68f7d5673e190 100644
> --- a/sound/soc/codecs/rt5659.c
> +++ b/sound/soc/codecs/rt5659.c
> @@ -3667,6 +3667,11 @@ static int rt5659_probe(struct snd_soc_component *component)
>
> rt5659->component = component;
>
> + if (rt5659->irq_disabled) {
> + enable_irq(to_i2c_client(component->dev)->irq);
> + rt5659->irq_disabled = false;
> + }
[Severity: High]
As reported by Sashiko on v5 [1], does re-enabling the IRQ here in
rt5659_probe() work as intended after rt5659_remove() has reset the
codec, and can it race with a dangling rt5659->hs_jack pointer?
At the end of rt5659_remove(), writing 0 to RT5659_RESET resets all
codec hardware registers to their power-on defaults, clearing the GPIO1
IRQ pin mux (RT5659_GP1_PIN_IRQ in RT5659_GPIO_CTRL_1) and the jack
detection and IRQ control registers (RT5659_EJD_CTRL_1,
RT5659_RC_CLK_CTRL, RT5659_GPIO_CTRL_3, RT5659_JD_CTRL_1,
RT5659_IRQ_CTRL_1, RT5659_IRQ_CTRL_2, RT5659_PWR_ANLG_1,
RT5659_PWR_ANLG_2) that were programmed only once in rt5659_i2c_probe().
Because RT5659_RESET is a volatile register in
rt5659_volatile_register(), the REGCACHE_RBTREE regmap cache is also not
marked dirty or resynced on re-probe, leaving the codec unable to
generate jack-detection interrupts after card rebind.
In addition, rt5659_remove() does not clear rt5659->hs_jack or
rt5659->component. When a sound card unbinds, its card-owned struct
snd_soc_jack is freed, leaving rt5659->hs_jack dangling. On card rebind:
snd_soc_component_probe()
rt5659_probe()
enable_irq()
runs before snd_soc_component_init() invokes the machine driver's init
callback to register a new snd_soc_jack via rt5659_set_jack_detect().
If an interrupt fires after enable_irq() unmasks the line before a new
jack is set (or if the rebound card does not call
rt5659_set_jack_detect() or fails later during bind), jack_detect_work
will pass the dangling rt5659->hs_jack pointer to
snd_soc_jack_report().
> +
> switch (rt5659->pdata.jd_src) {
> case RT5659_JD_HDA_HEADER:
> break;
> @@ -3684,6 +3689,15 @@ static int rt5659_probe(struct snd_soc_component *component)
> static void rt5659_remove(struct snd_soc_component *component)
> {
> struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
> + struct i2c_client *i2c = to_i2c_client(component->dev);
> +
> + if (rt5659->irq_acquired) {
> + disable_irq(i2c->irq);
> + rt5659->irq_disabled = true;
> + }
> +
> + if (rt5659->jack_detect_work_inited)
> + cancel_delayed_work_sync(&rt5659->jack_detect_work);
[Severity: High]
As reported by Sashiko on v5 [1], can rt5659->jack_detect_work still be
left pending on I2C device teardown when no ASoC card is bound, or
during system shutdown?
In rt5659_i2c_probe(), when rt5659->pdata.jd_src is RT5659_JD3 or
RT5659_JD_HDA_HEADER, the driver initializes rt5659->jack_detect_work,
enables codec jack-detection interrupts, registers rt5659_irq() via
devm_request_threaded_irq(), and enables RT5659_GP1_PIN_IRQ in
RT5659_GPIO_CTRL_1 before calling devm_snd_soc_register_component().
Once devm_request_threaded_irq() succeeds, any interrupt on i2c->irq
invokes rt5659_irq() and queues rt5659->jack_detect_work onto
system_power_efficient_wq with a 250 ms delay.
However, disable_irq() and cancel_delayed_work_sync() are placed only in
rt5659_remove() (the snd_soc_component_driver remove callback), while
rt5659_i2c_driver defines no remove callback and registers no devm
work-cancellation action. In the ASoC core, snd_soc_component_remove()
only calls component->driver->remove() when component->card is non-NULL
and the component was probed.
If devm_snd_soc_register_component() fails in rt5659_i2c_probe(), or if
the I2C device is unbound while no sound card is bound to the component,
rt5659_remove() is skipped and devres frees the IRQ, regmap, and struct
rt5659_priv while rt5659->jack_detect_work remains queued, leading to a
use-after-free in rt5659_jack_detect_work() or
rt5659_jack_detect_intel_hd_header() when the timer expires. Similarly,
rt5659_i2c_shutdown() writes RT5659_RESET without disabling the IRQ or
cancelling jack_detect_work.
[1] https://lore.kernel.org/r/20261030174837.1839402-3-tristan@talencesecurity.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261102165340.1558853-1-tristmd@gmail.com?part=2
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v6 3/4] ASoC: twl6040: cancel headset jack work on component removal
2026-11-02 16:53 [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
2026-11-02 16:53 ` [PATCH v6 1/4] ASoC: rt5668: cancel delayed works on component removal Tristan Madani
2026-11-02 16:53 ` [PATCH v6 2/4] ASoC: rt5659: cancel jack_detect_work " Tristan Madani
@ 2026-11-02 16:53 ` Tristan Madani
2026-11-02 16:53 ` [PATCH v6 4/4] ASoC: wm8994: drain delayed works on remove and probe error Tristan Madani
2026-11-02 23:19 ` [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Mark Brown
4 siblings, 0 replies; 9+ messages in thread
From: Tristan Madani @ 2026-11-02 16:53 UTC (permalink / raw)
To: Mark Brown, Liam Girdwood
Cc: Charles Keepax, Oder Chiou, Bard Liao, Jaroslav Kysela,
Takashi Iwai, Jorge Eduardo Candelaria, Margarita Olaya Cabrera,
Misael Lopez Cruz, patches, linux-sound, linux-kernel, stable,
tristan
From: Tristan Madani <tristan@talencesecurity.com>
The twl6040 codec driver initializes a delayed work for headset jack
detection (hs_jack.work) but the component remove callback only frees
the plug IRQ without cancelling the pending work.
Free the IRQ first so that the threaded handler cannot schedule new
work, then cancel any residual queued work synchronously. This
ordering prevents the race where the handler fires between a cancel
and the free_irq, re-arming the work after it was drained.
Fixes: a2d2362edf9f ("ASoC: twl6040: Add jack support for headset and handset")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
sound/soc/codecs/twl6040.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 650836f9615fb..826bde1a9ba46 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1136,6 +1136,7 @@ static void twl6040_remove(struct snd_soc_component *component)
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
free_irq(priv->plug_irq, component);
+ cancel_delayed_work_sync(&priv->hs_jack.work);
}
static const struct snd_soc_component_driver soc_component_dev_twl6040 = {
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v6 4/4] ASoC: wm8994: drain delayed works on remove and probe error
2026-11-02 16:53 [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
` (2 preceding siblings ...)
2026-11-02 16:53 ` [PATCH v6 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
@ 2026-11-02 16:53 ` Tristan Madani
2026-11-02 16:59 ` Charles Keepax
2026-11-02 23:19 ` [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Mark Brown
4 siblings, 1 reply; 9+ messages in thread
From: Tristan Madani @ 2026-11-02 16:53 UTC (permalink / raw)
To: Mark Brown, Liam Girdwood
Cc: Charles Keepax, Oder Chiou, Bard Liao, Jaroslav Kysela,
Takashi Iwai, Jorge Eduardo Candelaria, Margarita Olaya Cabrera,
Misael Lopez Cruz, patches, linux-sound, linux-kernel, stable,
tristan
From: Tristan Madani <tristan@talencesecurity.com>
wm8994_component_probe() schedules several delayed works to handle jack
detection and microphone status polling. If the component is removed or
probe fails after these works are scheduled, they can execute after
resources are freed.
Cancel all pending delayed works in the error and removal paths. Guard
cancel_delayed_work_sync(&wm8994->mic_work) with a type check because
INIT_DELAYED_WORK for mic_work only runs in the WM8994 and WM1811 code
paths.
Cancel mic_work before and after jackdet_bootstrap because the two can
re-arm each other: wm1811_mic_work() can re-arm jackdet_bootstrap
through the DAPM power-up chain (force-enabling MICBIAS2 triggers
clk_sys_event which queues jackdet_bootstrap), and
wm1811_jackdet_bootstrap() detects a jack and ultimately queues
mic_work. The second mic_work cancel catches any re-arm from a
jackdet_bootstrap handler that was in flight during the first cancel.
Fixes: 99af79dff5a6 ("ASoC: wm8994: Ensure we get a notification on startup for jackdet")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
---
Changes in v6:
- Add comments clarifying why mic_work is cancelled twice, per
Charles Keepax review. No functional change.
sound/soc/codecs/wm8994.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 6b91e02f130b3..6a790479c8a7e 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4595,6 +4595,14 @@ static int wm8994_component_probe(struct snd_soc_component *component)
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FIFOS_ERR, component);
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_SHUT, component);
wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_TEMP_WARN, component);
+ if (control->type == WM8994 || control->type == WM1811)
+ cancel_delayed_work_sync(&wm8994->mic_work);
+ cancel_delayed_work_sync(&wm8994->jackdet_bootstrap);
+ /* Catch mic_work re-armed by jackdet_bootstrap running concurrently. */
+ if (control->type == WM8994 || control->type == WM1811)
+ cancel_delayed_work_sync(&wm8994->mic_work);
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+ cancel_delayed_work_sync(&wm8994->open_circuit_work);
return ret;
}
@@ -4636,6 +4644,14 @@ static void wm8994_component_remove(struct snd_soc_component *component)
free_irq(wm8994->micdet_irq, wm8994);
break;
}
+ if (control->type == WM8994 || control->type == WM1811)
+ cancel_delayed_work_sync(&wm8994->mic_work);
+ cancel_delayed_work_sync(&wm8994->jackdet_bootstrap);
+ /* Catch mic_work re-armed by jackdet_bootstrap running concurrently. */
+ if (control->type == WM8994 || control->type == WM1811)
+ cancel_delayed_work_sync(&wm8994->mic_work);
+ cancel_delayed_work_sync(&wm8994->mic_complete_work);
+ cancel_delayed_work_sync(&wm8994->open_circuit_work);
release_firmware(wm8994->mbc);
release_firmware(wm8994->mbc_vss);
release_firmware(wm8994->enh_eq);
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v6 4/4] ASoC: wm8994: drain delayed works on remove and probe error
2026-11-02 16:53 ` [PATCH v6 4/4] ASoC: wm8994: drain delayed works on remove and probe error Tristan Madani
@ 2026-11-02 16:59 ` Charles Keepax
0 siblings, 0 replies; 9+ messages in thread
From: Charles Keepax @ 2026-11-02 16:59 UTC (permalink / raw)
To: Tristan Madani
Cc: Mark Brown, Liam Girdwood, Oder Chiou, Bard Liao, Jaroslav Kysela,
Takashi Iwai, Jorge Eduardo Candelaria, Margarita Olaya Cabrera,
Misael Lopez Cruz, patches, linux-sound, linux-kernel, stable,
tristan
On Mon, Nov 02, 2026 at 04:53:40PM +0000, Tristan Madani wrote:
> From: Tristan Madani <tristan@talencesecurity.com>
>
> wm8994_component_probe() schedules several delayed works to handle jack
> detection and microphone status polling. If the component is removed or
> probe fails after these works are scheduled, they can execute after
> resources are freed.
>
> Cancel all pending delayed works in the error and removal paths. Guard
> cancel_delayed_work_sync(&wm8994->mic_work) with a type check because
> INIT_DELAYED_WORK for mic_work only runs in the WM8994 and WM1811 code
> paths.
>
> Cancel mic_work before and after jackdet_bootstrap because the two can
> re-arm each other: wm1811_mic_work() can re-arm jackdet_bootstrap
> through the DAPM power-up chain (force-enabling MICBIAS2 triggers
> clk_sys_event which queues jackdet_bootstrap), and
> wm1811_jackdet_bootstrap() detects a jack and ultimately queues
> mic_work. The second mic_work cancel catches any re-arm from a
> jackdet_bootstrap handler that was in flight during the first cancel.
>
> Fixes: 99af79dff5a6 ("ASoC: wm8994: Ensure we get a notification on startup for jackdet")
> Cc: stable@vger.kernel.org
> Signed-off-by: Tristan Madani <tristan@talencesecurity.com>
> ---
Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Thanks,
Charles
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal
2026-11-02 16:53 [PATCH v6 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
` (3 preceding siblings ...)
2026-11-02 16:53 ` [PATCH v6 4/4] ASoC: wm8994: drain delayed works on remove and probe error Tristan Madani
@ 2026-11-02 23:19 ` Mark Brown
4 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-11-02 23:19 UTC (permalink / raw)
To: Liam Girdwood, Tristan Madani
Cc: Charles Keepax, Oder Chiou, Bard Liao, Jaroslav Kysela,
Takashi Iwai, Jorge Eduardo Candelaria, Margarita Olaya Cabrera,
Misael Lopez Cruz, patches, linux-sound, linux-kernel, stable,
tristan
On Mon, 02 Nov 2026 16:53:36 +0000, Tristan Madani wrote:
> ASoC: cancel pending delayed works on codec removal
>
> From: Tristan Madani <tristan@talencesecurity.com>
>
> Several ASoC codec drivers schedule delayed works for jack detection
> and microphone polling but do not cancel them on component removal,
> leaving handlers that can run against freed memory.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-7.5
Thanks!
[1/4] ASoC: rt5668: cancel delayed works on component removal
https://git.kernel.org/broonie/sound/c/965bad6195d0
[2/4] ASoC: rt5659: cancel jack_detect_work on component removal
https://git.kernel.org/broonie/sound/c/ca3efbaa8aeb
[3/4] ASoC: twl6040: cancel headset jack work on component removal
https://git.kernel.org/broonie/sound/c/0f367f70e185
[4/4] ASoC: wm8994: drain delayed works on remove and probe error
https://git.kernel.org/broonie/sound/c/905d79dffa3e
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark
^ permalink raw reply [flat|nested] 9+ messages in thread