scsi: qedf: Fix crash when MFW calls for protocol stats while function is still probing
authorChad Dupuis <cdupuis@marvell.com>
Thu, 16 Apr 2020 08:43:13 +0000 (01:43 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 24 Jun 2020 15:50:21 +0000 (17:50 +0200)
[ Upstream commit ad40f5256095c68dc17c991eb976261d5ea2daaa ]

The MFW may make a call to qed and then to qedf for protocol statistics
while the function is still probing.  If this happens it's possible that
some members of the struct qedf_ctx may not be fully initialized which can
result in a NULL pointer dereference or general protection fault.

To prevent this, add a new flag call QEDF_PROBING and set it when the
__qedf_probe() function is active. Then in the qedf_get_protocol_tlv_data()
function we can check if the function is still probing and return
immediantely before any uninitialized structures can be touched.

Link: https://lore.kernel.org/r/20200416084314.18851-9-skashyap@marvell.com
Signed-off-by: Chad Dupuis <cdupuis@marvell.com>
Signed-off-by: Saurav Kashyap <skashyap@marvell.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
drivers/scsi/qedf/qedf.h
drivers/scsi/qedf/qedf_main.c

index f3f399f..0da4e16 100644 (file)
@@ -355,6 +355,7 @@ struct qedf_ctx {
 #define QEDF_GRCDUMP_CAPTURE           4
 #define QEDF_IN_RECOVERY               5
 #define QEDF_DBG_STOP_IO               6
+#define QEDF_PROBING                   8
        unsigned long flags; /* Miscellaneous state flags */
        int fipvlan_retries;
        u8 num_queues;
index 59ca98f..3d0e345 100644 (file)
@@ -3153,7 +3153,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
 {
        int rc = -EINVAL;
        struct fc_lport *lport;
-       struct qedf_ctx *qedf;
+       struct qedf_ctx *qedf = NULL;
        struct Scsi_Host *host;
        bool is_vf = false;
        struct qed_ll2_params params;
@@ -3183,6 +3183,7 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
 
                /* Initialize qedf_ctx */
                qedf = lport_priv(lport);
+               set_bit(QEDF_PROBING, &qedf->flags);
                qedf->lport = lport;
                qedf->ctlr.lp = lport;
                qedf->pdev = pdev;
@@ -3206,9 +3207,12 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
        } else {
                /* Init pointers during recovery */
                qedf = pci_get_drvdata(pdev);
+               set_bit(QEDF_PROBING, &qedf->flags);
                lport = qedf->lport;
        }
 
+       QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe started.\n");
+
        host = lport->host;
 
        /* Allocate mempool for qedf_io_work structs */
@@ -3513,6 +3517,10 @@ static int __qedf_probe(struct pci_dev *pdev, int mode)
        else
                fc_fabric_login(lport);
 
+       QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");
+
+       clear_bit(QEDF_PROBING, &qedf->flags);
+
        /* All good */
        return 0;
 
@@ -3538,6 +3546,11 @@ err2:
 err1:
        scsi_host_put(lport->host);
 err0:
+       if (qedf) {
+               QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");
+
+               clear_bit(QEDF_PROBING, &qedf->flags);
+       }
        return rc;
 }
 
@@ -3687,11 +3700,25 @@ void qedf_get_protocol_tlv_data(void *dev, void *data)
 {
        struct qedf_ctx *qedf = dev;
        struct qed_mfw_tlv_fcoe *fcoe = data;
-       struct fc_lport *lport = qedf->lport;
-       struct Scsi_Host *host = lport->host;
-       struct fc_host_attrs *fc_host = shost_to_fc_host(host);
+       struct fc_lport *lport;
+       struct Scsi_Host *host;
+       struct fc_host_attrs *fc_host;
        struct fc_host_statistics *hst;
 
+       if (!qedf) {
+               QEDF_ERR(NULL, "qedf is null.\n");
+               return;
+       }
+
+       if (test_bit(QEDF_PROBING, &qedf->flags)) {
+               QEDF_ERR(&qedf->dbg_ctx, "Function is still probing.\n");
+               return;
+       }
+
+       lport = qedf->lport;
+       host = lport->host;
+       fc_host = shost_to_fc_host(host);
+
        /* Force a refresh of the fc_host stats including offload stats */
        hst = qedf_fc_get_host_stats(host);