Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

nfc: nci: fix circular locking dependency in nci_close_device

nci_close_device() flushes rx_wq and tx_wq while holding req_lock.
This causes a circular locking dependency because nci_rx_work()
running on rx_wq can end up taking req_lock too:

nci_rx_work -> nci_rx_data_packet -> nci_data_exchange_complete
-> __sk_destruct -> rawsock_destruct -> nfc_deactivate_target
-> nci_deactivate_target -> nci_request -> mutex_lock(&ndev->req_lock)

Move the flush of rx_wq after req_lock has been released.
This should safe (I think) because NCI_UP has already been cleared
and the transport is closed, so the work will see it and return
-ENETDOWN.

NIPA has been hitting this running the nci selftest with a debug
kernel on roughly 4% of the runs.

Fixes: 6a2968aaf50c ("NFC: basic NCI protocol implementation")
Reviewed-by: Ian Ray <ian.ray@gehealthcare.com>
Link: https://patch.msgid.link/20260317193334.988609-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+6 -4
+6 -4
net/nfc/nci/core.c
··· 579 579 skb_queue_purge(&ndev->rx_q); 580 580 skb_queue_purge(&ndev->tx_q); 581 581 582 - /* Flush RX and TX wq */ 583 - flush_workqueue(ndev->rx_wq); 582 + /* Flush TX wq, RX wq flush can't be under the lock */ 584 583 flush_workqueue(ndev->tx_wq); 585 584 586 585 /* Reset device */ ··· 591 592 msecs_to_jiffies(NCI_RESET_TIMEOUT)); 592 593 593 594 /* After this point our queues are empty 594 - * and no works are scheduled. 595 + * rx work may be running but will see that NCI_UP was cleared 595 596 */ 596 597 ndev->ops->close(ndev); 597 598 598 599 clear_bit(NCI_INIT, &ndev->flags); 599 600 600 - /* Flush cmd wq */ 601 + /* Flush cmd and tx wq */ 601 602 flush_workqueue(ndev->cmd_wq); 602 603 603 604 timer_delete_sync(&ndev->cmd_timer); ··· 611 612 ndev->flags &= BIT(NCI_UNREG); 612 613 613 614 mutex_unlock(&ndev->req_lock); 615 + 616 + /* rx_work may take req_lock via nci_deactivate_target */ 617 + flush_workqueue(ndev->rx_wq); 614 618 615 619 return 0; 616 620 }