[PATCH bpf v3 1/2] bpf: make dmabuf iterator stay drained after NULL

From: Kang Chen

Date: Fri Nov 13 2026 - 02:15:09 EST


bpf_iter_dmabuf_next() uses kit->dmabuf == NULL to mean "iteration has
not started". dma_buf_iter_next() also returns NULL at the end of the
list, so a next() call after exhaustion restarts from the list head.

The verifier relies on the iterator contract that once next() returns
NULL it keeps returning NULL: process_iter_next_call() marks the
iterator DRAINED and treats the result of later next() calls as the
constant zero. A restarted dmabuf iteration therefore hands the BPF
program a kernel pointer while the verifier believes R0 == 0.

NULL now means drained. Initialize the state to DMABUF_ITER_START and
translate it to dma_buf_iter_begin() on the first next(). The iterator
state is BPF-visible ABI (one pointer wide), so use an invalid pointer
sentinel instead of growing it.

Fixes: 6eab7ac7c5ee ("bpf: Add open coded dmabuf iterator")
Signed-off-by: Kang Chen <k.chen@xxxxxxxxxxxxxxxx>
---
v3:
- No change from v2; reposted with the updated 2/2 attribution.

kernel/bpf/dmabuf_iter.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/kernel/bpf/dmabuf_iter.c b/kernel/bpf/dmabuf_iter.c
index cd500248abd9..390f6d730a67 100644
--- a/kernel/bpf/dmabuf_iter.c
+++ b/kernel/bpf/dmabuf_iter.c
@@ -156,6 +156,9 @@ struct bpf_iter_dmabuf_kern {
struct dma_buf *dmabuf;
} __aligned(8);

+/* NULL means drained; START means "not started yet". */
+#define DMABUF_ITER_START ((struct dma_buf *)1L)
+
__bpf_kfunc_start_defs();

__bpf_kfunc int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it)
@@ -165,7 +168,7 @@ __bpf_kfunc int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it)
BUILD_BUG_ON(sizeof(*kit) > sizeof(*it));
BUILD_BUG_ON(__alignof__(*kit) != __alignof__(*it));

- kit->dmabuf = NULL;
+ kit->dmabuf = DMABUF_ITER_START;
return 0;
}

@@ -173,10 +176,14 @@ __bpf_kfunc struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it)
{
struct bpf_iter_dmabuf_kern *kit = (void *)it;

- if (kit->dmabuf)
- kit->dmabuf = dma_buf_iter_next(kit->dmabuf);
- else
+ /* Once NULL is returned, next() has to keep returning NULL. */
+ if (!kit->dmabuf)
+ return NULL;
+
+ if (kit->dmabuf == DMABUF_ITER_START)
kit->dmabuf = dma_buf_iter_begin();
+ else
+ kit->dmabuf = dma_buf_iter_next(kit->dmabuf);

return kit->dmabuf;
}
@@ -185,7 +192,7 @@ __bpf_kfunc void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it)
{
struct bpf_iter_dmabuf_kern *kit = (void *)it;

- if (kit->dmabuf)
+ if (kit->dmabuf && kit->dmabuf != DMABUF_ITER_START)
dma_buf_put(kit->dmabuf);
}

--
2.34.1