From: Pavel Begunkov asml.silence@gmail.com
[ Upstream commit d7ae46b454eb05e3df0d46c2ac9c61416a4d9057 ]
Add a warning if io_populate_area_dma() can't fill in all net_iovs, it should never happen.
Signed-off-by: Pavel Begunkov asml.silence@gmail.com Signed-off-by: Jens Axboe axboe@kernel.dk Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES
- What it changes - Adds a post-loop invariant check in `io_populate_area_dma()` to ensure every `net_iov` in the area got a valid DMA address. If not, it emits a one-time warning and fails the mapping with `-EFAULT`: - New logic: “if not all niovs filled → WARN_ON_ONCE + return -EFAULT” - Before: the function always returned 0 even if it didn’t populate all niovs. - This is a small, localized change to `io_uring/zcrx.c` that does not alter APIs or structures and only affects the zcrx receive path.
- Why it matters (bug/risk being fixed) - Today, `io_populate_area_dma()` returns success unconditionally after walking the SG table, even if fewer DMA addresses were written than `area->nia.num_niovs`. See unconditional return in `io_uring/zcrx.c:78`. - On success, `io_zcrx_map_area()` marks the area as mapped (sets `area->is_mapped = true`), which enables the page_pool memory provider to start using these entries, assuming per-`net_iov` DMA addresses are valid: - `io_uring/zcrx.c:277` and `io_uring/zcrx.c:290-293` - DMA addresses are later consumed in sync paths (e.g., `io_zcrx_sync_for_device()`), which fetches them via `page_pool_get_dma_addr_netmem()`: `io_uring/zcrx.c:304-306`. - If some `net_iov`s remained uninitialized (DMA address 0 or stale), the NIC could be programmed with an invalid DMA address. That is a correctness and potential security issue (device DMA to the wrong address). The new check prevents entering that state by failing early instead of silently accepting a partially-initialized mapping.
- Context and call sites - `io_populate_area_dma()` is used by both umem and dmabuf mappings: - dmabuf mapping calls into `io_populate_area_dma()` at `io_uring/zcrx.c:159-161`. Upstream already validates the SG total size matches the area length (`io_uring/zcrx.c:139-146`), but the new invariant check still hardens against any mismatch in the subsequent per-page DMA population loop. - umem mapping path invokes it after `dma_map_sgtable()`: `io_uring/zcrx.c:270-275`. - On error, both paths propagate the error, and `io_zcrx_map_area()` will not set `area->is_mapped = true` (`io_uring/zcrx.c:290-293`), preventing the page_pool consumer from using invalid entries. This aligns with other existing error checks in the function (e.g., `net_mp_niov_set_dma_addr()` failure returns `-EFAULT` already at `io_uring/zcrx.c:71-72`).
- Stable backport criteria assessment - Fixes a real bug class (silent partial initialization of DMA addresses) that can lead to invalid device DMA targets. Even if the author says it “should never happen,” enforcing the invariant eliminates a dangerous failure mode rather than allowing undefined behavior. - Change is small and tightly scoped to zcrx; it adds one WARN_ON_ONCE and a conditional `-EFAULT` return. No interface, ABI, or architectural changes. - Callers already handle negative returns and will abort mapping cleanly, so regression risk is minimal and clearly bounded to failing early when an invariant is violated. - No new features; pure correctness/hardening. - Touches io_uring zcrx, not core scheduling or memory subsystems; impact is contained.
- Additional code references supporting the assessment - Unconditional success today: `io_uring/zcrx.c:52-79` (function body, unconditional `return 0` at `io_uring/zcrx.c:78`). - Caller sets `is_mapped` only on success: `io_uring/zcrx.c:277-293`. - DMA address is used later for device sync: `io_uring/zcrx.c:304-306`. - dmabuf SG size checked already: `io_uring/zcrx.c:139-146`.
Conclusion: This is a low-risk, correctness hardening that prevents a silent and potentially serious failure mode. It satisfies stable rules for a minimal, contained bugfix. Backport to stable trees that include `io_uring/zcrx` and `io_populate_area_dma()` is advisable.
io_uring/zcrx.c | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/io_uring/zcrx.c b/io_uring/zcrx.c index e5ff49f3425e0..39d1ef52a57b1 100644 --- a/io_uring/zcrx.c +++ b/io_uring/zcrx.c @@ -75,6 +75,9 @@ static int io_populate_area_dma(struct io_zcrx_ifq *ifq, niov_idx++; } } + + if (WARN_ON_ONCE(niov_idx != area->nia.num_niovs)) + return -EFAULT; return 0; }