From: Hasan Basbunar <basbunarhasan@gmail.com>
To: Daniel Borkmann <daniel@iogearbox.net>
Cc: Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
bpf@vger.kernel.org, linux-kernel@vger.kernel.org,
Hasan Basbunar <basbunarhasan@gmail.com>
Subject: [PATCH] bpf: bpf_dbg: fix off-by-one in cmd_select and pcap_next_pkt
Date: Tue, 28 Apr 2026 12:01:09 +0200 [thread overview]
Message-ID: <20260428100109.56572-1-basbunarhasan@gmail.com> (raw)
bpf_dbg's interactive 'select <N>' command, documented in the file
header ("select 3 (run etc will start from the 3rd packet in the pcap)")
to use 1-based packet indexing, advances the pcap cursor one packet too
many. The loop in cmd_select():
pcap_reset_pkt(); /* cursor on packet 1 */
for (i = 0; i < which && (have_next = pcap_next_pkt()); i++)
/* noop */;
calls pcap_next_pkt() N times to reach packet N, but pcap_next_pkt()
validates the packet at the cursor and then advances past it. After
N calls the cursor is on packet N+1, so 'select 3' positions on
packet 4, 'select 4' on packet 5, etc. To land on packet N the loop
must advance the cursor only N-1 times.
A second off-by-one in pcap_next_pkt() rejects the last packet of any
pcap whose mapped size equals the sum of its packets exactly (the
common case — pcap files have no trailer):
if (pcap_ptr_va_curr + sizeof(*hdr) + hdr->caplen -
pcap_ptr_va_start >= pcap_map_size)
return false;
When the current packet ends exactly at the mmap boundary, the
expression equals pcap_map_size and the >= check rejects a fully
in-bounds packet. The same off-by-one is present in the earlier
header-fits check on the same function. Both should compare with >.
Combined effect: 'select N' on a pcap of N packets always reports
"no packet #N available!". For a 1-packet pcap, 'select 1' reports
the only packet as unavailable.
Reproduction (deterministic, no kernel needed): build bpf_dbg from
the unmodified tree, synthesize a pcap with N>=1 packets each with a
distinct payload byte, and drive 'select K / step 1 / quit'. Before
this fix, 'select 1' shows packet 2's payload; 'select N' shows the
"no packet" error. After this fix, 'select K' shows packet K for
all K in 1..N, and 'select N+1' correctly errors.
Cloudflare's downstream mirror at github.com/cloudflare/bpftools
carries the same defect.
Fixes: fd981e3c321a ("filter: bpf_dbg: add minimal bpf debugger")
Signed-off-by: Hasan Basbunar <basbunarhasan@gmail.com>
---
tools/bpf/bpf_dbg.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/tools/bpf/bpf_dbg.c b/tools/bpf/bpf_dbg.c
index 00e560a17baf..f21576dc2326 100644
--- a/tools/bpf/bpf_dbg.c
+++ b/tools/bpf/bpf_dbg.c
@@ -923,12 +923,12 @@ static bool pcap_next_pkt(void)
struct pcap_pkthdr *hdr = pcap_curr_pkt();
if (pcap_ptr_va_curr + sizeof(*hdr) -
- pcap_ptr_va_start >= pcap_map_size)
+ pcap_ptr_va_start > pcap_map_size)
return false;
if (hdr->caplen == 0 || hdr->len == 0 || hdr->caplen > hdr->len)
return false;
if (pcap_ptr_va_curr + sizeof(*hdr) + hdr->caplen -
- pcap_ptr_va_start >= pcap_map_size)
+ pcap_ptr_va_start > pcap_map_size)
return false;
pcap_ptr_va_curr += (sizeof(*hdr) + hdr->caplen);
@@ -1141,7 +1141,7 @@ static int cmd_select(char *num)
pcap_reset_pkt();
bpf_reset();
- for (i = 0; i < which && (have_next = pcap_next_pkt()); i++)
+ for (i = 1; i < which && (have_next = pcap_next_pkt()); i++)
/* noop */;
if (!have_next || pcap_curr_pkt() == NULL) {
rl_printf("no packet #%u available!\n", which);
--
2.53.0
next reply other threads:[~2026-04-28 10:01 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-28 10:01 Hasan Basbunar [this message]
2026-04-28 21:03 ` [PATCH] bpf: bpf_dbg: fix off-by-one in cmd_select and pcap_next_pkt sashiko-bot
2026-04-29 8:44 ` [PATCH v2] bpf: bpf_dbg: fix off-by-one in cmd_select Hasan Basbunar
2026-04-29 9:41 ` sashiko-bot
2026-04-29 12:35 ` [PATCH v3] bpf: bpf_dbg: split pcap_next_pkt() validation/advance, " Hasan Basbunar
2026-04-29 13:13 ` bot+bpf-ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260428100109.56572-1-basbunarhasan@gmail.com \
--to=basbunarhasan@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.