All of lore.kernel.org
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: shubham@octane.security
Cc: netdev@vger.kernel.org, david@ixit.cz,
	oe-linux-nfc@lists.linux.dev, gio@octane.security,
	stable@vger.kernel.org, kuba@kernel.org
Subject: Re: [PATCH net v6 5/6] nfc: serialize firmware download state
Date: Sun, 25 Oct 2026 04:03:58 +0000	[thread overview]
Message-ID: <179290103824.3134.1739068989213537290@kernel.org> (raw)
In-Reply-To: <20261021032443.44412-6-shubham@octane.security>

Thank you for your contribution! Sashiko AI review found 3 potential
issue(s) to consider.

Critical: 0 · High: 1 · Medium: 2 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] At this commit nfc_fw_download_done() releases the shared
  fw_download_in_progress claim unconditionally, with no owner or…
- [Medium] The patch changes how the st-nci vendor command
  ST_NCI_HCI_DM_FWUPD_START behaves, and the commit message does not…
- [Medium] nfc_fw_download() now claims fw_download_in_progress with
  atomic_cmpxchg_acquire(0->1) and rejects every later request with…

--

nfc: serialize firmware download state

This patch turns nfc_dev->fw_download_in_progress into an atomic_t. Both
nfc_fw_download() and the st-nci FWUPD_START vendor command now claim it
with cmpxchg and return -EBUSY if a download is already in progress.
nfc_fw_download_done() copies the firmware name, then releases the claim
before sending the netlink completion notification.

> diff --git a/drivers/nfc/st-nci/vendor_cmds.c b/drivers/nfc/st-nci/vendor_cmds.c
> index 6335d7afca24f..ec577d497500c 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;
>  }

[Severity: Medium]
This changes what the ST_NCI_HCI_DM_FWUPD_START vendor command does from
userspace, and the commit message doesn't mention it. Is that intended?

In st-nci the flag is set only by FWUPD_START. The only place it gets
cleared is st_nci_hci_dm_direct_load(). FWUPD_STOP leaves it alone:

drivers/nfc/st-nci/vendor_cmds.c:st_nci_hci_dm_fwupd_end() {
	...
	return nci_hci_send_cmd(ndev, ST_NCI_DEVICE_MGNT_GATE,
			ST_NCI_HCI_DM_FWUPD_STOP, data, data_len, NULL);
}

st_nci_hci_dm_reset() and the close/teardown paths don't clear it either.

Suppose a session is abandoned after a successful FWUPD_START: the tool
crashes, or it sends START then STOP, or START then RESET. Every later
FWUPD_START then returns -EBUSY. Before this patch, START was simply sent
again.

The only ways out seem to be sending DIRECT_LOAD, which sends
ST_NCI_HCI_DM_LOAD with user data to a chip that may already have left
update mode, or rebinding the driver.

There is a second change userspace can see. For NCI devices
dev->ops->fw_download is always nci_fw_download(). So while a vendor
START is pending, the new cmpxchg in nfc_fw_download() makes
NFC_CMD_FW_DOWNLOAD return -EBUSY on st-nci, where it used to return
-ENOTSUPP. Is that intended, given st-nci has no core firmware download
state to protect?

[ ... ]

> diff --git a/net/nfc/core.c b/net/nfc/core.c
> index f521669293f05..1f335fa7f6f07 100644
> --- a/net/nfc/core.c
> +++ b/net/nfc/core.c

[ ... ]

> @@ -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;
> +	}
> +

[Severity: Medium]
Can this leave a pn544 device stuck in -EBUSY for good?

pn544_hci_i2c_fw_download() returns 0 as soon as it has scheduled the
work:

drivers/nfc/pn544/i2c.c:pn544_hci_i2c_fw_download() {
	...
	phy->fw_work_state = FW_WORK_STATE_START;

	schedule_work(&phy->fw_work);

	return 0;
}

