Re: [PATCH bpf v2 6/8] bpf: user_ringbuf: Handle position wrap

From: Andrii Nakryiko

Date: Tue Jun 23 2026 - 16:15:44 EST


On Thu, Jun 18, 2026 at 5:27 PM Tamir Duberstein <tamird@xxxxxxxxxx> wrote:
>
> 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 <sashiko-bot@xxxxxxxxxx>
> Closes: https://lore.kernel.org/bpf/20260614020552.022A11F000E9@xxxxxxxxxxxxxxx/
> Assisted-by: Codex:gpt-5.5
> Signed-off-by: Tamir Duberstein <tamird@xxxxxxxxxx>
> ---
> 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;

hm, should we use long and then replace cons_pos >= prod_pos checks
with `cons_pos - prod_pos >= 0`, this will handle wrap correctly

>
> 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
>