summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Gunthorpe <jgg@nvidia.com>2026-06-01 13:52:32 -0300
committerJason Gunthorpe <jgg@nvidia.com>2026-06-05 12:36:33 -0300
commit09ea6837a0434fb4db99528a5055b6d822135dcf (patch)
tree138e9206d526912186d670b6f8e07296783e8346
parentbad4e98893afdfe0b1a03433d3af53972bafc67c (diff)
downloadlinux-stable-09ea6837a0434fb4db99528a5055b6d822135dcf.tar.gz
linux-stable-09ea6837a0434fb4db99528a5055b6d822135dcf.zip
RDMA/umem: Be careful about boundary conditions in ib_umem_find_best_pgsz()
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>
-rw-r--r--drivers/infiniband/core/umem.c18
-rw-r--r--include/rdma/ib_umem.h4
2 files changed, 14 insertions, 8 deletions
diff --git a/drivers/infiniband/core/umem.c b/drivers/infiniband/core/umem.c
index e424a9de66c1..fd8f3888da52 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -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;
diff --git a/include/rdma/ib_umem.h b/include/rdma/ib_umem.h
index bc1e6ed73b3f..4c8f433ba246 100644
--- a/include/rdma/ib_umem.h
+++ b/include/rdma/ib_umem.h
@@ -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;
}