diff options
| author | Zhenghang Xiao <kipreyyy@gmail.com> | 2026-05-26 18:31:21 +0800 |
|---|---|---|
| committer | David Heidelberg <david@ixit.cz> | 2026-07-26 21:26:03 +0200 |
| commit | bd7e7a2ad29cb8d8cfbcc163a55dca17caf90302 (patch) | |
| tree | 32b1a34550f868100bc6712180f72a5d94c4eaaa | |
| parent | 75fa10dbc2f58c218263ee17fd3724c3f06308f4 (diff) | |
| download | linux-next-bd7e7a2ad29cb8d8cfbcc163a55dca17caf90302.tar.gz linux-next-bd7e7a2ad29cb8d8cfbcc163a55dca17caf90302.zip | |
nfc: nci: fix double completion race in nci_data_exchange_complete
nci_close_device() and nci_rx_work can both call
nci_data_exchange_complete() concurrently. After commit 4527025d440ce8
("nfc: nci: fix circular locking dependency in nci_close_device") moved
flush_workqueue(ndev->rx_wq) after mutex_unlock(&ndev->req_lock),
rx_work is no longer serialized with the explicit completion call in the
close path. Both callers read the non-NULL callback pointer and invoke
rawsock_data_exchange_complete(), which calls sock_put() -- but only one
sock_hold() was taken, so the second sock_put() underflows the refcount
and frees the socket while it is still in use.
Replace the bare clear_bit(NCI_DATA_EXCHANGE) with
test_and_clear_bit() so that only the first caller proceeds to invoke
the callback.
Fixes: 4527025d440c ("nfc: nci: fix circular locking dependency in nci_close_device")
Signed-off-by: Zhenghang Xiao <kipreyyy@gmail.com>
Link: https://patch.msgid.link/20260526103121.47957-1-kipreyyy@gmail.com
Signed-off-by: David Heidelberg <david@ixit.cz>
| -rw-r--r-- | net/nfc/nci/data.c | 10 |
1 files changed, 5 insertions, 5 deletions
diff --git a/net/nfc/nci/data.c b/net/nfc/nci/data.c index 5f98c73db5af..4253edea5c8d 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, 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; + } if (cb) { /* forward skb to nfc core */ |
