mvumi_hs_process_page() takes max_io, the two command entry sizes and the list depth from the firmware handshake page. The entry sizes are u8 fields used as shift counts, and 1 is an int, so a count of 29 or more makes the << 2 applied to the shift overflow an int; a count of 32 or more is undefined outright. max_io is a u16, and the sizes derived from the shifts are multiplied by it in mvumi_init_data() to size three unsigned int allocations, so a large entry size wraps the product and the loops that follow then walk and write past the short allocation. The list depth is used as a shift count too, but only when the firmware sets HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF; a count of 32 or more is undefined there as well. The depth also sets how many entries the inbound and outbound rings hold, and the per-command slot index wraps on that count while the rings are allocated for max_io entries, so a depth yielding more entries than max_io -- or none -- walks the index past them. Reject an entry size above 29, which keeps 1U << n and the shift that follows it inside an unsigned int. Then compute the worst-case allocation size in u64 at the top of mvumi_init_data() and reject it if it does not fit an unsigned int. That total bounds all three allocations: the inbound list total, the outbound data pool and the tag arrays, whose sizes are the same products plus smaller terms. Reject a list depth of 32 or more on the shifted path so the shift stays defined, and reject a resulting entry count that is zero or above max_io so the slot index cannot walk past the rings. The shifts also use 1U now so that a legal count cannot overflow an int. No realistic controller is rejected: a controller reporting 4096 commands with 64 KiB entries and a depth of 12 needs 513 MiB and passes unchanged. Fixes: f0c568a478f0 ("[SCSI] mvumi: Add Marvell UMI driver") Cc: stable@vger.kernel.org Signed-off-by: Yehyeong Lee --- v2: - compute the allocation total in u64 so the check also holds on 32-bit (Sashiko). - reject a handshake list depth whose entry count exceeds max_io, which would walk the per-command ring past its allocation (Sashiko). drivers/scsi/mvumi.c | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c index e70d336b4ab39..e9ba072220331 100644 --- a/drivers/scsi/mvumi.c +++ b/drivers/scsi/mvumi.c @@ -912,10 +912,23 @@ static int mvumi_init_data(struct mvumi_hba *mhba) unsigned int tmp_size, offset, i; void *virmem, *v; dma_addr_t p; + u64 total; if (mhba->fw_flag & MVUMI_FW_ALLOC) return 0; + /* These are firmware-supplied and size the allocations below. */ + total = (u64)mhba->ib_max_size * mhba->max_io + + (u64)mhba->ob_max_size * mhba->max_io + + sizeof(struct mvumi_dyn_list_entry) * (u64)mhba->max_io + + 160; + if (total > UINT_MAX) { + dev_err(&mhba->pdev->dev, + "handshake sizes too large: io %u, in %u, out %u\n", + mhba->max_io, mhba->ib_max_size, mhba->ob_max_size); + return -1; + } + tmp_size = mhba->ib_max_size * mhba->max_io; if (mhba->hba_capability & HS_CAPABILITY_SUPPORT_DYN_SRC) tmp_size += sizeof(struct mvumi_dyn_list_entry) * mhba->max_io; @@ -1051,16 +1064,24 @@ static int mvumi_hs_process_page(struct mvumi_hba *mhba, case HS_PAGE_FIRM_CAP: hs_page1 = (struct mvumi_hs_page1 *) hs_header; + /* Firmware-supplied shift counts: (1U << n) << 2 must fit. */ + if (hs_page1->cl_in_max_entry_size > 29 || + hs_page1->cl_out_max_entry_size > 29) { + dev_err(&mhba->pdev->dev, + "invalid handshake entry size\n"); + return -1; + } + mhba->max_io = hs_page1->max_io_support; mhba->list_num_io = hs_page1->cl_inout_list_depth; mhba->max_transfer_size = hs_page1->max_transfer_size; mhba->max_target_id = hs_page1->max_devices_support; mhba->hba_capability = hs_page1->capability; mhba->ib_max_size_setting = hs_page1->cl_in_max_entry_size; - mhba->ib_max_size = (1 << hs_page1->cl_in_max_entry_size) << 2; + mhba->ib_max_size = (1U << hs_page1->cl_in_max_entry_size) << 2; mhba->ob_max_size_setting = hs_page1->cl_out_max_entry_size; - mhba->ob_max_size = (1 << hs_page1->cl_out_max_entry_size) << 2; + mhba->ob_max_size = (1U << hs_page1->cl_out_max_entry_size) << 2; dev_dbg(&mhba->pdev->dev, "FW version:%d\n", hs_page1->fw_ver.ver_build); @@ -1069,8 +1090,22 @@ static int mvumi_hs_process_page(struct mvumi_hba *mhba, mhba->eot_flag = 22; else mhba->eot_flag = 27; - if (mhba->hba_capability & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF) - mhba->list_num_io = 1 << hs_page1->cl_inout_list_depth; + if (mhba->hba_capability & HS_CAPABILITY_NEW_PAGE_IO_DEPTH_DEF) { + if (hs_page1->cl_inout_list_depth >= 32) { + dev_err(&mhba->pdev->dev, + "invalid handshake list depth\n"); + return -1; + } + mhba->list_num_io = 1U << hs_page1->cl_inout_list_depth; + } + /* The rings hold max_io entries but the index wraps on list_num_io. */ + if (mhba->list_num_io == 0 || + mhba->list_num_io > mhba->max_io) { + dev_err(&mhba->pdev->dev, + "invalid handshake list depth: %u entries for %u commands\n", + mhba->list_num_io, mhba->max_io); + return -1; + } break; default: dev_err(&mhba->pdev->dev, "handshake: page code error\n"); base-commit: 24a19b953e2a3d0ea5a8c4951a0ab8f32720c907 -- 2.43.0