]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
RDMA/umem: Be careful about boundary conditions in ib_umem_find_best_pgsz()
authorJason Gunthorpe <jgg@nvidia.com>
Mon, 1 Jun 2026 16:52:32 +0000 (13:52 -0300)
committerJason Gunthorpe <jgg@nvidia.com>
Fri, 5 Jun 2026 15:36:33 +0000 (12:36 -0300)
Several corner cases, especially important on 32 bits:

- umem->iova is u64, the function argument should pass in u64 or
  iova will be truncated
- Check that the length is not too large for the iova
- Check that lengths > 4G don't overflow the GENMASK

Link: https://patch.msgid.link/r/2-v1-88303e9e509f+f7-ib_umem_types_jgg@nvidia.com
Signed-off-by: Jason Gunthorpe <jgg@nvidia.com>
drivers/infiniband/core/umem.c
include/rdma/ib_umem.h

index e424a9de66c177e954b4af26d0e654022878a446..fd8f3888da5227f1d43cdc80a10434e7dc87e55a 100644 (file)
@@ -84,14 +84,17 @@ static void __ib_umem_release(struct ib_device *dev, struct ib_umem *umem, int d
  */
 unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem,
                                     unsigned long pgsz_bitmap,
-                                    unsigned long virt)
+                                    u64 virt)
 {
        unsigned long curr_len = 0;
        dma_addr_t curr_base = ~0;
-       unsigned long va, pgoff;
+       unsigned long pgoff;
        struct scatterlist *sg;
-       dma_addr_t mask;
+       unsigned long mask = 0;
+       unsigned int bits;
        dma_addr_t end;
+       u64 last_va;
+       u64 va;
        int i;
 
        umem->iova = va = virt;
@@ -109,9 +112,12 @@ unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem,
         * number of required pages. Compute the largest page size that could
         * work based on VA address bits that don't change.
         */
-       mask = pgsz_bitmap &
-              GENMASK(BITS_PER_LONG - 1,
-                      bits_per((umem->length - 1 + virt) ^ virt));
+       if (check_add_overflow(umem->length - 1, virt, &last_va))
+               return 0;
+       bits = bits_per(virt ^ last_va);
+       if (bits < BITS_PER_LONG)
+               mask = pgsz_bitmap & GENMASK(BITS_PER_LONG - 1, bits);
+
        /* offset into first SGL */
        pgoff = umem->address & ~PAGE_MASK;
 
index bc1e6ed73b3f49a39e5aeac63c7384038298f12c..4c8f433ba246f3ac07718c29513c85aa074ebd1c 100644 (file)
@@ -109,7 +109,7 @@ int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offset,
                      size_t length);
 unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem,
                                     unsigned long pgsz_bitmap,
-                                    unsigned long virt);
+                                    u64 virt);
 
 /**
  * ib_umem_find_best_pgoff - Find best HW page size
@@ -234,7 +234,7 @@ static inline int ib_umem_copy_from(void *dst, struct ib_umem *umem, size_t offs
 }
 static inline unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem,
                                                   unsigned long pgsz_bitmap,
-                                                  unsigned long virt)
+                                                  u64 virt)
 {
        return 0;
 }