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 <[email protected]>
Closes: https://lore.kernel.org/bpf/[email protected]/
Assisted-by: Codex:gpt-5.5
Signed-off-by: Tamir Duberstein <[email protected]>
---
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