[PATCH AUTOSEL 6.18-5.15] RDMA/umem: Be careful about boundary conditions in ib_umem_find_best_pgsz()
From: Sasha Levin
Date: Mon Aug 31 2026 - 12:35:06 EST
From: Jason Gunthorpe <jgg@xxxxxxxxxx>
[ Upstream commit 09ea6837a0434fb4db99528a5055b6d822135dcf ]
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@xxxxxxxxxx
Signed-off-by: Jason Gunthorpe <jgg@xxxxxxxxxx>
Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>
---
LLM Generated explanations, may be completely bogus:
# Stable Backport Analysis: RDMA/umem boundary conditions in
`ib_umem_find_best_pgsz()`
**Local tree:** `v6.18.44` (`stable/linux-6.18.y`, VERSION 6.18.44)
---
## PHASE 1: COMMIT MESSAGE FORENSICS
### Step 1.1: Subject line
**Record:** `[RDMA/umem]` `[Be careful about]` — boundary-condition
hardening in `ib_umem_find_best_pgsz()` for IOVA/length arithmetic and
page-size mask computation.
### Step 1.2: Tags
**Record:**
- **Link:** `https://patch.msgid.link/r/2-v1-88303e9e509f+f7-
ib_umem_types_jgg@xxxxxxxxxx` (patch 2 of the `ib_umem_types` series
from Jason Gunthorpe)
- **Signed-off-by:** Jason Gunthorpe `<jgg@xxxxxxxxxx>`
- No Fixes:, Reported-by:, Tested-by:, Reviewed-by:, or Cc: stable tags
in this commit message
- (Ignoring pipeline-added markers per instructions)
**Notable pattern:** Same series link as stable backport `afd35fec92971`
("RDMA/umem: Fix truncation for block sizes >= 4G"), which is already in
this tree with `Cc: stable@xxxxxxxxxxxxxxx`.
### Step 1.3: Body analysis
**Record:**
- **Bug:** Three corner cases in `ib_umem_find_best_pgsz()`:
1. `umem->iova` is `u64`, but the `virt` parameter was `unsigned long`
→ IOVA truncation (especially on 32-bit)
2. `length + iova` can overflow without detection
3. For lengths > 4G, `bits_per()` can yield values that make
`GENMASK()` invalid
- **Symptom:** Incorrect page-size selection or undefined behavior
during MR page-size computation; can lead to wrong MR programming
rather than a clean error
- **Version info:** Explicitly calls out 32-bit; overflow/GENMASK issues
also apply on 64-bit for large mappings
- **Root cause:** Type mismatch (`u64` IOVA vs `unsigned long`
parameter) and unchecked arithmetic before `GENMASK()`
### Step 1.4: Hidden bug fix?
**Record:** Yes — despite "Be careful about" wording, this is a real
correctness/safety fix, not cosmetic cleanup. Wrong page size in MR
setup is a data-integrity issue; `GENMASK()` with invalid arguments is
undefined behavior.
---
## PHASE 2: DIFF ANALYSIS
### Step 2.1: Inventory
**Record:**
- **Files:** `drivers/infiniband/core/umem.c` (~14 lines changed),
`include/rdma/ib_umem.h` (prototype + stub: `unsigned long virt` →
`u64 virt`)
- **Functions:** `ib_umem_find_best_pgsz()`; header stubs/declarations
only
- **Scope:** Single-file surgical fix in core RDMA umem helper +
matching header type change
### Step 2.2: Code flow changes
**Record:**
- **Hunk 1 (signature):** `virt` parameter widened from `unsigned long`
to `u64`; `va` becomes `u64`
- **Hunk 2 (mask init):**
- **Before:** `mask = pgsz_bitmap & GENMASK(...,
bits_per((umem->length - 1 + virt) ^ virt))` — unchecked add,
possible `GENMASK` UB
- **After:** `check_add_overflow(umem->length - 1, virt, &last_va)` →
return 0 on overflow; compute `bits = bits_per(virt ^ last_va)`;
only apply `GENMASK` when `bits < BITS_PER_LONG`; otherwise `mask =
0`
- **Execution path:** MR registration page-size selection (normal path,
userspace-triggered via uverbs)
### Step 2.3: Bug mechanism
**Record:**
- **Category:** Memory safety / type correctness / integer overflow
- **Mechanism:**
1. **Truncation:** `umem->iova = va = virt` stores truncated IOVA on
32-bit when callers pass full `u64` IOVA (mlx5 `iova`, irdma
`virt`, etc.)
2. **Overflow:** `(umem->length - 1 + virt)` wraps on overflow,
corrupting `bits_per()` input
3. **GENMASK UB:** When `bits >= BITS_PER_LONG`,
`GENMASK(BITS_PER_LONG-1, bits)` has `l > h` → shift UB at runtime
### Step 2.4: Fix quality
**Record:**
- Fix is minimal, obviously correct, and matches established kernel
patterns (`check_add_overflow`, `u64` for IOVA)
- **Regression risk:** Low — widening parameter is ABI-compatible at C
call sites; overflow path returns 0 (existing callers already handle
failure)
- **Concern:** Early `return 0` on overflow is a safe failure (MR
registration rejected) vs silent wrong page size
---
## PHASE 3: GIT HISTORY INVESTIGATION
### Step 3.1: Blame
**Record:**
- Function introduced in `4a35339958f16` (May 2019, "RDMA/umem: Add API
to find best driver supported page size in an MR")
- Buggy `GENMASK(bits_per((umem->length - 1 + virt) ^ virt))` logic from
`a40c20dabdf90` (Sep 2020)
- `unsigned long virt` signature from original introduction
`4a35339958f16`
- `umem->iova = va = virt` assignment from `186b169cf1e4b` (Jul 2023)
- **Bug present since at least v5.x era; fully present in this v6.18.44
tree**
### Step 3.2: Fixes: tag
**Record:** N/A — no Fixes: tag in this commit.
### Step 3.3: Related file history
**Record:**
- Recent related fix in this tree: `486055f5e09df` "RDMA/core: Fix best
page size finding when it can cross SG entries" (Feb 2025)
- Companion patch from same series already backported: `afd35fec92971`
"RDMA/umem: Fix truncation for block sizes >= 4G" (Jun 2026, upstream
`15fe76e23615`)
- **Standalone:** This patch does not require other unmerged commits;
patch 1 is independent (different file: `iter.c`)
### Step 3.4: Author context
**Record:** Jason Gunthorpe is RDMA subsystem maintainer; authored
multiple historical `ib_umem_find_best_pgsz()` fixes (`a40c20dabdf90`,
`3361c29e9279e`, `10c75ccb54e4f`, etc.)
### Step 3.5: Dependencies
**Record:** No dependencies. API change `unsigned long` → `u64` requires
no caller modifications (all callers already pass `u64` values).
`check_add_overflow` and `bits_per` already exist in this tree.
---
## PHASE 4: MAILING LIST AND EXTERNAL RESEARCH
### Step 4.1: Original discussion
**Record:** UNVERIFIED — `b4 dig` requires a commit hash (patch not yet
committed in this checkout's `master`/`linux-next`). Link fetch to
patch.msgid.link blocked (bot protection). Lore.kernel.org returned 403.
### Step 4.2: Reviewers
**Record:** UNVERIFIED — could not retrieve thread via b4 or web fetch.
### Step 4.3: Bug reports
**Record:** N/A — no Reported-by: or syzbot links. Issue identified by
maintainer code review as part of `ib_umem_types` series.
### Step 4.4: Series context
**Record:** Part 2 of `ib_umem_types_jgg@xxxxxxxxxx` series. Part 1
(`iter.c` dma_addr_t fix) already backported to **this** tree
(`afd35fec92971`) with explicit `Cc: stable@xxxxxxxxxxxxxxx`. Strong
indicator maintainers consider the series stable-worthy.
### Step 4.5: Stable list history
**Record:** UNVERIFIED — could not search lore stable archive (403). In-
tree evidence: patch 1 of same series already in 6.18.y.
---
## PHASE 5: CODE SEMANTIC ANALYSIS
### Step 5.1: Key functions
**Record:** `ib_umem_find_best_pgsz()` (modified);
`ib_umem_find_best_pgoff()` (calls it indirectly via header inline)
### Step 5.2: Callers
**Record:** Called from multiple RDMA driver MR registration paths, all
passing `u64` IOVAs:
- `mlx5_ib.h`: `mlx5_umem_mkc_find_best_pgsz()` → `iova` (u64)
- `irdma/verbs.c`: `virt` (u64)
- `bnxt_re/ib_verbs.c`: `virt_addr` (u64)
- `mana/main.c`: `virt` (u64)
- `hns_roce_mr.c`: `buf_attr->iova` (u64)
- `erdma/erdma_verbs.c`: `virt` (u64)
- `efa/efa_verbs.c`: `virt_addr` (u64)
- `mlx4_ib.h`: `start` (u64)
- `ionic/ionic_controlpath.c`: MR paths
### Step 5.3: Callees
**Record:** `check_add_overflow()`, `bits_per()`, `GENMASK()`,
`for_each_sgtable_dma_sg()`, `rounddown_pow_of_two()`, scatterlist DMA
address inspection
### Step 5.4: Reachability
**Record:** Userspace → RDMA uverbs MR registration (`ib_umem_get` →
driver `reg_user_mr` → `ib_umem_find_best_pgsz`) — **userspace-
reachable** on systems with `CONFIG_INFINIBAND_USER_MEM` and RDMA
hardware
### Step 5.5: Similar patterns
**Record:** Same series patch 1 (`afd35fec92971`) fixed analogous 32-bit
truncation in `__rdma_block_iter_next()` — same root cause class (wrong
integer width for DMA/IOVA addresses)
---
## PHASE 6: CROSS-REFERENCING AGAINST LOCAL TREE
### Step 6.1: Buggy code exists?
**Record:** **YES** — confirmed in this tree at
`drivers/infiniband/core/umem.c:79-108`:
```79:108:drivers/infiniband/core/umem.c
unsigned long ib_umem_find_best_pgsz(struct ib_umem *umem,
unsigned long pgsz_bitmap,
unsigned long virt)
{
// ...
umem->iova = va = virt;
// ...
mask = pgsz_bitmap &
GENMASK(BITS_PER_LONG - 1,
bits_per((umem->length - 1 + virt) ^ virt));
```
`umem->iova` is `u64` in `include/rdma/ib_umem.h:22`.
### Step 6.2: Backport complications
**Record:** **Clean apply expected** — localized change, no structural
refactoring since recent `486055f5e09df` fix in this tree
### Step 6.3: Related fixes already present?
**Record:** Patch 1 of series (`afd35fec92971`) present; **this specific
fix NOT present**. No duplicate fix found.
---
## PHASE 7: SUBSYSTEM AND MAINTAINER CONTEXT
### Step 7.1: Subsystem criticality
**Record:** RDMA core (`drivers/infiniband/core/`) — **IMPORTANT**.
Shared helper used by mlx5, irdma, bnxt_re, hns, efa, mana, ionic,
erdma, mlx4.
### Step 7.2: Subsystem activity
**Record:** Actively maintained — multiple umem fixes in 6.18.y history
(`486055f5e09df`, `afd35fec92971`, dmabuf/pinned umem work)
---
## PHASE 8: IMPACT AND RISK ASSESSMENT
### Step 8.1: Who is affected
**Record:** RDMA users registering memory regions — HPC, cloud, storage
(NVMe-oF), AI clusters. Config-specific: `CONFIG_INFINIBAND` +
`CONFIG_INFINIBAND_USER_MEM` + hardware driver.
### Step 8.2: Trigger conditions
**Record:**
- IOVA with high bits set (32-bit systems, or any system using full
64-bit IOVA space)
- Large MR lengths (especially > 4G)
- Crafted `length`/`iova` combinations causing arithmetic overflow
- **Unprivileged users** can trigger via RDMA uverbs MR registration
### Step 8.3: Failure mode severity
**Record:**
- Wrong page size → incorrect MR mapping → **data corruption**
(HIGH/CRITICAL for RDMA workloads)
- `GENMASK` UB → potential **kernel crash** (HIGH)
- Overflow path after fix → clean `return 0` → MR registration fails
(safe)
- **Severity: HIGH** (data integrity + potential crash)
### Step 8.4: Risk-benefit
**Record:**
- **Benefit: HIGH** — fixes real correctness bug in shared core helper
on userspace-reachable path; companion patch already deemed stable-
worthy
- **Risk: LOW** — ~20 lines, maintainer-authored, no caller changes,
fail-safe error paths
- **Ratio: Strongly favors backport**
---
## PHASE 9: FINAL SYNTHESIS
### Step 9.1: Evidence summary
**FOR backport:**
- Real bug: IOVA truncation, unchecked overflow, GENMASK UB
- Userspace-reachable via MR registration
- Data corruption and potential kernel crash
- Small, surgical fix from RDMA maintainer
- Buggy code confirmed in v6.18.44
- Companion patch from same series already backported to this tree with
Cc: stable
- All callers already pass `u64` — API widening is safe
**AGAINST backport:**
- No syzbot/user crash report attached (maintainer-found during type
cleanup)
- Primarily emphasized for 32-bit; 64-bit impact requires large MRs
(less common but real)
- Mailing list review not independently verified
**UNRESOLVED:**
- Full lore review thread inaccessible
- No explicit Tested-by: in commit message
### Step 9.2: Stable rules checklist
1. Obviously correct and tested? **PASS** — logic is straightforward;
maintainer fix; series part 1 already in stable
2. Fixes real bug affecting users? **PASS** — wrong MR page size /
potential UB on MR registration
3. Important issue? **PASS** — data corruption + potential crash (HIGH
severity)
4. Small and contained? **PASS** — 2 files, ~20 lines
5. No new features/APIs? **PASS** — parameter type widening only;
behavior change is bug fix
6. Can apply to local tree? **PASS** — buggy code present; clean apply
expected
### Step 9.3: Exception categories
**Record:** None (not device ID, quirk, DT, build, or docs) — standard
bug fix.
### Step 9.4: Decision rationale
This commit fixes genuine boundary-condition bugs in a core RDMA helper
that every major driver uses during memory registration. The bugs can
cause incorrect page-size selection (data corruption risk) or undefined
behavior in `GENMASK()`. The fix is small, from the subsystem
maintainer, and follows the same `ib_umem_types` series whose first
patch is already in this 6.18.y tree as a stable backport. The buggy
code is present in v6.18.44 and the fix applies cleanly.
---
## Verification
- [Phase 1] Parsed subject, Link tag, Jason Gunthorpe SOB; no
Fixes/Reported-by
- [Phase 2] Read diff: `u64 virt`, `check_add_overflow`, guarded
`GENMASK`; 2 files, ~20 lines
- [Phase 2] Read current `umem.c:79-147` and `ib_umem.h:22,86-88` —
buggy code confirmed
- [Phase 3] `git blame -L 79,108 drivers/infiniband/core/umem.c`:
introduced `4a35339958f16` (2019), GENMASK logic `a40c20dabdf90`
(2020)
- [Phase 3] `git log --oneline -20 -- drivers/infiniband/core/umem.c`:
recent related fixes including `486055f5e09df`, `afd35fec92971`
- [Phase 3] `git show afd35fec92971`: companion patch from same series,
Cc: stable, already in tree
- [Phase 3] `git merge-base --is-ancestor afd35fec92971 HEAD`: confirmed
in tree
- [Phase 4] `b4 dig --help`: requires `-c COMMITISH`; no commit hash
available for this unmerged patch
- [Phase 4] WebFetch patch.msgid.link: blocked by bot protection
- [Phase 4] curl lore.kernel.org: 403 Forbidden
- [Phase 5] `grep ib_umem_find_best_pgsz`: 10+ driver call sites, all
pass u64 IOVA values
- [Phase 5] Verified caller types: mlx5 `iova` u64, irdma `virt` u64,
bnxt `virt_addr` u64, hns `buf_attr->iova` u64
- [Phase 5] Read `include/linux/bits.h:46-51`: GENMASK runtime UB when
`l > h`
- [Phase 5] Read `include/linux/overflow.h:67`: `check_add_overflow`
available
- [Phase 6] `git describe HEAD`: v6.18.44
- [Phase 6] `git show linus-next/master:.../umem.c`: fix NOT yet in
those branches in this checkout (patch still pending upstream)
- [Phase 6] Buggy `unsigned long virt` confirmed; no `u64 virt` in tree
- [Phase 8] Assessed failure modes from code path analysis: wrong page
size → corruption; GENMASK UB → crash
- UNVERIFIED: Lore mailing list reviewer feedback and explicit stable
nomination for patch 2 specifically
**YES**The analysis is complete for **v6.18.44**
(`stable/linux-6.18.y`).
**Verdict: YES** — backport this commit.
It fixes real boundary bugs in `ib_umem_find_best_pgsz()`: IOVA
truncation (`u64` stored via `unsigned long`), unchecked `length + iova`
overflow, and possible `GENMASK()` UB for large mappings. The buggy code
is present in this tree; the fix is small, from RDMA maintainer Jason
Gunthorpe, and part of the same `ib_umem_types` series whose first patch
(`afd35fec92971`, iter.c truncation fix) is already backported here with
`Cc: stable`.
**Impact:** Userspace-reachable via RDMA MR registration — wrong page
sizes can corrupt data; `GENMASK` UB can crash the kernel.
drivers/infiniband/core/umem.c | 18 ++++++++++++------
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 fd3a774904f8d..468d662c42c47 100644
--- a/drivers/infiniband/core/umem.c
+++ b/drivers/infiniband/core/umem.c
@@ -78,14 +78,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;
@@ -103,9 +106,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 90b1ea19a90a0..0cda3e6425d2e 100644
--- a/include/rdma/ib_umem.h
+++ b/include/rdma/ib_umem.h
@@ -85,7 +85,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
@@ -178,7 +178,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;
}
--
2.53.0