User ring buffer positions are unsigned long counters, but __bpf_user_ringbuf_peek() widens them to u64 before comparing and subtracting them. On 32-bit systems, producer_pos wrapping below consumer_pos therefore appears to move backwards and permanently stalls the ring. The widened subtraction can also bypass the advertised-window check. Keep the positions word-sized and derive the available data with word-sized subtraction so the arithmetic wraps with the counters. Treat a zero span as empty and reject spans larger than the ring before reading a record header. Fixes: 205715673844 ("bpf: Add bpf_user_ringbuf_drain() helper") Reported-by: Sashiko Closes: https://lore.kernel.org/bpf/20260614020552.022A11F000E9@smtp.kernel.org/ Assisted-by: Codex:gpt-5.5 Signed-off-by: Tamir Duberstein --- kernel/bpf/ringbuf.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/kernel/bpf/ringbuf.c b/kernel/bpf/ringbuf.c index 909880031fd3..19cbb9b74ee7 100644 --- a/kernel/bpf/ringbuf.c +++ b/kernel/bpf/ringbuf.c @@ -748,9 +748,9 @@ const struct bpf_func_proto bpf_ringbuf_discard_dynptr_proto = { static int __bpf_user_ringbuf_peek(struct bpf_ringbuf *rb, void **sample, u32 *size) { + unsigned long avail, cons_pos, prod_pos; int err; u32 hdr_len, sample_len, total_len, flags, *hdr; - u64 cons_pos, prod_pos; /* Synchronizes with smp_store_release() in user-space producer. */ prod_pos = smp_load_acquire(&rb->producer_pos); @@ -759,8 +759,11 @@ static int __bpf_user_ringbuf_peek(struct bpf_ringbuf *rb, void **sample, u32 *s /* Synchronizes with smp_store_release() in __bpf_user_ringbuf_sample_release() */ cons_pos = smp_load_acquire(&rb->consumer_pos); - if (cons_pos >= prod_pos) + avail = prod_pos - cons_pos; + if (!avail) return -ENODATA; + if (avail > ringbuf_total_data_sz(rb)) + return -EINVAL; hdr = (u32 *)((uintptr_t)rb->data + (uintptr_t)(cons_pos & rb->mask)); /* Synchronizes with smp_store_release() in user-space producer. */ @@ -770,7 +773,7 @@ static int __bpf_user_ringbuf_peek(struct bpf_ringbuf *rb, void **sample, u32 *s total_len = round_up(sample_len + BPF_RINGBUF_HDR_SZ, 8); /* The sample must fit within the region advertised by the producer position. */ - if (total_len > prod_pos - cons_pos) + if (total_len > avail) return -EINVAL; /* The sample must fit within the data region of the ring buffer. */ -- 2.55.0.rc0.159.gbe5d7338c2