mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers
@ 2026-11-06 13:17 hyunjungg
  2026-11-06 13:17 ` [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed hyunjungg
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: hyunjungg @ 2026-11-06 13:17 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc,
	Hans de Goede, Hyunjung Ko

From: Hyunjung Ko <hj351016@gmail.com>

Five extcon drivers register a detect/IRQ work for device-managed
cancellation (devm_work_autocancel() / devm_delayed_work_autocancel())
in their probe function *before* they acquire the resources that the
work handler dereferences -- most importantly the extcon device from
devm_extcon_dev_allocate() / devm_extcon_dev_register(), and for
intel-int3496 also several GPIO descriptors.

devres unwinds in reverse registration order, so on unbind the extcon
device (and the GPIOs) are released first and the work is cancelled
last:

	/* free_irq() */
	devm_extcon_dev_unregister() / extcon freed
	...
	cancel_[delayed_]work_sync(&work);	/* too late */

The work is armed from the IRQ handler (schedule_work() in the MUIC
drivers; queue_delayed_work()/mod_delayed_work() in palmas/int3496), so
an instance can still be pending or running at unbind. When it runs in
the window after the extcon device has been freed but before the work is
cancelled, the handler dereferences freed memory via
extcon_set_state_sync() -> use-after-free. The MUIC drivers' early
"if (!info->edev) return;" guard does not help: info is itself devm
memory freed last, so info->edev is a dangling, non-NULL pointer in that
window rather than NULL.

Each patch fixes one driver by moving the devm work-cancel registration
to *after* the extcon device (and, for int3496, the GPIOs) are acquired
and registered, and still *before* the IRQ that arms the work. Teardown
then runs in the correct order: free IRQ -> drain/cancel work -> free
extcon device. The relocated devm_[delayed_]work_autocancel() still
performs the INIT before the work's first queuing site (the IRQ handler,
requested afterwards), so no queue-before-INIT window is introduced. No
functional change otherwise.

Found by static analysis / code review. None of the five is reproduced
on hardware: each needs its MUIC/PMIC/USB-ID-mux chip and is not
QEMU-reproducible. Treat accordingly.

Hyunjung Ko (5):
  extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed
  extcon: palmas: drain ID-detect work before the extcon device is freed
  extcon: max8997: drain MUIC IRQ work before extcon device is freed
  extcon: max14577: drain MUIC IRQ work before extcon device is freed
  extcon: max77693: drain MUIC IRQ work before extcon device is freed

 drivers/extcon/extcon-intel-int3496.c |  7 ++++---
 drivers/extcon/extcon-palmas.c        | 12 ++++++------
 drivers/extcon/extcon-max8997.c       | 26 +++++++++++++-------------
 drivers/extcon/extcon-max14577.c      | 28 ++++++++++++++--------------
 drivers/extcon/extcon-max77693.c      | 28 ++++++++++++++--------------
 5 files changed, 51 insertions(+), 50 deletions(-)

--
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed
  2026-11-06 13:17 [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers hyunjungg
@ 2026-11-06 13:17 ` hyunjungg
  2026-11-06 15:46   ` Hans de Goede
  2026-11-06 13:17 ` [PATCH 2/5] extcon: palmas: drain ID-detect work before the extcon device is freed hyunjungg
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 7+ messages in thread
From: hyunjungg @ 2026-11-06 13:17 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc,
	Hans de Goede, Hyunjung Ko, stable

From: Hyunjung Ko <hj351016@gmail.com>

int3496_probe() registers the debounce work for device-managed
cancellation before it acquires any of the resources that the work
handler dereferences:

	devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
	...
	data->gpio_usb_id  = devm_gpiod_get(dev, "id", ...);
	data->gpio_vbus_en = devm_gpiod_get(dev, "vbus", ...);
	data->gpio_usb_mux = devm_gpiod_get(dev, "mux", ...);
	data->edev = devm_extcon_dev_allocate(dev, int3496_cable);
	devm_extcon_dev_register(dev, data->edev);
	devm_request_threaded_irq(dev, data->usb_id_irq, ...);

devres releases resources in reverse registration order, so on unbind
the extcon device and all three GPIO descriptors are released first and
the work is cancelled last:

	/* free_irq() */
	devm_extcon_dev_unregister() / extcon freed
	gpiod_put(mux) / gpiod_put(vbus_en) / gpiod_put(usb_id)
	...
	cancel_delayed_work_sync(&data->work);		/* too late */

int3496_thread_isr() arms the work with mod_delayed_work() on every ID
pin edge, so a work instance can be pending or running at unbind. The
handler int3496_do_usb_id() dereferences data->gpio_usb_id
(gpiod_get_value_cansleep), data->gpio_usb_mux, data->gpio_vbus_en and
data->edev (extcon_set_state_sync) with no guard. If it runs in the
window after the GPIOs/extcon device have been released but before the
work is cancelled, it operates on freed resources -> use-after-free.

Register the work for cancellation after the GPIOs and extcon device
are acquired and registered (and before the IRQ, which arms it), so
devres (LIFO) frees the IRQ first, then cancels the work, and only then
releases the GPIOs and extcon device. devm_delayed_work_autocancel()
still performs INIT_DELAYED_WORK() before the work is first queued
(queue_delayed_work() below, and the IRQ handler, both run after it).

Found by static analysis / code review; compile-tested only, not
reproduced on hardware (requires an INT3496 ACPI USB-ID mux device).

Fixes: f94a5becabf4 ("extconn: Clean-up few drivers by using managed work init")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c
index 7d16d5b..40774fb 100644
--- a/drivers/extcon/extcon-intel-int3496.c
+++ b/drivers/extcon/extcon-intel-int3496.c
@@ -130,9 +130,6 @@ static int int3496_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	data->dev = dev;
-	ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
-	if (ret)
-		return ret;
 
 	data->gpio_usb_id =
 		devm_gpiod_get(dev, "id", GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
@@ -169,6 +166,10 @@ static int int3496_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
+	if (ret)
+		return ret;
+
 	ret = devm_request_threaded_irq(dev, data->usb_id_irq,
 					NULL, int3496_thread_isr,
 					IRQF_SHARED | IRQF_ONESHOT |

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 2/5] extcon: palmas: drain ID-detect work before the extcon device is freed
  2026-11-06 13:17 [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers hyunjungg
  2026-11-06 13:17 ` [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed hyunjungg
@ 2026-11-06 13:17 ` hyunjungg
  2026-11-06 13:17 ` [PATCH 3/5] extcon: max8997: drain MUIC IRQ work before " hyunjungg
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 7+ messages in thread
From: hyunjungg @ 2026-11-06 13:17 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc,
	Hans de Goede, Hyunjung Ko, stable

From: Hyunjung Ko <hj351016@gmail.com>

palmas_usb_probe() registers the GPIO ID-detect work for device-managed
cancellation before it allocates and registers the extcon device that
the work handler dereferences:

	devm_delayed_work_autocancel(&pdev->dev, &palmas_usb->wq_detectid,
				     palmas_gpio_id_detect);
	...
	palmas_usb->edev = devm_extcon_dev_allocate(&pdev->dev, ...);
	devm_extcon_dev_register(&pdev->dev, palmas_usb->edev);
	...
	devm_request_threaded_irq(..., palmas_gpio_id_irq_handler, ...);

devres releases resources in reverse registration order, so on unbind
the extcon device is torn down first and the work is cancelled last:

	/* free_irq() */
	devm_extcon_dev_unregister() / extcon freed
	...
	cancel_delayed_work_sync(&palmas_usb->wq_detectid);	/* too late */

palmas_gpio_id_irq_handler() queues wq_detectid with a debounce delay on
every ID GPIO edge, so a work instance can be pending at unbind. The
handler palmas_gpio_id_detect() dereferences palmas_usb->edev via
extcon_set_state_sync(). Its only early-return guard tests
palmas_usb->id_gpiod (acquired before the work registration, so still
valid), not ->edev, so it does not prevent the access. If the work runs
in the window after the extcon device has been freed but before the
work is cancelled, it dereferences freed memory -> use-after-free.

Register the work for cancellation after the extcon device is allocated
and registered (and before the IRQs, which queue it), so devres (LIFO)
frees the IRQs first, then cancels the work, and only then frees the
extcon device. The ID GPIO the handler also uses is acquired earlier and
thus still outlives the work. devm_delayed_work_autocancel() still runs
INIT_DELAYED_WORK() before the work is first queued (the IRQ handler and
the explicit palmas_gpio_id_detect() call both run after it).

Found by static analysis / code review; compile-tested only, not
reproduced on hardware (requires a Palmas/TWL6035 USB PHY).

Fixes: f94a5becabf4 ("extconn: Clean-up few drivers by using managed work init")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c
index d339b86..f7496e5 100644
--- a/drivers/extcon/extcon-palmas.c
+++ b/drivers/extcon/extcon-palmas.c
@@ -237,12 +237,6 @@ static int palmas_usb_probe(struct platform_device *pdev)
 			palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce);
 	}
 
-	status = devm_delayed_work_autocancel(&pdev->dev,
-					      &palmas_usb->wq_detectid,
-					      palmas_gpio_id_detect);
-	if (status)
-		return status;
-
 	palmas->usb = palmas_usb;
 	palmas_usb->palmas = palmas;
 
@@ -265,6 +259,12 @@ static int palmas_usb_probe(struct platform_device *pdev)
 		return status;
 	}
 
+	status = devm_delayed_work_autocancel(&pdev->dev,
+					      &palmas_usb->wq_detectid,
+					      palmas_gpio_id_detect);
+	if (status)
+		return status;
+
 	if (palmas_usb->enable_id_detection) {
 		palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data,
 							     PALMAS_ID_OTG_IRQ);

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 3/5] extcon: max8997: drain MUIC IRQ work before extcon device is freed
  2026-11-06 13:17 [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers hyunjungg
  2026-11-06 13:17 ` [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed hyunjungg
  2026-11-06 13:17 ` [PATCH 2/5] extcon: palmas: drain ID-detect work before the extcon device is freed hyunjungg
@ 2026-11-06 13:17 ` hyunjungg
  2026-11-06 13:17 ` [PATCH 4/5] extcon: max14577: " hyunjungg
  2026-11-06 13:17 ` [PATCH 5/5] extcon: max77693: " hyunjungg
  4 siblings, 0 replies; 7+ messages in thread
From: hyunjungg @ 2026-11-06 13:17 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc,
	Hans de Goede, Hyunjung Ko, stable

From: Hyunjung Ko <hj351016@gmail.com>

max8997_muic_probe() registers the MUIC interrupt work for device-managed
cancellation before it allocates and registers the extcon device that the
work handler dereferences:

	INIT_WORK(&info->irq_work, max8997_muic_irq_work);
	devm_work_autocancel(&pdev->dev, &info->irq_work, ...);
	...
	devm_request_threaded_irq(&pdev->dev, virq, ...);	/* arms work */
	...
	info->edev = devm_extcon_dev_allocate(&pdev->dev, ...);
	devm_extcon_dev_register(&pdev->dev, info->edev);

devres releases resources in reverse registration order, so on unbind the
extcon device is torn down first and the work is cancelled last:

	/* free_irq() */
	devm_extcon_dev_unregister() / extcon freed
	...
	cancel_work_sync(&info->irq_work);		/* too late */

max8997_muic_irq_handler() queues irq_work with schedule_work() on every
MUIC interrupt, so a work instance can be pending or running at unbind.
The handler max8997_muic_irq_work() dereferences info->edev via
extcon_set_state_sync(). Its only early-return guard tests info->edev, but
info is itself devm-allocated and freed last, so in the teardown window
info->edev is a dangling (non-NULL) pointer rather than NULL and the guard
does not fire. If the work runs after the extcon device has been freed but
before the work is cancelled, it dereferences freed memory, i.e. a
use-after-free.

Allocate and register the extcon device before registering the work for
cancellation (the redundant INIT_WORK() is moved up with it), so devres
(LIFO) frees the IRQs first, then cancels the work, and only then frees
the extcon device. devm_work_autocancel() still performs INIT_WORK()
before the work is first queued (the IRQ handler, requested below, is the
only queuing site).

Found by static analysis / code review; compile-tested only, not
reproduced on hardware (requires a MAX8997 MUIC).

Fixes: 87ee8de23c9d ("extcon: extcon-max8997: Simplify driver using devm")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
diff --git a/drivers/extcon/extcon-max8997.c b/drivers/extcon/extcon-max8997.c
index 9cddf08..a54e7c4 100644
--- a/drivers/extcon/extcon-max8997.c
+++ b/drivers/extcon/extcon-max8997.c
@@ -650,6 +650,19 @@ static int max8997_muic_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, info);
 	mutex_init(&info->mutex);
 
+	/* External connector */
+	info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
+	if (IS_ERR(info->edev)) {
+		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
+		return PTR_ERR(info->edev);
+	}
+
+	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register extcon device\n");
+		return ret;
+	}
+
 	INIT_WORK(&info->irq_work, max8997_muic_irq_work);
 	ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
 				   max8997_muic_irq_work);
@@ -678,19 +691,6 @@ static int max8997_muic_probe(struct platform_device *pdev)
 		}
 	}
 
-	/* External connector */
-	info->edev = devm_extcon_dev_allocate(&pdev->dev, max8997_extcon_cable);
-	if (IS_ERR(info->edev)) {
-		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
-		return PTR_ERR(info->edev);
-	}
-
-	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
-	if (ret) {
-		dev_err(&pdev->dev, "failed to register extcon device\n");
-		return ret;
-	}
-
 	if (pdata && pdata->muic_pdata) {
 		struct max8997_muic_platform_data *muic_pdata
 			= pdata->muic_pdata;

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 4/5] extcon: max14577: drain MUIC IRQ work before extcon device is freed
  2026-11-06 13:17 [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers hyunjungg
                   ` (2 preceding siblings ...)
  2026-11-06 13:17 ` [PATCH 3/5] extcon: max8997: drain MUIC IRQ work before " hyunjungg
@ 2026-11-06 13:17 ` hyunjungg
  2026-11-06 13:17 ` [PATCH 5/5] extcon: max77693: " hyunjungg
  4 siblings, 0 replies; 7+ messages in thread
From: hyunjungg @ 2026-11-06 13:17 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc,
	Hans de Goede, Hyunjung Ko, stable

From: Hyunjung Ko <hj351016@gmail.com>

max14577_muic_probe() registers the MUIC interrupt work for device-managed
cancellation before it allocates and registers the extcon device that the
work handler dereferences:

	devm_work_autocancel(&pdev->dev, &info->irq_work, ...);
	...
	devm_request_threaded_irq(&pdev->dev, virq, ...);	/* arms work */
	...
	info->edev = devm_extcon_dev_allocate(&pdev->dev, ...);
	devm_extcon_dev_register(&pdev->dev, info->edev);

devres releases resources in reverse registration order, so on unbind the
extcon device is torn down first and the work is cancelled last:

	/* free_irq() */
	devm_extcon_dev_unregister() / extcon freed
	...
	cancel_work_sync(&info->irq_work);		/* too late */

max14577_muic_irq_handler() queues irq_work with schedule_work() on every
MUIC interrupt, so a work instance can be pending or running at unbind.
The handler max14577_muic_irq_work() dereferences info->edev via
extcon_set_state_sync(). Its only early-return guard tests info->edev, but
info is itself devm-allocated and freed last, so in the teardown window
info->edev is a dangling (non-NULL) pointer rather than NULL and the guard
does not fire. If the work runs after the extcon device has been freed but
before the work is cancelled, it dereferences freed memory, i.e. a
use-after-free.

Allocate and register the extcon device before registering the work for
cancellation, so devres (LIFO) frees the IRQs first, then cancels the
work, and only then frees the extcon device. devm_work_autocancel() still
performs INIT_WORK() before the work is first queued (the IRQ handler,
requested below, is the only queuing site).

Found by static analysis / code review; compile-tested only, not
reproduced on hardware (requires a MAX14577/MAX77836 MUIC).

Fixes: 14ad76825f00 ("extcon: extcon-max14577: Fix potential work-queue cancellation race")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
diff --git a/drivers/extcon/extcon-max14577.c b/drivers/extcon/extcon-max14577.c
index 5476f48..1267769 100644
--- a/drivers/extcon/extcon-max14577.c
+++ b/drivers/extcon/extcon-max14577.c
@@ -674,6 +674,20 @@ static int max14577_muic_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, info);
 	mutex_init(&info->mutex);
 
