Patch "NFC: add NCI_UNREG flag to eliminate the race" has been added to the 5.10-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    NFC: add NCI_UNREG flag to eliminate the race

to the 5.10-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     nfc-add-nci_unreg-flag-to-eliminate-the-race.patch
and it can be found in the queue-5.10 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit b92f5c2d7595629c0446de24cbb6fc1fddc9935e
Author: Lin Ma <linma@xxxxxxxxxx>
Date:   Tue Nov 16 23:27:32 2021 +0800

    NFC: add NCI_UNREG flag to eliminate the race
    
    [ Upstream commit 48b71a9e66c2eab60564b1b1c85f4928ed04e406 ]
    
    There are two sites that calls queue_work() after the
    destroy_workqueue() and lead to possible UAF.
    
    The first site is nci_send_cmd(), which can happen after the
    nci_close_device as below
    
    nfcmrvl_nci_unregister_dev   |  nfc_genl_dev_up
      nci_close_device           |
        flush_workqueue          |
        del_timer_sync           |
      nci_unregister_device      |    nfc_get_device
        destroy_workqueue        |    nfc_dev_up
        nfc_unregister_device    |      nci_dev_up
          device_del             |        nci_open_device
                                 |          __nci_request
                                 |            nci_send_cmd
                                 |              queue_work !!!
    
    Another site is nci_cmd_timer, awaked by the nci_cmd_work from the
    nci_send_cmd.
    
      ...                        |  ...
      nci_unregister_device      |  queue_work
        destroy_workqueue        |
        nfc_unregister_device    |  ...
          device_del             |  nci_cmd_work
                                 |  mod_timer
                                 |  ...
                                 |  nci_cmd_timer
                                 |    queue_work !!!
    
    For the above two UAF, the root cause is that the nfc_dev_up can race
    between the nci_unregister_device routine. Therefore, this patch
    introduce NCI_UNREG flag to easily eliminate the possible race. In
    addition, the mutex_lock in nci_close_device can act as a barrier.
    
    Signed-off-by: Lin Ma <linma@xxxxxxxxxx>
    Fixes: 6a2968aaf50c ("NFC: basic NCI protocol implementation")
    Reviewed-by: Jakub Kicinski <kuba@xxxxxxxxxx>
    Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@xxxxxxxxxxxxx>
    Link: https://lore.kernel.org/r/20211116152732.19238-1-linma@xxxxxxxxxx
    Signed-off-by: Jakub Kicinski <kuba@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/include/net/nfc/nci_core.h b/include/net/nfc/nci_core.h
index 33979017b7824..004e49f748419 100644
--- a/include/net/nfc/nci_core.h
+++ b/include/net/nfc/nci_core.h
@@ -30,6 +30,7 @@ enum nci_flag {
 	NCI_UP,
 	NCI_DATA_EXCHANGE,
 	NCI_DATA_EXCHANGE_TO,
+	NCI_UNREG,
 };
 
 /* NCI device states */
diff --git a/net/nfc/nci/core.c b/net/nfc/nci/core.c
index 4d3ab0f44c9f4..e38719e2ee582 100644
--- a/net/nfc/nci/core.c
+++ b/net/nfc/nci/core.c
@@ -473,6 +473,11 @@ static int nci_open_device(struct nci_dev *ndev)
 
 	mutex_lock(&ndev->req_lock);
 
+	if (test_bit(NCI_UNREG, &ndev->flags)) {
+		rc = -ENODEV;
+		goto done;
+	}
+
 	if (test_bit(NCI_UP, &ndev->flags)) {
 		rc = -EALREADY;
 		goto done;
@@ -536,6 +541,10 @@ static int nci_open_device(struct nci_dev *ndev)
 static int nci_close_device(struct nci_dev *ndev)
 {
 	nci_req_cancel(ndev, ENODEV);
+
+	/* This mutex needs to be held as a barrier for
+	 * caller nci_unregister_device
+	 */
 	mutex_lock(&ndev->req_lock);
 
 	if (!test_and_clear_bit(NCI_UP, &ndev->flags)) {
@@ -573,8 +582,8 @@ static int nci_close_device(struct nci_dev *ndev)
 
 	del_timer_sync(&ndev->cmd_timer);
 
-	/* Clear flags */
-	ndev->flags = 0;
+	/* Clear flags except NCI_UNREG */
+	ndev->flags &= BIT(NCI_UNREG);
 
 	mutex_unlock(&ndev->req_lock);
 
@@ -1259,6 +1268,12 @@ void nci_unregister_device(struct nci_dev *ndev)
 {
 	struct nci_conn_info    *conn_info, *n;
 
+	/* This set_bit is not protected with specialized barrier,
+	 * However, it is fine because the mutex_lock(&ndev->req_lock);
+	 * in nci_close_device() will help to emit one.
+	 */
+	set_bit(NCI_UNREG, &ndev->flags);
+
 	nci_close_device(ndev);
 
 	destroy_workqueue(ndev->cmd_wq);



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux