From: Daniel Gomez <[email protected]>

Once dma_blk_cb() chunks at IOV_MAX, a later patch drops the current
mdts 2 MiB cap, allowing mdts (and zasl, which may be as large as mdts)
to essentially be 32 or more. Ensure shifting doesn't go out of range
by adding a static inline helper that clamps to UINT64_MAX when mdts or
zasl shift operations exceed uint64_t width.

Fixes error with ubsan enabled and large mdts:
../hw/nvme/ctrl.c:1685:36: runtime error: shift exponent 32 is too large
for 32-bit type 'unsigned int'

Signed-off-by: Daniel Gomez <[email protected]>
---
 hw/nvme/ctrl.c | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c
index 4893cf7e741..a24a674a4c5 100644
--- a/hw/nvme/ctrl.c
+++ b/hw/nvme/ctrl.c
@@ -1713,11 +1713,18 @@ static void nvme_clear_events(NvmeCtrl *n, uint8_t 
event_type)
     }
 }
 
+static inline uint64_t nvme_max_data_transfer_size(NvmeCtrl *n, uint8_t exp)
+{
+    unsigned shift = n->page_bits + exp;
+
+    return shift >= 64 ? UINT64_MAX : 1ULL << shift;
+}
+
 static inline uint16_t nvme_check_mdts(NvmeCtrl *n, size_t len)
 {
     uint8_t mdts = n->params.mdts;
 
-    if (mdts && len > n->page_size << mdts) {
+    if (mdts && len > nvme_max_data_transfer_size(n, mdts)) {
         trace_pci_nvme_err_mdts(len);
         return NVME_INVALID_FIELD | NVME_DNR;
     }
@@ -3809,7 +3816,7 @@ static uint16_t nvme_do_write(NvmeCtrl *n, NvmeRequest 
*req, bool append,
             }
 
             if (n->params.zasl &&
-                data_size > (uint64_t)n->page_size << n->params.zasl) {
+                data_size > nvme_max_data_transfer_size(n, n->params.zasl)) {
                 trace_pci_nvme_err_zasl(data_size);
                 return NVME_INVALID_FIELD | NVME_DNR;
             }

-- 
2.55.0


Reply via email to