diff --git a/drivers/nfc/nfcsim.c b/drivers/nfc/nfcsim.c
index f8e5c4da3b13..52d1fbf8e532 100644
--- a/drivers/nfc/nfcsim.c
+++ b/drivers/nfc/nfcsim.c
@@ -239,9 +239,15 @@ static int nfcsim_send(struct nfc_digital_dev *ddev, struct sk_buff *skb,
static void nfcsim_abort_cmd(struct nfc_digital_dev *ddev)
{
- const struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
+ struct nfcsim *dev = nfc_digital_get_drvdata(ddev);
nfcsim_link_recv_cancel(dev->link_in);
+ /*
+ * recv_work blocks in the link wait and then calls the digital
+ * completion. Wake it and wait so the callback cannot run after
+ * abort_cmd() returns.
+ */
+ flush_work(&dev->recv_work);
}
static int nfcsim_switch_rf(struct nfc_digital_dev *ddev, bool on)diff --git a/drivers/nfc/port100.c b/drivers/nfc/port100.c
index e769a30b8b5c..738acb87f19b 100644
--- a/drivers/nfc/port100.c
+++ b/drivers/nfc/port100.c
@@ -968,6 +968,13 @@ static void port100_abort_cmd(struct nfc_digital_dev *ddev)
/* cancel the urb request */
usb_kill_urb(dev->in_urb);
+
+ /*
+ * The URB completion handler only queues cmd_complete_work, which
+ * is what calls back into the digital stack. Run it before
+ * returning so that callback cannot outlive abort_cmd().
+ */
+ flush_work(&dev->cmd_complete_work);
}
static struct sk_buff *port100_alloc_skb(const struct port100 *dev, unsigned int size)
diff --git a/drivers/nfc/st95hf/core.c b/drivers/nfc/st95hf/core.c
index 4d772a308bff..b39a5215dad7 100644
--- a/drivers/nfc/st95hf/core.c
+++ b/drivers/nfc/st95hf/core.c
@@ -856,9 +856,16 @@ static irqreturn_t st95hf_irq_thread_handler(int irq, void *st95hfcontext)
kfree_skb(skb_resp);
wtx = false;
cb_arg->rats = false;
- skb_resp = ERR_PTR(result);
- /* call of callback with error */
- cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg, skb_resp);
+ /*
+ * remove() frees the digital command before dropping rm_lock.
+ * The completion argument is then gone; do not write it.
+ */
+ if (!stcontext->nfcdev_free) {
+ skb_resp = ERR_PTR(result);
+ /* call of callback with error */
+ cb_arg->complete_cb(stcontext->ddev, cb_arg->cb_usrarg,
+ skb_resp);
+ }
/* up the semaphore before returning */
up(&stcontext->exchange_lock);
mutex_unlock(&stcontext->rm_lock);diff --git a/drivers/nfc/trf7970a.c b/drivers/nfc/trf7970a.c
index ddfc58c29964..87c833abb36a 100644
--- a/drivers/nfc/trf7970a.c
+++ b/drivers/nfc/trf7970a.c
@@ -1903,6 +1903,7 @@ static void trf7970a_abort_cmd(struct nfc_digital_dev *ddev)
trf->aborting = true;
break;
case TRF7970A_ST_LISTENING:
+ case TRF7970A_ST_LISTENING_MD:
trf->ignore_timeout = !cancel_delayed_work(&trf->timeout_work);
trf7970a_send_err_upstream(trf, -ECANCELED);
dev_dbg(trf->dev, "Abort process complete\n");
@@ -2017,6 +2018,8 @@ static void trf7970a_shutdown(struct trf7970a *trf)
case TRF7970A_ST_WAIT_FOR_RX_DATA_CONT:
case TRF7970A_ST_WAIT_TO_ISSUE_EOF:
case TRF7970A_ST_LISTENING:
+ case TRF7970A_ST_LISTENING_MD:
+ trf->ignore_timeout = !cancel_delayed_work(&trf->timeout_work);
trf7970a_send_err_upstream(trf, -ECANCELED);
fallthrough;
case TRF7970A_ST_IDLE:
@@ -2230,6 +2233,14 @@ static void trf7970a_remove(struct spi_device *spi)
mutex_unlock(&trf->lock);
nfc_digital_unregister_device(trf->ddev);
+
+ /*
+ * shutdown() leaves the chip powered off, so no IRQ or command path
+ * can arm the timeout again and unregister has cancelled cmd_work.
+ * Wait for a handler that was already running before freeing trf.
+ */
+ cancel_delayed_work_sync(&trf->timeout_work);
+
nfc_digital_free_device(trf->ddev);
regulator_disable(trf->vddio_regulator);
diff --git a/include/net/nfc/digital.h b/include/net/nfc/digital.h
index bb3e8fdc0692..d5ae45da5a33 100644
--- a/include/net/nfc/digital.h
+++ b/include/net/nfc/digital.h
@@ -131,7 +131,11 @@ typedef void (*nfc_digital_cmd_complete_t)(struct nfc_digital_dev *ddev,
* @switch_rf: Turns device radio on or off. The stack does not call explicitly
* switch_rf to turn the radio on. A call to in|tg_configure_hw must turn
* the device radio on.
- * @abort_cmd: Discard the last sent command.
+ * @abort_cmd: Discard the last sent command. When called from
+ * nfc_digital_unregister_device(), the command is freed after this
+ * returns, so the driver must ensure that its completion callback
+ * cannot run afterwards. Completions delivered from work items, IRQ
+ * handlers or timers must be cancelled or flushed before returning.
*
* Notes: Asynchronous functions have a timeout parameter. It is the driver
* responsibility to call the digital stack back through the
diff --git a/net/nfc/core.c b/net/nfc/core.c
index f521669293f0..af2c9bd1fadb 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -1133,7 +1133,7 @@ int nfc_register_device(struct nfc_dev *dev)
dev->rfkill = NULL;
}
}
- dev->shutting_down = false;
+ WRITE_ONCE(dev->shutting_down, false);
device_unlock(&dev->dev);
rc = nfc_genl_device_added(dev);
@@ -1167,7 +1167,7 @@ void nfc_unregister_rfkill(struct nfc_dev *dev)
rfk = dev->rfkill;
dev->rfkill = NULL;
}
- dev->shutting_down = true;
+ WRITE_ONCE(dev->shutting_down, true);
device_unlock(&dev->dev);
if (rfk) {diff --git a/net/nfc/digital_core.c b/net/nfc/digital_core.c
index 18236221d898..376dc78b5b19 100644
--- a/net/nfc/digital_core.c
+++ b/net/nfc/digital_core.c
@@ -162,7 +162,11 @@ static void digital_wq_cmd(struct work_struct *work)
cmd = list_first_entry_or_null(&ddev->cmd_queue, struct digital_cmd,
queue);
- if (!cmd || cmd->pending) {
+ /*
+ * Unregister has already handed an in-flight command to the driver
+ * and will free whatever is still queued. Do not submit another one.
+ */
+ if (READ_ONCE(ddev->nfc_dev->shutting_down) || !cmd || cmd->pending) {
mutex_unlock(&ddev->cmd_lock);
return;
}@@ -244,6 +248,11 @@ int digital_send_cmd(struct nfc_digital_dev *ddev, u8 cmd_type,
INIT_LIST_HEAD(&cmd->queue);
mutex_lock(&ddev->cmd_lock);
+ if (READ_ONCE(ddev->nfc_dev->shutting_down)) {
+ mutex_unlock(&ddev->cmd_lock);
+ kfree(cmd);
+ return -ENODEV;
+ }
list_add_tail(&cmd->queue, &ddev->cmd_queue);
mutex_unlock(&ddev->cmd_lock);
@@ -846,9 +855,31 @@ void nfc_digital_unregister_device(struct nfc_digital_dev *ddev)
cancel_work_sync(&ddev->cmd_work);
cancel_work_sync(&ddev->cmd_complete_work);
+ /*
+ * A command with pending set has been given to the driver as the
+ * completion argument. Freeing it here lets a late
+ * digital_send_cmd_complete() write cmd->resp into freed memory.
+ * abort_cmd() drops that exchange; the driver may schedule
+ * cmd_complete_work before it returns, so drain both works again.
+ */
+ digital_abort_cmd(ddev);
+ cancel_work_sync(&ddev->cmd_complete_work);
+ cancel_work_sync(&ddev->cmd_work);
+
list_for_each_entry_safe(cmd, n, &ddev->cmd_queue, queue) {
list_del(&cmd->queue);
+ /*
+ * A command that was never submitted still owns its request.
+ * A command whose completion work was cancelled above may
+ * have a response that nobody consumed.
+ */
+ if (!cmd->pending)
+ kfree_skb(cmd->req);
+
+ if (!IS_ERR_OR_NULL(cmd->resp))
+ kfree_skb(cmd->resp);
+
/* Call the command callback if any and pass it a ENODEV error.
* This gives a chance to the command issuer to free any
* allocated buffer.--
2.56.0