Thread (7 messages) 7 messages, 1 author, 3d ago

[PATCH net v6 5/6] nfc: serialize firmware download state

flat view
WARM3d

From: Shubham Antil <hidden>
Date: 2026-10-21 03:25:03
Also in: 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_fw_download() marks a firmware download in progress but does not reject
a second request.  An overlapping request can replace driver-owned download
state, and an error from that request clears the shared in-progress flag
while the first download is still running.

Make the in-progress state atomic and claim it before invoking the driver.
An overlapping request then leaves the first download's state unchanged.
Copy the firmware name before releasing that claim, then send the
completion notification after the release so userspace can immediately
start its next operation.  Use release/acquire ordering so the next
operation observes the completed download cleanup.

OpenAI Codex, gpt-5.6-sol assisted a source audit of NFC firmware teardown.

Fixes: 9674da8759df ("NFC: Add firmware upload netlink command")
Cc: stable@vger.kernel.org
Assisted-by: LLM KASAN
Signed-off-by: Shubham Antil <redacted>
---
 drivers/nfc/nfcmrvl/main.c       |  4 ++--
 drivers/nfc/nfcmrvl/uart.c       |  4 ++--
 drivers/nfc/st-nci/vendor_cmds.c |  8 ++++----
 include/net/nfc/nfc.h            |  3 ++-
 net/nfc/core.c                   | 19 ++++++++++++++-----
 5 files changed, 24 insertions(+), 14 deletions(-)
diff --git a/drivers/nfc/nfcmrvl/main.c b/drivers/nfc/nfcmrvl/main.c
index b6f47cce0f1f..27fadd64155e 100644
--- a/drivers/nfc/nfcmrvl/main.c
+++ b/drivers/nfc/nfcmrvl/main.c
@@ -189,7 +189,7 @@ void nfcmrvl_nci_unregister_dev(struct nfcmrvl_private *priv)
 	if (priv->if_ops->nci_quiesce)
 		priv->if_ops->nci_quiesce(priv);
 
-	if (priv->ndev->nfc_dev->fw_download_in_progress)
+	if (atomic_read(&priv->ndev->nfc_dev->fw_download_in_progress))
 		nfcmrvl_fw_dnld_abort(priv);
 
 	nfcmrvl_fw_dnld_deinit(priv);
@@ -213,7 +213,7 @@ int nfcmrvl_nci_recv_frame(struct nfcmrvl_private *priv, struct sk_buff *skb)
 		}
 	}
 
