Skip to content

Commit d720f94

Browse files
committed
fix(pcapfilter): pin pcap stub operands across NOP region
Bind pcap stub parameters to R1-R5 and thread them through the reserved NOP region as in/out asm operands. This preserves the spliced filter register contract and prevents clang from scheduling the verdict comparison before the patch area, where NOPs can clobber r0.
1 parent 6eed963 commit d720f94

1 file changed

Lines changed: 39 additions & 6 deletions

File tree

bpf/include/bpf_pcap_stub.h

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,54 @@
2020
static __noinline bool pcap_stub_l3(void *_ctx, void *__ctx, void *___ctx,
2121
void *data, void *data_end)
2222
{
23-
/* 512 × 8-byte NOP insns; must equal stubReservedInsns in internal/pcapfilter/elfpatch.go */
23+
/*
24+
* Bind the five parameters to R1..R5 and route them through the NOP
25+
* region as "+r" in/out operands. This creates a real data dependency
26+
* that forces clang to (1) emit the comparison below AFTER the region
27+
* and (2) read its operands from R1..R5 — exactly the registers the
28+
* spliced filter leaves set (R4=verdict, R5=0, R1=R2=R3=0 via the
29+
* fall-through epilogue in internal/pcapfilter/bpf_filter.go).
30+
*
31+
* Without these constraints clang is free to schedule the comparison
32+
* before the region: clang-12 does, stashing the result in callee-saved
33+
* registers and then letting the region's `r0 = 0` NOPs clobber the
34+
* return value, so the filter verdict is silently dropped. Do not
35+
* "simplify" the register pinning away.
36+
*
37+
*/
38+
register void *a1 asm("r1") = _ctx;
39+
register void *a2 asm("r2") = __ctx;
40+
register void *a3 asm("r3") = ___ctx;
41+
register void *a4 asm("r4") = data;
42+
register void *a5 asm("r5") = data_end;
2443
asm volatile(".rept 512\n\t"
2544
".byte 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n\t"
26-
".endr\n\t");
27-
return data != data_end && _ctx == __ctx && __ctx == ___ctx;
45+
".endr\n\t"
46+
: "+r"(a1), "+r"(a2), "+r"(a3), "+r"(a4), "+r"(a5)
47+
:: "r0");
48+
return a4 != a5 && a1 == a2 && a2 == a3;
2849
}
2950

3051
static __noinline bool pcap_stub_l2(void *_ctx, void *__ctx, void *___ctx,
3152
void *data, void *data_end)
3253
{
33-
/* 512 × 8-byte NOP insns; must equal stubReservedInsns in internal/pcapfilter/elfpatch.go */
54+
/*
55+
* Register-pinned NOP region; see pcap_stub_l3 above for why the
56+
* parameters are bound to R1..R5 and fed through the asm as "+r"
57+
* operands. Do not "simplify" the register pinning away.
58+
*
59+
*/
60+
register void *a1 asm("r1") = _ctx;
61+
register void *a2 asm("r2") = __ctx;
62+
register void *a3 asm("r3") = ___ctx;
63+
register void *a4 asm("r4") = data;
64+
register void *a5 asm("r5") = data_end;
3465
asm volatile(".rept 512\n\t"
3566
".byte 0xb7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00\n\t"
36-
".endr\n\t");
37-
return data != data_end && _ctx == __ctx && __ctx == ___ctx;
67+
".endr\n\t"
68+
: "+r"(a1), "+r"(a2), "+r"(a3), "+r"(a4), "+r"(a5)
69+
:: "r0");
70+
return a4 != a5 && a1 == a2 && a2 == a3;
3871
}
3972

4073
/*

0 commit comments

Comments
 (0)