DORMANTno replies

[PATCH net v2] nfc: trf7970a: drain timeout_work and keep trf->lock valid across teardown

From: Fan Wu <hidden>
Date: 2026-07-28 03:08:22
Also in: linux-wireless, lkml, oe-linux-nfc, stable
Subsystem: nfc subsystem, the rest, ti trf7970a nfc driver · Maintainers: David Heidelberg, Linus Torvalds, Mark Greer

The driver never drains trf->timeout_work with a blocking primitive:
every cancel_delayed_work() in the driver is the non-blocking variant,
kept that way on purpose so the timeout handler can consume its return
value for the ignore_timeout protocol. A timeout handler that is already
running and blocked on trf->lock during trf7970a_remove() therefore
survives past the teardown, then acquires the lock and dereferences trf
-- a use-after-free.

trf7970a_remove() also calls mutex_destroy(&trf->lock) before the devm
chain frees trf, while the threaded IRQ handler trf7970a_irq() takes the
same lock at entry and is only synchronised by the devm_free_irq() that
runs after .remove() returns; it can thus observe the destroyed lock.

Fix both by draining timeout_work with cancel_delayed_work_sync() and by
dropping the premature mutex_destroy():

In trf7970a_remove(), cancel timeout_work between
nfc_digital_unregister_device() and nfc_digital_free_device().
nfc_digital_unregister_device() flushes the digital core's cmd_work,
which invokes the driver's in_send_cmd/tg_send_cmd/tg_listen entry points
and arms timeout_work; cancelling timeout_work before unregistering could
let a final in-flight command re-arm it after the drain. The handler also
dereferences trf->ddev, so the drain must precede freeing the digital
device. On the probe-error path the device was never registered, so the
cancel runs directly before nfc_digital_free_device().

Drop mutex_destroy(&trf->lock) from trf7970a_remove() and from the
err_destroy_lock probe-error label. trf is devm_kzalloc()'d and
devm_request_threaded_irq() is registered after it, so devm reverse-order
cleanup runs devm_free_irq() (synchronising the threaded IRQ handler)
before freeing trf. The mutex is embedded in trf, so leaving it valid
until then lets the IRQ handler be drained before the lock goes away.

This issue was found by an in-house static analysis tool.

Fixes: 165063f1dac4 ("NFC: trf7970a: Add driver with ISO/IEC 14443 Type 2 Tag Support")
Cc: stable@vger.kernel.org
Assisted-by: Codex:gpt-5.6
Signed-off-by: Fan Wu <redacted>
---
Changes in v2:
  - Move the cancel_delayed_work_sync(&trf->timeout_work) in
    trf7970a_remove() to after nfc_digital_unregister_device() and
    before nfc_digital_free_device(), so the drain runs after the
    digital core flushes its cmd_work instead of racing it. The
    probe-error-path cancel and the mutex_destroy() removals are
    unchanged.

 drivers/nfc/trf7970a.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
index f22e091..9dc6931 100644
--- a/drivers/nfc/trf7970a.c
+++ b/drivers/nfc/trf7970a.c
@@ -2207,13 +2207,13 @@ static int trf7970a_probe(struct spi_device *spi)
 err_shutdown:
 	trf7970a_shutdown(trf);
 err_free_ddev:
+	cancel_delayed_work_sync(&trf->timeout_work);
 	nfc_digital_free_device(trf->ddev);
 err_disable_vddio_regulator:
 	regulator_disable(trf->vddio_regulator);
 err_disable_vin_regulator:
 	regulator_disable(trf->vin_regulator);
 err_destroy_lock:
-	mutex_destroy(&trf->lock);
 	return ret;
 }
 
@@ -2228,12 +2228,11 @@ static void trf7970a_remove(struct spi_device *spi)
 	mutex_unlock(&trf->lock);
 
 	nfc_digital_unregister_device(trf->ddev);
+	cancel_delayed_work_sync(&trf->timeout_work);
 	nfc_digital_free_device(trf->ddev);
 
 	regulator_disable(trf->vddio_regulator);
 	regulator_disable(trf->vin_regulator);
-
-	mutex_destroy(&trf->lock);
 }
 
 #ifdef CONFIG_PM_SLEEP
-- 
2.34.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help