+	/* Initialize extcon device */
+	info->edev = devm_extcon_dev_allocate(&pdev->dev,
+					      max14577_extcon_cable);
+	if (IS_ERR(info->edev)) {
+		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
+		return PTR_ERR(info->edev);
+	}
+
+	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register extcon device\n");
+		return ret;
+	}
+
 	ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
 				   max14577_muic_irq_work);
 	if (ret)
@@ -712,20 +726,6 @@ static int max14577_muic_probe(struct platform_device *pdev)
 		}
 	}
 
-	/* Initialize extcon device */
-	info->edev = devm_extcon_dev_allocate(&pdev->dev,
-					      max14577_extcon_cable);
-	if (IS_ERR(info->edev)) {
-		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
-		return PTR_ERR(info->edev);
-	}
-
-	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
-	if (ret) {
-		dev_err(&pdev->dev, "failed to register extcon device\n");
-		return ret;
-	}
-
 	/* Default h/w line path */
 	info->path_usb = CTRL1_SW_USB;
 	info->path_uart = CTRL1_SW_UART;

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 5/5] extcon: max77693: drain MUIC IRQ work before extcon device is freed
  2026-11-06 13:17 [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers hyunjungg
                   ` (3 preceding siblings ...)
  2026-11-06 13:17 ` [PATCH 4/5] extcon: max14577: " hyunjungg
@ 2026-11-06 13:17 ` hyunjungg
  4 siblings, 0 replies; 7+ messages in thread
From: hyunjungg @ 2026-11-06 13:17 UTC (permalink / raw)
  To: MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc,
	Hans de Goede, Hyunjung Ko, stable

From: Hyunjung Ko <hj351016@gmail.com>

max77693_muic_probe() registers the MUIC interrupt work for device-managed
cancellation before it allocates and registers the extcon device that the
work handler dereferences:

	devm_work_autocancel(&pdev->dev, &info->irq_work, ...);
	...
	devm_request_threaded_irq(&pdev->dev, virq, ...);	/* arms work */
	...
	info->edev = devm_extcon_dev_allocate(&pdev->dev, ...);
	devm_extcon_dev_register(&pdev->dev, info->edev);

devres releases resources in reverse registration order, so on unbind the
extcon device is torn down first and the work is cancelled last:

	/* free_irq() */
	devm_extcon_dev_unregister() / extcon freed
	...
	cancel_work_sync(&info->irq_work);		/* too late */

max77693_muic_irq_handler() queues irq_work with schedule_work() on every
MUIC interrupt, so a work instance can be pending or running at unbind.
The handler max77693_muic_irq_work() dereferences info->edev via
extcon_set_state_sync(). Its only early-return guard tests info->edev, but
info is itself devm-allocated and freed last, so in the teardown window
info->edev is a dangling (non-NULL) pointer rather than NULL and the guard
does not fire. If the work runs after the extcon device has been freed but
before the work is cancelled, it dereferences freed memory, i.e. a
use-after-free.

Allocate and register the extcon device before registering the work for
cancellation, so devres (LIFO) frees the IRQs first, then cancels the
work, and only then frees the extcon device. The dock input device the
handler also uses is registered earlier and thus still outlives the work.
devm_work_autocancel() still performs INIT_WORK() before the work is first
queued (the IRQ handler, requested below, is the only queuing site).

Found by static analysis / code review; compile-tested only, not
reproduced on hardware (requires a MAX77693 MUIC).

Fixes: 74047eaa2281 ("extcon: extcon-max77693.c: Fix potential work-queue cancellation race")
Cc: stable@vger.kernel.org
Signed-off-by: Hyunjung Ko <hj351016@gmail.com>
---
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 2c567e0..f151787 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -1128,6 +1128,20 @@ static int max77693_muic_probe(struct platform_device *pdev)
 	platform_set_drvdata(pdev, info);
 	mutex_init(&info->mutex);
 
+	/* Initialize extcon device */
+	info->edev = devm_extcon_dev_allocate(&pdev->dev,
+					      max77693_extcon_cable);
+	if (IS_ERR(info->edev)) {
+		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
+		return PTR_ERR(info->edev);
+	}
+
+	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register extcon device\n");
+		return ret;
+	}
+
 	ret = devm_work_autocancel(&pdev->dev, &info->irq_work,
 				   max77693_muic_irq_work);
 	if (ret)
@@ -1156,20 +1170,6 @@ static int max77693_muic_probe(struct platform_device *pdev)
 		}
 	}
 
-	/* Initialize extcon device */
-	info->edev = devm_extcon_dev_allocate(&pdev->dev,
-					      max77693_extcon_cable);
-	if (IS_ERR(info->edev)) {
-		dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
-		return PTR_ERR(info->edev);
-	}
-
-	ret = devm_extcon_dev_register(&pdev->dev, info->edev);
-	if (ret) {
-		dev_err(&pdev->dev, "failed to register extcon device\n");
-		return ret;
-	}
-
 	/* Initialize MUIC register by using platform data or default data */
 	if (pdata && pdata->muic_data) {
 		init_data = pdata->muic_data->init_data;

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed
  2026-11-06 13:17 ` [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed hyunjungg
@ 2026-11-06 15:46   ` Hans de Goede
  0 siblings, 0 replies; 7+ messages in thread
From: Hans de Goede @ 2026-11-06 15:46 UTC (permalink / raw)
  To: hyunjungg, MyungJoo Ham, Chanwoo Choi
  Cc: linux-kernel, Krzysztof Kozlowski, linux-samsung-soc, stable

Hi,

On 6-Nov-26 14:17, hyunjungg wrote:
> From: Hyunjung Ko <hj351016@gmail.com>
> 
> int3496_probe() registers the debounce work for device-managed
> cancellation before it acquires any of the resources that the work
> handler dereferences:
> 
> 	devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
> 	...
> 	data->gpio_usb_id  = devm_gpiod_get(dev, "id", ...);
> 	data->gpio_vbus_en = devm_gpiod_get(dev, "vbus", ...);
> 	data->gpio_usb_mux = devm_gpiod_get(dev, "mux", ...);
> 	data->edev = devm_extcon_dev_allocate(dev, int3496_cable);
> 	devm_extcon_dev_register(dev, data->edev);
> 	devm_request_threaded_irq(dev, data->usb_id_irq, ...);
> 
> devres releases resources in reverse registration order, so on unbind
> the extcon device and all three GPIO descriptors are released first and
> the work is cancelled last:
> 
> 	/* free_irq() */
> 	devm_extcon_dev_unregister() / extcon freed
> 	gpiod_put(mux) / gpiod_put(vbus_en) / gpiod_put(usb_id)
> 	...
> 	cancel_delayed_work_sync(&data->work);		/* too late */
> 
> int3496_thread_isr() arms the work with mod_delayed_work() on every ID
> pin edge, so a work instance can be pending or running at unbind. The
> handler int3496_do_usb_id() dereferences data->gpio_usb_id
> (gpiod_get_value_cansleep), data->gpio_usb_mux, data->gpio_vbus_en and
> data->edev (extcon_set_state_sync) with no guard. If it runs in the
> window after the GPIOs/extcon device have been released but before the
> work is cancelled, it operates on freed resources -> use-after-free.
> 
> Register the work for cancellation after the GPIOs and extcon device
> are acquired and registered (and before the IRQ, which arms it), so
> devres (LIFO) frees the IRQ first, then cancels the work, and only then
> releases the GPIOs and extcon device. devm_delayed_work_autocancel()
> still performs INIT_DELAYED_WORK() before the work is first queued
> (queue_delayed_work() below, and the IRQ handler, both run after it).
> 
> Found by static analysis / code review; compile-tested only, not
> reproduced on hardware (requires an INT3496 ACPI USB-ID mux device).
> 
> Fixes: f94a5becabf4 ("extconn: Clean-up few drivers by using managed work init")
> Cc: stable@vger.kernel.org
> Signed-off-by: Hyunjung Ko <hj351016@gmail.com>

Thanks, patch looks good to me:

Reviewed-by: Hans de Goede <johannes.goede@oss.qualcomm.com>

Regards,

Hans



> ---
> diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c
> index 7d16d5b..40774fb 100644
> --- a/drivers/extcon/extcon-intel-int3496.c
> +++ b/drivers/extcon/extcon-intel-int3496.c
> @@ -130,9 +130,6 @@ static int int3496_probe(struct platform_device *pdev)
>  		return -ENOMEM;
>  
>  	data->dev = dev;
> -	ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
> -	if (ret)
> -		return ret;
>  
>  	data->gpio_usb_id =
>  		devm_gpiod_get(dev, "id", GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
> @@ -169,6 +166,10 @@ static int int3496_probe(struct platform_device *pdev)
>  		return ret;
>  	}
>  
> +	ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id);
> +	if (ret)
> +		return ret;
> +
>  	ret = devm_request_threaded_irq(dev, data->usb_id_irq,
>  					NULL, int3496_thread_isr,
>  					IRQF_SHARED | IRQF_ONESHOT |


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-11-06 15:46 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-06 13:17 [PATCH 0/5] extcon: fix work-vs-devres teardown use-after-free in MUIC/connector drivers hyunjungg
2026-11-06 13:17 ` [PATCH 1/5] extcon: intel-int3496: drain ID work before its GPIOs and extcon device are freed hyunjungg
2026-11-06 15:46   ` Hans de Goede
2026-11-06 13:17 ` [PATCH 2/5] extcon: palmas: drain ID-detect work before the extcon device is freed hyunjungg
2026-11-06 13:17 ` [PATCH 3/5] extcon: max8997: drain MUIC IRQ work before " hyunjungg
2026-11-06 13:17 ` [PATCH 4/5] extcon: max14577: " hyunjungg
2026-11-06 13:17 ` [PATCH 5/5] extcon: max77693: " hyunjungg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®