|
20 | 20 | static __noinline bool pcap_stub_l3(void *_ctx, void *__ctx, void *___ctx, |
21 | 21 | void *data, void *data_end) |
22 | 22 | { |
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; |
24 | 43 | asm volatile(".rept 512\n\t" |
25 | 44 | ".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; |
28 | 49 | } |
29 | 50 |
|
30 | 51 | static __noinline bool pcap_stub_l2(void *_ctx, void *__ctx, void *___ctx, |
31 | 52 | void *data, void *data_end) |
32 | 53 | { |
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; |
34 | 65 | asm volatile(".rept 512\n\t" |
35 | 66 | ".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; |
38 | 71 | } |
39 | 72 |
|
40 | 73 | /* |
|
0 commit comments