Re: [PATCH nfc] nfc: nci: fix double completion race in nci_data_exchange_complete
Simon Horman <[email protected]> Thu, 28 May 2026 21:16:26 +0100
| Newsgroups | dev.linux.lists.oe-linux-nfc,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
From: 'Simon Horman' <[email protected]> This is an AI-generated review of your patch. The human sending this email has considered the AI review valid, or at least plausible. Full review at: https://sashiko.dev --- Subject: nfc: nci: fix double completion race in nci_data_exchange_complete This patch attempts to resolve a double completion race condition by replacing clear_bit() with test_and_clear_bit() on the NCI_DATA_EXCHANGE flag. This is intended to prevent a socket refcount underflow when nci_close_device() and nci_rx_work() execute concurrently. > diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c > index 5f98c73db5afd..4253edea5c8db 100644 > --- a/net/nfc/nci/data.c > +++ b/net/nfc/nci/data.c > @@ -46,11 +46,11 @@ void nci_data_exchange_complete(struct nci_dev *ndev, struct sk_buff *skb, 1. Simon says: Although the issue flagged below is pre-existing, it appears to be exacerbated by this patch and thus I believe it needs to be considered in that context. [Severity: High] This is a pre-existing issue, but does an NTF packet with an invalid connection ID improperly clear the transceive state? Looking earlier in nci_data_exchange_complete(): conn_info = nci_get_conn_info_by_conn_id(ndev, conn_id); if (!conn_info) { kfree_skb(skb); clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); return; } If a misbehaving or malicious NFC hardware sends an error NTF packet with an invalid connection ID while a valid RF transceive operation is active, it would execute clear_bit(NCI_DATA_EXCHANGE, &ndev->flags). Would this abort the ongoing transceive state machine and drop the actual response when it arrives? 2. Simon says: Conversely, the issue flagged below seems to be orthogonal to this patch. And I think it can be treated in the context of possible follow-up. [Severity: Critical] This isn't a bug introduced by this patch, but is there a use-after-free race condition in nci_transceive()? In nci_transceive(), the NCI_DATA_EXCHANGE flag is set using test_and_set_bit() before the data_exchange_cb and data_exchange_cb_context pointers are updated on the conn_info structure. Since incoming packets and notifications are processed concurrently by nci_rx_work, could an exceptionally fast response or a concurrent interface error notification trigger nci_data_exchange_complete() immediately after the bit is set but before the callback pointers are updated? This could cause the lockless reader to fetch the stale callback and context pointers from a previous transceive socket and execute them. > timer_delete_sync(&ndev->data_timer); > clear_bit(NCI_DATA_EXCHANGE_TO, &ndev->flags); > > - /* Mark the exchange as done before calling the callback. > - * The callback (e.g. rawsock_data_exchange_complete) may > - * want to immediately queue another data exchange. > - */ > - clear_bit(NCI_DATA_EXCHANGE, &ndev->flags); > + /* Claim completion atomically -- both close and rx_work may race here */ > + if (!test_and_clear_bit(NCI_DATA_EXCHANGE, &ndev->flags)) { > + kfree_skb(skb); > + return; > + } 3. Simon says: Like issue 1. I believe the issue below should be considered in the context of this patch. [Severity: High] Will this change cause valid HCI and loopback packets to be dropped? The NCI_DATA_EXCHANGE bit is only set during RF transceive operations in nci_transceive(). Subsystems like HCI (via nci_hci_send_cmd()) and loopback send data without setting this global flag. If valid HCI or loopback data packets arrive, won't they fail this bit check, causing the SKB to be dropped and skipping callbacks like nci_hci_data_received_cb? Additionally, if an asynchronous packet arrives concurrently while a valid RF transceive is active, could it falsely consume the NCI_DATA_EXCHANGE bit? When the actual RF response arrives, it would then fail the check, drop the packet, and permanently leak the socket's reference count. > > if (cb) { > /* forward skb to nfc core */ > cb(cb_context, skb, err); 4. Simon says: And like issue 3. I believe this is orthogonal to this patch and can be considered in the context of possible follow-up. [Severity: High] This is a pre-existing issue, but does this code leak the SKB when an error status is returned? When nci_add_rx_data_frag() processes an incoming fragment with a non-zero status byte, it passes both the valid skb and the err code to nci_data_exchange_complete(), which forwards them to the cb here. Looking at core callbacks like rawsock_data_exchange_complete(): net/nfc/rawsock.c:rawsock_data_exchange_complete() { ... if (err) goto error; ... error: sock_put(sk); } The error label bypasses the error_skb label where kfree_skb(skb) is called. Does this result in an unconditional memory leak of the SKB whenever the hardware or external target responds with an error status? > } else if (skb) { > pr_err("no rx callback, dropping rx data...\n"); [ ... ]