-	if (priv->ndev->nfc_dev->fw_download_in_progress) {
+	if (atomic_read(&priv->ndev->nfc_dev->fw_download_in_progress)) {
 		nfcmrvl_fw_dnld_recv_frame(priv, skb);
 		return 0;
 	}
diff --git a/drivers/nfc/nfcmrvl/uart.c b/drivers/nfc/nfcmrvl/uart.c
index 95223c20d50f..d87f6497baf2 100644
--- a/drivers/nfc/nfcmrvl/uart.c
+++ b/drivers/nfc/nfcmrvl/uart.c
@@ -176,7 +176,7 @@ static void nfcmrvl_nci_uart_tx_start(struct nci_uart *nu)
 {
 	struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
 
-	if (priv->ndev->nfc_dev->fw_download_in_progress)
+	if (atomic_read(&priv->ndev->nfc_dev->fw_download_in_progress))
 		return;
 
 	/* Remove BREAK to wake up the NFCC */
@@ -190,7 +190,7 @@ static void nfcmrvl_nci_uart_tx_done(struct nci_uart *nu)
 {
 	struct nfcmrvl_private *priv = (struct nfcmrvl_private *)nu->drv_data;
 
-	if (priv->ndev->nfc_dev->fw_download_in_progress)
+	if (atomic_read(&priv->ndev->nfc_dev->fw_download_in_progress))
 		return;
 
 	/*
diff --git a/drivers/nfc/st-nci/vendor_cmds.c b/drivers/nfc/st-nci/vendor_cmds.c
index 6335d7afca24..ec577d497500 100644
--- a/drivers/nfc/st-nci/vendor_cmds.c
+++ b/drivers/nfc/st-nci/vendor_cmds.c
@@ -158,11 +158,12 @@ static int st_nci_hci_dm_fwupd_start(struct nfc_dev *dev, void *data,
 	int r;
 	struct nci_dev *ndev = nfc_get_drvdata(dev);
 
-	dev->fw_download_in_progress = true;
+	if (atomic_cmpxchg_acquire(&dev->fw_download_in_progress, 0, 1))
+		return -EBUSY;
 	r = nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
 			ST_NCI_HCI_DM_FWUPD_START, data, data_len, NULL);
 	if (r)
-		dev->fw_download_in_progress = false;
+		atomic_set_release(&dev->fw_download_in_progress, 0);
 
 	return r;
 }
@@ -181,8 +182,7 @@ static int st_nci_hci_dm_direct_load(struct nfc_dev *dev, void *data,
 {
 	struct nci_dev *ndev = nfc_get_drvdata(dev);
 
-	if (dev->fw_download_in_progress) {
-		dev->fw_download_in_progress = false;
+	if (atomic_xchg(&dev->fw_download_in_progress, 0)) {
 		return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
 				ST_NCI_HCI_DM_LOAD, data, data_len, NULL);
 	}
diff --git a/include/net/nfc/nfc.h b/include/net/nfc/nfc.h
index bcafab5c53e5..f6cb17f46ca7 100644
--- a/include/net/nfc/nfc.h
+++ b/include/net/nfc/nfc.h
@@ -11,6 +11,7 @@
 #ifndef __NET_NFC_H
 #define __NET_NFC_H
 
+#include <linux/atomic.h>
 #include <linux/nfc.h>
 #include <linux/device.h>
 #include <linux/skbuff.h>
@@ -172,7 +173,7 @@ struct nfc_dev {
 	int targets_generation;
 	struct device dev;
 	bool dev_up;
-	bool fw_download_in_progress;
+	atomic_t fw_download_in_progress;
 	u8 rf_mode;
 	bool polling;
 	struct nfc_target *active_target;
diff --git a/net/nfc/core.c b/net/nfc/core.c
index f521669293f0..1f335fa7f6f0 100644
--- a/net/nfc/core.c
+++ b/net/nfc/core.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/rfkill.h>
 #include <linux/nfc.h>
+#include <linux/string.h>
 
 #include <net/genetlink.h>
 
@@ -53,10 +54,14 @@ int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name)
 		goto error;
 	}
 
-	dev->fw_download_in_progress = true;
+	if (atomic_cmpxchg_acquire(&dev->fw_download_in_progress, 0, 1)) {
+		rc = -EBUSY;
+		goto error;
+	}
+
 	rc = dev->ops->fw_download(dev, firmware_name);
 	if (rc)
-		dev->fw_download_in_progress = false;
+		atomic_set_release(&dev->fw_download_in_progress, 0);
 
 error:
 	device_unlock(&dev->dev);
@@ -73,9 +78,12 @@ error:
 int nfc_fw_download_done(struct nfc_dev *dev, const char *firmware_name,
 			 u32 result)
 {
-	dev->fw_download_in_progress = false;
+	char name[NFC_FIRMWARE_NAME_MAXSIZE + 1];
+
+	strscpy(name, firmware_name, sizeof(name));
+	atomic_set_release(&dev->fw_download_in_progress, 0);
 
-	return nfc_genl_fw_download_done(dev, firmware_name, result);
+	return nfc_genl_fw_download_done(dev, name, result);
 }
 EXPORT_SYMBOL(nfc_fw_download_done);
 
@@ -104,7 +112,7 @@ int nfc_dev_up(struct nfc_dev *dev)
 		goto error;
 	}
 
-	if (dev->fw_download_in_progress) {
+	if (atomic_read_acquire(&dev->fw_download_in_progress)) {
 		rc = -EBUSY;
 		goto error;
 	}
@@ -1073,6 +1081,7 @@ struct nfc_dev *nfc_allocate_device(const struct nfc_ops *ops,
 	dev->dev.class = &nfc_class;
 	dev_set_name(&dev->dev, "nfc%d", dev->idx);
 	device_initialize(&dev->dev);
+	atomic_set(&dev->fw_download_in_progress, 0);
 
 	dev->ops = ops;
 	dev->supported_protocols = supported_protocols;
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help