bpf_dbg's interactive 'select ' 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 --- 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