scsi: lpfc: Fix bad memory access during VPD DUMP mailbox command
authorJames Smart <jsmart2021@gmail.com>
Wed, 21 Apr 2021 23:45:11 +0000 (16:45 -0700)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 27 Apr 2021 02:58:38 +0000 (22:58 -0400)
The dump command for reading a region passes a requested read length
specified in words (4-byte units). The response overwrites the same field
with the actual number of bytes read.

The mailbox handler for DUMP which reads VPD data (region 23) is treating
the response field as if it were still a word_cnt, thus multiplying it by 4
to set the read's "length". Given the read value was calculated based on
the size of the read buffer, the longer response length runs off the end of
the buffer.

Fix by reworking the code to use the response field as a byte count.

Link: https://lore.kernel.org/r/20210421234511.102206-1-jsmart2021@gmail.com
Co-developed-by: Justin Tee <justin.tee@broadcom.com>
Signed-off-by: Justin Tee <justin.tee@broadcom.com>
Signed-off-by: James Smart <jsmart2021@gmail.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_sli.c

index 1e4c792bb660e45458d249aeac39a143fffb4cf9..5f018d02bf56206ebdfbba8460e9cb5227fd9ca6 100644 (file)
@@ -254,13 +254,13 @@ lpfc_config_port_prep(struct lpfc_hba *phba)
                if (mb->un.varDmp.word_cnt == 0)
                        break;
 
-               i =  mb->un.varDmp.word_cnt * sizeof(uint32_t);
-               if (offset + i >  DMP_VPD_SIZE)
-                       i =  DMP_VPD_SIZE - offset;
+               if (mb->un.varDmp.word_cnt > DMP_VPD_SIZE - offset)
+                       mb->un.varDmp.word_cnt = DMP_VPD_SIZE - offset;
                lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
-                                     lpfc_vpd_data  + offset, i);
-               offset += i;
-       } while (offset < DMP_VPD_SIZE);
+                                     lpfc_vpd_data + offset,
+                                     mb->un.varDmp.word_cnt);
+               offset += mb->un.varDmp.word_cnt;
+       } while (mb->un.varDmp.word_cnt && offset < DMP_VPD_SIZE);
 
        lpfc_parse_vpd(phba, lpfc_vpd_data, offset);
 
index 579ac75dfe79fd7f9045714b950d438128b272d5..573c8599d71c2d3e155fdb64b268ef3296a0aacd 100644 (file)
@@ -19777,7 +19777,7 @@ lpfc_sli_get_config_region23(struct lpfc_hba *phba, char *rgn23_data)
        LPFC_MBOXQ_t *pmb = NULL;
        MAILBOX_t *mb;
        uint32_t offset = 0;
-       int i, rc;
+       int rc;
 
        if (!rgn23_data)
                return 0;
@@ -19808,13 +19808,14 @@ lpfc_sli_get_config_region23(struct lpfc_hba *phba, char *rgn23_data)
                if (mb->un.varDmp.word_cnt == 0)
                        break;
 
-               i =  mb->un.varDmp.word_cnt * sizeof(uint32_t);
-               if (offset + i >  DMP_RGN23_SIZE)
-                       i =  DMP_RGN23_SIZE - offset;
+               if (mb->un.varDmp.word_cnt > DMP_RGN23_SIZE - offset)
+                       mb->un.varDmp.word_cnt = DMP_RGN23_SIZE - offset;
+
                lpfc_sli_pcimem_bcopy(((uint8_t *)mb) + DMP_RSP_OFFSET,
-                                     rgn23_data  + offset, i);
-               offset += i;
-       } while (offset < DMP_RGN23_SIZE);
+                                      rgn23_data + offset,
+                                      mb->un.varDmp.word_cnt);
+               offset += mb->un.varDmp.word_cnt;
+       } while (mb->un.varDmp.word_cnt && offset < DMP_RGN23_SIZE);
 
        mempool_free(pmb, phba->mbox_mem_pool);
        return offset;