[PATCH net v2 1/2] nfc: reject SE I/O while a device is shutting down
From: Fan Wu <hidden>
Date: 2026-07-31 02:45:00
Also in:
lkml, oe-linux-nfc, stable
Subsystem:
networking [general], nfc subsystem, the rest · Maintainers:
"David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, David Heidelberg, Linus Torvalds
nfc_se_io() is the only secure-element entry point that does not check dev->shutting_down: nfc_enable_se(), nfc_disable_se(), nfc_dev_up(), nfc_dev_down() and nfc_start_poll() all reject new operations once nfc_unregister_rfkill() has set the flag under the device lock. An NFC_CMD_SE_IO request racing device removal can therefore still reach a driver's se_io op after teardown has begun, arming driver state (for example a timer) that the driver's later drain cannot account for. Add the same shutting_down check the other SE operations already perform; the existing error path frees the freshly allocated se_io_ctx. This also adds an optional nci_ops::pre_unregister hook, called in nci_unregister_device() right after nfc_unregister_rfkill() (the admission barrier is up, so new operations are rejected) and before nci_close_device()/destroy_workqueue() (the NCI core and its workqueues still exist). An NCI driver whose teardown helper touches the NCI core can use it to drain driver-owned state at the correct point. The hook is optional; nci_ops initialisers that leave it NULL are unaffected. This follows the barrier-first teardown ordering from d2492688bb9f. Cc: stable@vger.kernel.org # 6.6+ Signed-off-by: Fan Wu <redacted> --- include/net/nfc/nci_core.h | 1 + net/nfc/nci/core.c | 3 +++ net/nfc/netlink.c | 5 +++++ 3 files changed, 9 insertions(+)
diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 664d5058e..754a67185 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h@@ -82,6 +82,7 @@ struct nci_ops { struct sk_buff *skb); void (*hci_cmd_received)(struct nci_dev *ndev, u8 pipe, u8 cmd, struct sk_buff *skb); + void (*pre_unregister)(struct nci_dev *ndev); const struct nci_driver_ops *prop_ops; size_t n_prop_ops;
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 5f46c4b57..86286c076 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c@@ -1321,6 +1321,9 @@ void nci_unregister_device(struct nci_dev *ndev) nfc_unregister_rfkill(ndev->nfc_dev); + if (ndev->ops->pre_unregister) + ndev->ops->pre_unregister(ndev); + /* This set_bit is not protected with specialized barrier, * However, it is fine because the mutex_lock(&ndev->req_lock); * in nci_close_device() will help to emit one.
diff --git a/net/nfc/netlink.c b/net/nfc/netlink.c
index 0c58824cb..3132f7a90 100644
--- a/net/nfc/netlink.c
+++ b/net/nfc/netlink.c@@ -1426,6 +1426,11 @@ static int nfc_se_io(struct nfc_dev *dev, u32 se_idx, device_lock(&dev->dev); + if (dev->shutting_down) { + rc = -ENODEV; + goto error; + } + if (!device_is_registered(&dev->dev)) { rc = -ENODEV; goto error;