-
Notifications
You must be signed in to change notification settings - Fork 147
bpf,x64: implement jump padding in jit #608
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Master branch: 6f02b54 |
|
Master branch: 0565ff5 |
7b5ff55 to
982a9db
Compare
The x64 bpf jit expects bpf images converge within the given passes, but
it could fail to do so with some corner cases. For example:
l0: ja 40
l1: ja 40
[... repeated ja 40 ]
l39: ja 40
l40: ret #0
This bpf program contains 40 "ja 40" instructions which are effectively
NOPs and designed to be replaced with valid code dynamically. Ideally,
bpf jit should optimize those "ja 40" instructions out when translating
the bpf instructions into x64 machine code. However, do_jit() can only
remove one "ja 40" for offset==0 on each pass, so it requires at least
40 runs to eliminate those JMPs and exceeds the current limit of
passes(20). In the end, the program got rejected when BPF_JIT_ALWAYS_ON
is set even though it's legit as a classic socket filter.
To make bpf images more likely converge within 20 passes, this commit
pads some instructions with NOPs in the last 5 passes:
1. conditional jumps
A possible size variance comes from the adoption of imm8 JMP. If the
offset is imm8, we calculate the size difference of this BPF instruction
between the previous and the current pass and fill the gap with NOPs.
To avoid the recalculation of jump offset, those NOPs are inserted before
the JMP code, so we have to subtract the 2 bytes of imm8 JMP when
calculating the NOP number.
2. BPF_JA
There are two conditions for BPF_JA.
a.) nop jumps
If this instruction is not optimized out in the previous pass,
instead of removing it, we insert the equivalent size of NOPs.
b.) label jumps
Similar to condition jumps, we prepend NOPs right before the JMP
code.
To make the code concise, emit_nops() is modified to use the signed len and
return the number of inserted NOPs.
For bpf-to-bpf, the 'padded' flag is introduced to 'struct x64_jit_data' so
that bpf_int_jit_compile() could know whether the program is padded in the
previous run or not.
After applying this patch, the corner case was loaded with the following
jit code:
flen=45 proglen=77 pass=17 image=ffffffffc03367d4 from=jump pid=10097
JIT code: 00000000: 0f 1f 44 00 00 55 48 89 e5 53 41 55 31 c0 45 31
JIT code: 00000010: ed 48 89 fb eb 30 eb 2e eb 2c eb 2a eb 28 eb 26
JIT code: 00000020: eb 24 eb 22 eb 20 eb 1e eb 1c eb 1a eb 18 eb 16
JIT code: 00000030: eb 14 eb 12 eb 10 eb 0e eb 0c eb 0a eb 08 eb 06
JIT code: 00000040: eb 04 eb 02 66 90 31 c0 41 5d 5b c9 c3
0: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]
5: 55 push rbp
6: 48 89 e5 mov rbp,rsp
9: 53 push rbx
a: 41 55 push r13
c: 31 c0 xor eax,eax
e: 45 31 ed xor r13d,r13d
11: 48 89 fb mov rbx,rdi
14: eb 30 jmp 0x46
16: eb 2e jmp 0x46
...
3e: eb 06 jmp 0x46
40: eb 04 jmp 0x46
42: eb 02 jmp 0x46
44: 66 90 xchg ax,ax
46: 31 c0 xor eax,eax
48: 41 5d pop r13
4a: 5b pop rbx
4b: c9 leave
4c: c3 ret
At the 16th pass, 15 jumps were already optimized out, and one jump was
replaced with NOPs at 44 and the image converged at the 17th pass.
v2:
- Simplify the sample code in the description and provide the jit code
- Check the expected padding bytes with WARN_ONCE
- Move the 'padded' flag to 'struct x64_jit_data'
Signed-off-by: Gary Lin <glin@suse.com>
With NOPs padding, x64 jit now can handle the jump cases like bpf_fill_maxinsns11(). Signed-off-by: Gary Lin <glin@suse.com>
There are two tests added into verifier's jit tests to trigger x64
jit jump padding. The first test can be represented as the following
assembly code:
1: bpf_call bpf_get_prandom_u32
2: if r0 == 0 goto pc+128
3: if r0 == 1 goto pc+128
...
129: if r0 == 127 goto pc+128
130: goto pc+128
131: goto pc+127
...
256: goto pc+1
257: goto pc+0
258: r0 = 1
259: ret
We first store a random number to r0 and add the corresponding
conditional jumps (2~129) to make verifier believe that those jump
instructions from 130 to 257 are reachable. When the program is sent to
x64 jit, it starts to optimize out the NOP jumps backwards from 257.
Since there are 128 such jumps, the program easily reaches 15 passes and
triggers jump padding.
Here is the x64 jit code of the first test:
0: 0f 1f 44 00 00 nop DWORD PTR [rax+rax*1+0x0]
5: 66 90 xchg ax,ax
7: 55 push rbp
8: 48 89 e5 mov rbp,rsp
b: e8 4c 90 75 e3 call 0xffffffffe375905c
10: 48 83 f8 01 cmp rax,0x1
14: 0f 84 fe 04 00 00 je 0x518
1a: 48 83 f8 02 cmp rax,0x2
1e: 0f 84 f9 04 00 00 je 0x51d
...
f6: 48 83 f8 18 cmp rax,0x18
fa: 0f 84 8b 04 00 00 je 0x58b
100: 48 83 f8 19 cmp rax,0x19
104: 0f 84 86 04 00 00 je 0x590
10a: 48 83 f8 1a cmp rax,0x1a
10e: 0f 84 81 04 00 00 je 0x595
...
500: 0f 84 83 01 00 00 je 0x689
506: 48 81 f8 80 00 00 00 cmp rax,0x80
50d: 0f 84 76 01 00 00 je 0x689
513: e9 71 01 00 00 jmp 0x689
518: e9 6c 01 00 00 jmp 0x689
...
5fe: e9 86 00 00 00 jmp 0x689
603: e9 81 00 00 00 jmp 0x689
608: 0f 1f 00 nop DWORD PTR [rax]
60b: eb 7c jmp 0x689
60d: eb 7a jmp 0x689
...
683: eb 04 jmp 0x689
685: eb 02 jmp 0x689
687: 66 90 xchg ax,ax
689: b8 01 00 00 00 mov eax,0x1
68e: c9 leave
68f: c3 ret
As expected, a 3 bytes NOPs is inserted at 608 due to the transition
from imm32 jmp to imm8 jmp. A 2 bytes NOPs is also inserted at 687 to
replace a NOP jump.
The second test is to invoke the first test as a subprog to test
bpf2bpf. Per the system log, there was one more jit happened with only
one pass and the same jit code was produced.
Signed-off-by: Gary Lin <glin@suse.com>
|
Master branch: 286e95e |
982a9db to
c8f3244
Compare
|
At least one diff in series https://patchwork.kernel.org/project/netdevbpf/list/?series=410285 expired. Closing PR. |
Failing tests: - kernel-patches#110 fexit_bpf2bpf:FAIL - kernel-patches#124 for_each:FAIL - kernel-patches#144 iters:FAIL - kernel-patches#148 kfree_skb:FAIL - kernel-patches#161 l4lb_all:FAIL - kernel-patches#193 map_kptr:FAIL - kernel-patches#23 bpf_loop:FAIL - kernel-patches#260 pkt_access:FAIL - kernel-patches#269 prog_run_opts:FAIL - kernel-patches#280 rbtree_success:FAIL - kernel-patches#356 res_spin_lock_failure:FAIL - kernel-patches#364 setget_sockopt:FAIL - kernel-patches#381 sock_fields:FAIL - kernel-patches#394 spin_lock:FAIL - kernel-patches#395 spin_lock_success:FAIL - kernel-patches#444 test_bpffs:FAIL - kernel-patches#453 test_profiler:FAIL - kernel-patches#479 usdt:FAIL - kernel-patches#488 verifier_bits_iter:FAIL - kernel-patches#597 verif_scale_pyperf600:FAIL - kernel-patches#598 verif_scale_pyperf600_bpf_loop:FAIL - kernel-patches#599 verif_scale_pyperf600_iter:FAIL - kernel-patches#608 verif_scale_strobemeta_subprogs:FAIL - kernel-patches#622 xdp_attach:FAIL - kernel-patches#637 xdp_noinline:FAIL - kernel-patches#639 xdp_synproxy:FAIL - kernel-patches#72 cls_redirect:FAIL - kernel-patches#88 crypto_sanity:FAIL - kernel-patches#97 dynptr:FAIL Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Failing tests: - kernel-patches#110 fexit_bpf2bpf:FAIL - kernel-patches#124 for_each:FAIL - kernel-patches#144 iters:FAIL - kernel-patches#148 kfree_skb:FAIL - kernel-patches#161 l4lb_all:FAIL - kernel-patches#193 map_kptr:FAIL - kernel-patches#23 bpf_loop:FAIL - kernel-patches#260 pkt_access:FAIL - kernel-patches#269 prog_run_opts:FAIL - kernel-patches#280 rbtree_success:FAIL - kernel-patches#356 res_spin_lock_failure:FAIL - kernel-patches#364 setget_sockopt:FAIL - kernel-patches#381 sock_fields:FAIL - kernel-patches#394 spin_lock:FAIL - kernel-patches#395 spin_lock_success:FAIL - kernel-patches#444 test_bpffs:FAIL - kernel-patches#453 test_profiler:FAIL - kernel-patches#479 usdt:FAIL - kernel-patches#488 verifier_bits_iter:FAIL - kernel-patches#597 verif_scale_pyperf600:FAIL - kernel-patches#598 verif_scale_pyperf600_bpf_loop:FAIL - kernel-patches#599 verif_scale_pyperf600_iter:FAIL - kernel-patches#608 verif_scale_strobemeta_subprogs:FAIL - kernel-patches#622 xdp_attach:FAIL - kernel-patches#637 xdp_noinline:FAIL - kernel-patches#639 xdp_synproxy:FAIL - kernel-patches#72 cls_redirect:FAIL - kernel-patches#88 crypto_sanity:FAIL - kernel-patches#97 dynptr:FAIL Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Pull request for series with
subject: bpf,x64: implement jump padding in jit
version: 2
url: https://patchwork.kernel.org/project/netdevbpf/list/?series=410285