diff options
| author | Jason Gunthorpe <jgg@nvidia.com> | 2026-06-01 13:52:32 -0300 |
|---|---|---|
| committer | Jason Gunthorpe <jgg@nvidia.com> | 2026-06-05 12:36:33 -0300 |
| commit | 09ea6837a0434fb4db99528a5055b6d822135dcf (patch) | |
| tree | 138e9206d526912186d670b6f8e07296783e8346 | |
| parent | bad4e98893afdfe0b1a03433d3af53972bafc67c (diff) | |
| download | linux-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.c | 18 | ||||
| -rw-r--r-- | include/rdma/ib_umem.h | 4 |
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; } |
