* [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal
@ 2026-10-29 21:25 Tristan Madani
2026-10-29 21:25 ` [PATCH v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal Tristan Madani
` (4 more replies)
0 siblings, 5 replies; 9+ messages in thread
From: Tristan Madani @ 2026-10-29 21:25 UTC (permalink / raw)
To: Mark Brown
Cc: Oder Chiou, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Bard Liao, Misael Lopez Cruz, patches, linux-sound, stable
Several ASoC codec drivers schedule delayed works for jack detection but
do not cancel them in the component remove callback. If the device is
unbound while a work is pending or the IRQ fires shortly before removal,
the work handler can execute after resources are freed.
This series adds cancel_delayed_work_sync() calls in the remove (and
where applicable, probe error) paths of four affected codecs:
1/4 rt5668 - jack_detect_work not cancelled on remove
2/4 rt5659 - jack_detect_work not cancelled; INIT_DELAYED_WORK is
conditional on jd_src, so a guard flag is needed
3/4 twl6040 - hs_jack.work not cancelled before free_irq
4/4 wm8994 - multiple delayed works (jackdet_bootstrap, mic_work,
mic_complete_work, open_circuit_work) not cancelled
Changes in v2:
- Resend as unified series per Mark Brown request (previously sent as
individual patches with inflated version numbers)
- wm8994: cancel mic_work both before and after jackdet_bootstrap to
handle cyclic re-arming (mic_work -> DAPM -> jackdet_bootstrap ->
jack detect -> mic_work)
- rt5659: unchanged from prior version (jack_detect_work_inited flag
per Mark Brown feedback)
Tristan Madani (4):
ASoC: rt5668: cancel jack_detect_work on component removal
ASoC: rt5659: cancel jack_detect_work on component removal
ASoC: twl6040: cancel headset jack work on component removal
ASoC: wm8994: drain delayed works on remove and probe error
sound/soc/codecs/rt5659.c | 20 +++++++++++++++++++-
sound/soc/codecs/rt5659.h | 3 +++
sound/soc/codecs/rt5668.c | 2 ++
sound/soc/codecs/twl6040.c | 1 +
sound/soc/codecs/wm8994.c | 14 ++++++++++++++
5 files changed, 39 insertions(+), 1 deletion(-)
--
2.47.3
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal
2026-10-29 21:25 [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
@ 2026-10-29 21:25 ` Tristan Madani
2026-10-29 21:46 ` sashiko-bot
2026-10-29 21:25 ` [PATCH v2 2/4] ASoC: rt5659: " Tristan Madani
` (3 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Tristan Madani @ 2026-10-29 21:25 UTC (permalink / raw)
To: Mark Brown
Cc: Oder Chiou, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Bard Liao, Misael Lopez Cruz, patches, linux-sound, stable
The rt5668 codec driver initializes two delayed works but only
jd_check_work is cancelled during removal. The jack_detect_work is
never cancelled, which can lead to the work handler accessing freed
memory if the device is unbound while the work is pending.
Add cancel_delayed_work_sync() for jack_detect_work in
rt5668_remove().
Fixes: d59fb28562232 ("ASoC: rt5668: add rt5668B codec driver")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristmd@gmail.com>
---
Changes in v2:
- Resend as part of unified series (no code changes from v1)
sound/soc/codecs/rt5668.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 4154bad276101..0e6594c327311 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2223,6 +2223,8 @@ static void rt5668_remove(struct snd_soc_component *component)
{
struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
+ cancel_delayed_work_sync(&rt5668->jack_detect_work);
+
rt5668_reset(rt5668->regmap);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 2/4] ASoC: rt5659: cancel jack_detect_work on component removal
2026-10-29 21:25 [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
2026-10-29 21:25 ` [PATCH v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal Tristan Madani
@ 2026-10-29 21:25 ` Tristan Madani
2026-10-29 21:56 ` sashiko-bot
2026-10-29 21:25 ` [PATCH v2 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-10-29 21:25 UTC (permalink / raw)
To: Mark Brown
Cc: Oder Chiou, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Bard Liao, Misael Lopez Cruz, patches, linux-sound, stable
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 <tristmd@gmail.com>
---
Changes in v2:
- Resend as part of unified series (no code changes from prior version)
- Guard cancel with jack_detect_work_inited flag since INIT_DELAYED_WORK
is conditional on jd_src (JD3 or HDA_HEADER only), per Mark Brown
- Track IRQ acquisition to guard disable_irq/enable_irq
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 3097ee6d4e89f..cdf499fb36d67 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);
}
@@ -4271,6 +4285,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);
@@ -4280,6 +4295,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:
@@ -4290,7 +4306,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
* [PATCH v2 3/4] ASoC: twl6040: cancel headset jack work on component removal
2026-10-29 21:25 [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
2026-10-29 21:25 ` [PATCH v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal Tristan Madani
2026-10-29 21:25 ` [PATCH v2 2/4] ASoC: rt5659: " Tristan Madani
@ 2026-10-29 21:25 ` Tristan Madani
2026-10-29 21:59 ` sashiko-bot
2026-10-29 21:25 ` [PATCH v2 4/4] ASoC: wm8994: drain delayed works on remove and probe error Tristan Madani
2026-10-29 22:34 ` [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Mark Brown
4 siblings, 1 reply; 9+ messages in thread
From: Tristan Madani @ 2026-10-29 21:25 UTC (permalink / raw)
To: Mark Brown
Cc: Oder Chiou, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Bard Liao, Misael Lopez Cruz, patches, linux-sound, stable
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. If the IRQ fires and
schedules the work shortly before removal, it can execute on freed
memory.
Add cancel_delayed_work_sync() in twl6040_remove() before freeing the
IRQ to ensure no stale work remains queued.
Fixes: a2d2362edf9f ("ASoC: twl6040: Add jack support for headset and handset")
Cc: stable@vger.kernel.org
Signed-off-by: Tristan Madani <tristmd@gmail.com>
---
Changes in v2:
- Resend as part of unified series (no code changes from v1)
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..43feb660b635b 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1135,6 +1135,7 @@ static void twl6040_remove(struct snd_soc_component *component)
{
struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
+ cancel_delayed_work_sync(&priv->hs_jack.work);
free_irq(priv->plug_irq, component);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH v2 4/4] ASoC: wm8994: drain delayed works on remove and probe error
2026-10-29 21:25 [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
` (2 preceding siblings ...)
2026-10-29 21:25 ` [PATCH v2 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
@ 2026-10-29 21:25 ` Tristan Madani
2026-10-29 22:34 ` [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Mark Brown
4 siblings, 0 replies; 9+ messages in thread
From: Tristan Madani @ 2026-10-29 21:25 UTC (permalink / raw)
To: Mark Brown
Cc: Oder Chiou, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Bard Liao, Misael Lopez Cruz, patches, linux-sound, stable
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 <tristmd@gmail.com>
---
Changes in v2:
- Resend as part of unified series
- Cancel mic_work both before and after jackdet_bootstrap to handle
cyclic re-arming through DAPM power-up chain
sound/soc/codecs/wm8994.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 8bf58a6a8af7c..b9e0df83b9bfe 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4551,6 +4551,13 @@ 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);
+ 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;
}
@@ -4592,6 +4599,13 @@ 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);
+ 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 v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal
2026-10-29 21:25 ` [PATCH v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal Tristan Madani
@ 2026-10-29 21:46 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-29 21:46 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: jd_check_work is not cancelled in rt5668_remove()
- [High] ASoC: rt5668: jack_detect_work can remain active or re-arm on I2C unbind
--
commit aa3b3b8978b5829c3441db1dd94723b81c7cb27d
Author: Tristan Madani <tristmd@gmail.com>
ASoC: rt5668: cancel jack_detect_work on component removal
This commit cancels jack_detect_work in rt5668_remove() to prevent the
delayed work handler from accessing freed memory if the device is unbound
while work is pending.
> diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
> index cd6245d6b91af..b1c65924d9959 100644
> --- a/sound/soc/codecs/rt5668.c
> +++ b/sound/soc/codecs/rt5668.c
> @@ -2223,6 +2223,8 @@ static void rt5668_remove(struct snd_soc_component *component)
> {
> struct rt5668_priv *rt5668 = snd_soc_component_get_drvdata(component);
>
> + cancel_delayed_work_sync(&rt5668->jack_detect_work);
[Severity: High]
The commit message states that "only jd_check_work is cancelled during
removal", but is rt5668->jd_check_work actually cancelled on removal?
Looking at sound/soc/codecs/rt5668.c, the only call to
cancel_delayed_work_sync(&rt5668->jd_check_work) is in
rt5668_jack_detect_handler() when no button press is detected:
sound/soc/codecs/rt5668.c:rt5668_jack_detect_handler() {
...
if (rt5668->jack_type & (SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3))
schedule_delayed_work(&rt5668->jd_check_work, 0);
else
cancel_delayed_work_sync(&rt5668->jd_check_work);
...
}
Once scheduled, rt5668_jd_check_handler() re-schedules itself every 500
jiffies for as long as the jack remains inserted, accessing
rt5668->component and rt5668->hs_jack:
sound/soc/codecs/rt5668.c:rt5668_jd_check_handler() {
...
if (!(snd_soc_component_read(rt5668->component, RT5668_AJD1_CTRL)
& RT5668_JDH_RS_MASK)) {
...
} else {
schedule_delayed_work(&rt5668->jd_check_work, 500);
}
}
In addition, if rt5668_jack_detect_handler() is already running when
cancel_delayed_work_sync(&rt5668->jack_detect_work) is called, it can
schedule rt5668->jd_check_work right before finishing. Shouldn't
rt5668->jd_check_work also be cancelled after jack_detect_work so it cannot
access freed rt5668_priv memory after unbind?
[Severity: High]
Can rt5668->jack_detect_work still remain active or be re-queued when the
I2C device is unbound?
There appear to be two paths in sound/soc/codecs/rt5668.c where cancelling
the work only in the ASoC component remove callback rt5668_remove() leaves
rt5668->jack_detect_work running after rt5668 is freed:
1. In rt5668_i2c_probe(), devm_request_threaded_irq() is registered before
devm_snd_soc_register_component(), and rt5668_i2c_driver does not define an
I2C .remove callback:
sound/soc/codecs/rt5668.c:rt5668_i2c_probe() {
...
if (i2c->irq) {
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5668_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5668", rt5668);
if (ret)
dev_err(&i2c->dev, "Failed to request IRQ: %d\n", ret);
}
return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668,
rt5668_dai, ARRAY_SIZE(rt5668_dai));
}
On I2C device unbind, devres unregisters the ASoC component (invoking
rt5668_remove()) before devm_irq_release() frees i2c->irq. Because the IRQ
remains enabled during and after rt5668_remove(), rt5668_irq() can fire
before devm_irq_release() and re-queue rt5668->jack_detect_work via
mod_delayed_work().
2. rt5668_remove() is only invoked by snd_soc_component_remove() when the
component is bound to an instantiated sound card (component->card != NULL).
If rt5668_irq() fires when no sound card is instantiated,
rt5668_jack_detect_handler() re-arms rt5668->jack_detect_work every 15 ms:
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;
}
...
}
If the I2C device is unbound while no sound card is bound, rt5668_remove()
is never called and the 15 ms retry loop continues running after devres
frees rt5668.
Should the driver disable the IRQ and cancel both delayed works in an I2C
driver .remove callback (or via a devm action registered after
devm_request_threaded_irq())?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261029212504.2997364-1-tristmd@gmail.com?part=1
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 2/4] ASoC: rt5659: cancel jack_detect_work on component removal
2026-10-29 21:25 ` [PATCH v2 2/4] ASoC: rt5659: " Tristan Madani
@ 2026-10-29 21:56 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-29 21:56 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: jack_detect_work not cancelled on I2C unbind without card
- [High] ASoC: rt5659: broken jack detect state and dangling hs_jack in rt5659_probe()
--
commit 58a577f04ded4350869b1fdee73beb3b1dbfecaa
Author: Tristan Madani <tristmd@gmail.com>
ASoC: rt5659: cancel jack_detect_work on component removal
This commit tracks IRQ acquisition and jack_detect_work initialization so
that rt5659_remove() can disable the IRQ and synchronously cancel the
delayed work before resetting the codec. It also re-enables the IRQ in
rt5659_probe() when a sound card rebinds to the component.
> 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]
Does re-enabling the IRQ here in rt5659_probe() in sound/soc/codecs/rt5659.c
actually restore jack detection after a card unbind/rebind, or can it also
trigger a use-after-free on rt5659->hs_jack?
At the end of rt5659_remove(), regmap_write(rt5659->regmap, RT5659_RESET, 0)
resets all codec hardware registers to their power-on defaults. This wipes
out the GPIO1 IRQ pin mux (RT5659_GP1_PIN_IRQ in RT5659_GPIO_CTRL_1) and all
jack-detection and IRQ control registers (RT5659_EJD_CTRL_1,
RT5659_RC_CLK_CTRL, RT5659_JD_CTRL_1, RT5659_IRQ_CTRL_1, RT5659_IRQ_CTRL_2)
that were programmed only once in rt5659_i2c_probe(). Because RT5659_RESET
is marked volatile in rt5659_volatile_register(), the regmap cache is not
marked dirty or resynced on re-probe, leaving the codec unable to generate
jack detection interrupts after rebind.
In addition, rt5659_remove() does not clear rt5659->hs_jack or
rt5659->component. When the previous sound card unbinds, its card-owned
struct snd_soc_jack is freed, but rt5659->hs_jack retains the dangling
pointer. When a card binds again, snd_soc_component_probe() calls
rt5659_probe() before the machine driver init callback runs to register a
new jack via rt5659_set_jack_detect(). If an interrupt fires once
enable_irq() unmasks the line (or if the rebound card does not call
rt5659_set_jack_detect()), jack_detect_work will pass the freed
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);
>
> regmap_write(rt5659->regmap, RT5659_RESET, 0);
> }
[Severity: High]
What happens if the I2C device is unbound while no sound card is bound to
the component, or if devm_snd_soc_register_component() fails in
rt5659_i2c_probe()?
In rt5659_i2c_probe() in sound/soc/codecs/rt5659.c, when
rt5659->pdata.jd_src is RT5659_JD3 or RT5659_JD_HDA_HEADER, the driver
initializes rt5659->jack_detect_work, enables the codec jack-detection
interrupt registers, requests rt5659_irq() via devm_request_threaded_irq(),
and enables the GPIO1 IRQ output before calling
devm_snd_soc_register_component():
sound/soc/codecs/rt5659.c:rt5659_i2c_probe() {
...
if (i2c->irq) {
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
rt5659_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
| IRQF_ONESHOT, "rt5659", rt5659);
...
regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1,
RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ);
}
return devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_rt5659,
rt5659_dai, ARRAY_SIZE(rt5659_dai));
}
As soon as devm_request_threaded_irq() succeeds, an interrupt on i2c->irq
can run rt5659_irq() and queue rt5659->jack_detect_work on
system_power_efficient_wq with a 250ms delay.
However, the ASoC core only calls the component .remove callback
(rt5659_remove) when component->card is non-NULL:
sound/soc/soc-core.c:snd_soc_del_component_unlocked() {
...
if (card) {
instantiated = card->instantiated;
snd_soc_unbind_card(card);
...
}
}
sound/soc/soc-component.c:snd_soc_component_remove() {
if (!component->card)
return;
if (probed && component->driver->remove)
component->driver->remove(component);
...
}
Because rt5659_i2c_driver has no I2C .remove callback and does not register
a devres action such as devm_delayed_work_autocancel(), unbinding the I2C
device when component->card is NULL (or failing in
devm_snd_soc_register_component()) skips rt5659_remove() entirely.
devres_release_all() will free the IRQ, regmap, and struct rt5659_priv while
rt5659->jack_detect_work is still pending or running, leading to a
use-after-free in rt5659_jack_detect_work() or
rt5659_jack_detect_intel_hd_header(). Similarly, rt5659_i2c_shutdown()
resets the codec via RT5659_RESET without disabling the IRQ or cancelling
jack_detect_work.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261029212504.2997364-1-tristmd@gmail.com?part=2
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 3/4] ASoC: twl6040: cancel headset jack work on component removal
2026-10-29 21:25 ` [PATCH v2 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
@ 2026-10-29 21:59 ` sashiko-bot
0 siblings, 0 replies; 9+ messages in thread
From: sashiko-bot @ 2026-10-29 21:59 UTC (permalink / raw)
To: Tristan Madani; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Critical] ASoC: twl6040: work cancelled before free_irq() in twl6040_remove()
--
commit c1e273e65955a707961d70889d530cc54f85f31c
Author: Tristan Madani <tristmd@gmail.com>
ASoC: twl6040: cancel headset jack work on component removal
This commit adds cancel_delayed_work_sync() in twl6040_remove() to cancel the
headset jack detection delayed work upon component removal.
> diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
> index 650836f9615fb..43feb660b635b 100644
> --- a/sound/soc/codecs/twl6040.c
> +++ b/sound/soc/codecs/twl6040.c
> @@ -1135,6 +1135,7 @@ static void twl6040_remove(struct snd_soc_component *component)
> {
> struct twl6040_data *priv = snd_soc_component_get_drvdata(component);
>
> + cancel_delayed_work_sync(&priv->hs_jack.work);
> free_irq(priv->plug_irq, component);
[Severity: Critical]
Can priv->hs_jack.work be re-queued if priv->plug_irq fires after
cancel_delayed_work_sync() returns, but before free_irq() completes?
In sound/soc/codecs/twl6040.c, the threaded interrupt handler
twl6040_audio_handler() schedules the delayed work whenever priv->plug_irq
fires:
queue_delayed_work(system_power_efficient_wq,
&priv->hs_jack.work, msecs_to_jiffies(200));
If twl6040_audio_handler() runs in the window between
cancel_delayed_work_sync() and free_irq() in twl6040_remove(),
priv->hs_jack.work will remain queued after removal completes, allowing
twl6040_accessory_work() to dereference priv and component after devres has
freed them.
Should free_irq(priv->plug_irq, component) be called before
cancel_delayed_work_sync(&priv->hs_jack.work), or should
disable_delayed_work_sync() be used instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261029212504.2997364-1-tristmd@gmail.com?part=3
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal
2026-10-29 21:25 [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
` (3 preceding siblings ...)
2026-10-29 21:25 ` [PATCH v2 4/4] ASoC: wm8994: drain delayed works on remove and probe error Tristan Madani
@ 2026-10-29 22:34 ` Mark Brown
4 siblings, 0 replies; 9+ messages in thread
From: Mark Brown @ 2026-10-29 22:34 UTC (permalink / raw)
To: Tristan Madani
Cc: Oder Chiou, Liam Girdwood, Jaroslav Kysela, Takashi Iwai,
Bard Liao, Misael Lopez Cruz, patches, linux-sound, stable
[-- Attachment #1: Type: text/plain, Size: 398 bytes --]
On Thu, Oct 29, 2026 at 09:25:00PM +0000, Tristan Madani wrote:
> Changes in v2:
> - Resend as unified series per Mark Brown request (previously sent as
> individual patches with inflated version numbers)
I asked you to send a v10 (just prior to you sending v10 of one of the
patches) because my tools have seen that randomly higher version...
I'll fix things up, please don't resend for this.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2026-10-29 22:34 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-29 21:25 [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Tristan Madani
2026-10-29 21:25 ` [PATCH v2 1/4] ASoC: rt5668: cancel jack_detect_work on component removal Tristan Madani
2026-10-29 21:46 ` sashiko-bot
2026-10-29 21:25 ` [PATCH v2 2/4] ASoC: rt5659: " Tristan Madani
2026-10-29 21:56 ` sashiko-bot
2026-10-29 21:25 ` [PATCH v2 3/4] ASoC: twl6040: cancel headset jack work " Tristan Madani
2026-10-29 21:59 ` sashiko-bot
2026-10-29 21:25 ` [PATCH v2 4/4] ASoC: wm8994: drain delayed works on remove and probe error Tristan Madani
2026-10-29 22:34 ` [PATCH v2 0/4] ASoC: cancel pending delayed works on codec removal Mark Brown
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox