bpf_single_step() handles BPF_LDX | BPF_W | BPF_LEN by assigning the packet length to A instead of X. This leaves X unchanged and clobbers A, so bpf_dbg can report the opposite verdict from the kernel for a valid classic BPF filter. For example, load the following filter and run it on a nonempty pcap: load bpf 3,0 0 0 0,129 0 0 0,22 0 0 0 The filter initializes A to zero, loads the packet length into X and returns A. The kernel rejects the packet, but bpf_dbg reports it as passing because the length load overwrites A. Write the length to X, as required by the instruction, preserving A. The issue was found by reviewing the load cases in bpf_single_step() and comparing them with bpf_convert_filter(). Tested the complete bpf_dbg executable before and after the fix with GCC 12.3.1 on x86-64 Linux 6.6. The fixed tool agrees with SO_ATTACH_FILTER on 21 cases covering X updates, preservation of A, and LD LEN, LDX IMM and LDX MEM controls, including a truncated capture with a larger original length. The original tool disagrees on 12 cases; the nine controls pass with both versions. Fixes: fd981e3c321a ("filter: bpf_dbg: add minimal bpf debugger") Signed-off-by: yanxia --- tools/bpf/bpf_dbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/bpf/bpf_dbg.c b/tools/bpf/bpf_dbg.c index 00e560a..51af4c2 100644 --- a/tools/bpf/bpf_dbg.c +++ b/tools/bpf/bpf_dbg.c @@ -713,7 +713,7 @@ static void bpf_single_step(struct bpf_regs *r, struct sock_filter *f, r->A = pkt_len; break; case BPF_LDX_W | BPF_LEN: - r->A = pkt_len; + r->X = pkt_len; break; case BPF_LD | BPF_IMM: r->A = K; base-commit: ff47652a4b66c067c765a7ad464d930b5a9367cc