pn544_hci_i2c_fw_work() then waits in FW_WORK_STATE_WAIT_WRITE_ANSWER,
WAIT_CHECK_ANSWER or WAIT_SECURE_WRITE_ANSWER. It moves on only when the
IRQ handler reschedules the work, which needs run_mode == PN544_FW_MODE
and an interrupt from the chip. drivers/nfc/pn544/ has no timer or
timeout for these states.

So if the controller never answers (hung chip, I2C fault, bad image),
nfc_fw_download_done() is never called. After that:

  - every NFC_CMD_FW_DOWNLOAD fails with -EBUSY from this cmpxchg
  - nfc_dev_up() fails with -EBUSY from atomic_read_acquire()

This lasts until the i2c driver is unbound, which completes with -ENODEV
in pn544_hci_i2c_remove(), or until a reboot.

Before this patch, a second NFC_CMD_FW_DOWNLOAD reset fw_work_state to
START and rescheduled the work, so userspace could recover. Should there
be a timeout or a core abort path before this recovery is removed?

>  	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 @@ int nfc_fw_download(struct nfc_dev *dev, const char *firmware_name)
>  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);

[Severity: High]
Can a second completion of the same download clear the claim of a newer
download here?

nfc_fw_download_done() releases the claim unconditionally. There is no
owner or generation check. At this commit nfcmrvl can finish one
download twice: fw_dnld_timeout() runs in timer context, and both it and
fw_dnld_rx_work() can call fw_dnld_over().

fw_dnld_over() syncs the timer only if it is still pending:

drivers/nfc/nfcmrvl/fw_dnld.c:fw_dnld_over() {
	...
	if (timer_pending(&priv->fw_dnld.timer))
		timer_delete_sync(&priv->fw_dnld.timer);
	...
}

So it doesn't wait for a timeout callback that is already running. Also,
the rx_work loop keeps dequeuing frames after fw_dnld_over(0).

A second completion can then:

  - call release_firmware() again on the same firmware, or on a newer one
  - delete a newer download's timer
  - halt the chip
  - clear the newer download's fw_download_in_progress claim

Later in the same series, the commit "nfc: nfcmrvl: stop firmware work
before unregistering" seems to fix this. There, fw_dnld_over() claims
completion through fw_dnld->active under rx_lock, calls
timer_delete_sync() unconditionally, and runs the timeout as
timeout_work on rx_wq. At this commit, though, the window is still
open.

>  
> -	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);

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20261021032443.44412-1-shubham%40octane.security

  reply	other threads:[~2026-10-25  4:03 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21  3:24 [PATCH net v6 0/6] nfc: fix NCI UART and firmware teardown races Shubham Antil
2026-10-21  3:24 ` [PATCH net v6 1/6] nfc: nci: skip controller reset while unregistering Shubham Antil
2026-10-25  4:03   ` netdev-bot+sashiko
2026-10-21  3:24 ` [PATCH net v6 2/6] nfc: nci: uart: serialize driver selection Shubham Antil
2026-10-21  3:24 ` [PATCH net v6 3/6] nfc: nci: uart: quiesce callbacks during teardown Shubham Antil
2026-10-25  4:03   ` netdev-bot+sashiko
2026-10-28 16:43     ` Simon Horman
2026-10-28 16:58       ` Shubham Antil
2026-10-21  3:24 ` [PATCH net v6 4/6] nfc: nfcmrvl: publish UART state before registration Shubham Antil
2026-10-21  3:24 ` [PATCH net v6 5/6] nfc: serialize firmware download state Shubham Antil
2026-10-25  4:03   ` netdev-bot+sashiko [this message]
2026-10-21  3:24 ` [PATCH net v6 6/6] nfc: nfcmrvl: stop firmware work before unregistering Shubham Antil
2026-10-25  4:03   ` netdev-bot+sashiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=179290103824.3134.1739068989213537290@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=david@ixit.cz \
    --cc=gio@octane.security \
    --cc=kuba@kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=oe-linux-nfc@lists.linux.dev \
    --cc=shubham@octane.security \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.