[PATCH net v2 1/2] nfc: reject SE I/O while a device is shutting down

From: Fan Wu

Date: Thu Jul 30 2026 - 22:47:30 EST


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@xxxxxxxxxxxxxxx # 6.6+
Signed-off-by: Fan Wu <fanwu01@xxxxxxxxxx>
---

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;