Skip to content

Commit 5b0983f

Browse files
committed
bpf: workaround llvm behaviour with indirect jumps
When indirect jumps are enabled in LLVM, it might generate unreachable instructions. For example, the following code SEC("syscall") int foo(struct simple_ctx *ctx) { switch (ctx->x) { case 0: ret_user = 2; break; case 11: ret_user = 3; break; case 27: ret_user = 4; break; case 31: ret_user = 5; break; default: ret_user = 19; break; } return 0; } compiles into <foo>: ; switch (ctx->x) { 224: 79 11 00 00 00 00 00 00 r1 = *(u64 *)(r1 + 0x0) 225: 25 01 0f 00 1f 00 00 00 if r1 > 0x1f goto +0xf <foo+0x88> 226: 67 01 00 00 03 00 00 00 r1 <<= 0x3 227: 18 02 00 00 a8 00 00 00 00 00 00 00 00 00 00 00 r2 = 0xa8 ll 0000000000000718: R_BPF_64_64 .rodata 229: 0f 12 00 00 00 00 00 00 r2 += r1 230: 79 21 00 00 00 00 00 00 r1 = *(u64 *)(r2 + 0x0) 231: 0d 01 00 00 00 00 00 00 gotox r1 232: 05 00 08 00 00 00 00 00 goto +0x8 <foo+0x88> 233: b7 01 00 00 02 00 00 00 r1 = 0x2 ; switch (ctx->x) { 234: 05 00 07 00 00 00 00 00 goto +0x7 <foo+0x90> 235: b7 01 00 00 04 00 00 00 r1 = 0x4 ; break; 236: 05 00 05 00 00 00 00 00 goto +0x5 <foo+0x90> 237: b7 01 00 00 03 00 00 00 r1 = 0x3 ; break; 238: 05 00 03 00 00 00 00 00 goto +0x3 <foo+0x90> 239: b7 01 00 00 05 00 00 00 r1 = 0x5 ; break; 240: 05 00 01 00 00 00 00 00 goto +0x1 <foo+0x90> 241: b7 01 00 00 13 00 00 00 r1 = 0x13 242: 18 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 r2 = 0x0 ll 0000000000000790: R_BPF_64_64 ret_user 244: 7b 12 00 00 00 00 00 00 *(u64 *)(r2 + 0x0) = r1 ; return 0; 245: b4 00 00 00 00 00 00 00 w0 = 0x0 246: 95 00 00 00 00 00 00 00 exit The jump table is 242, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 237, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 241, 235, 241, 241, 241, 239 The check 225: 25 01 0f 00 1f 00 00 00 if r1 > 0x1f goto +0xf <foo+0x88> makes sure that the r1 register is always loaded from the jump table. This makes the instruction 232: 05 00 08 00 00 00 00 00 goto +0x8 <foo+0x88> unreachable. Patch verifier to ignore such unreachable JA instructions. Signed-off-by: Anton Protopopov <[email protected]>
1 parent cd1c318 commit 5b0983f

File tree

1 file changed

+24
-0
lines changed

1 file changed

+24
-0
lines changed

kernel/bpf/verifier.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17869,6 +17869,27 @@ static bool insn_is_gotox(struct bpf_insn *insn)
1786917869
BPF_SRC(insn->code) == BPF_X;
1787017870
}
1787117871

17872+
static bool insn_is_ja(struct bpf_insn *insn)
17873+
{
17874+
return BPF_CLASS(insn->code) == BPF_JMP &&
17875+
BPF_OP(insn->code) == BPF_JA &&
17876+
BPF_SRC(insn->code) == BPF_K;
17877+
}
17878+
17879+
/*
17880+
* This is a workaround to overcome a LLVM "bug". The problem is that
17881+
* sometimes LLVM would generate code like
17882+
*
17883+
* gotox rX
17884+
* goto +offset
17885+
*
17886+
* even though rX never points to the goto +offset instruction.
17887+
*/
17888+
static inline bool magic_dead_ja(struct bpf_insn *insn, bool have_prev)
17889+
{
17890+
return have_prev && insn_is_gotox(insn - 1) && insn_is_ja(insn);
17891+
}
17892+
1787217893
/* non-recursive depth-first-search to detect loops in BPF program
1787317894
* loop == back-edge in directed graph
1787417895
*/
@@ -17943,6 +17964,9 @@ static int check_cfg(struct bpf_verifier_env *env)
1794317964
struct bpf_insn *insn = &env->prog->insnsi[i];
1794417965

1794517966
if (insn_state[i] != EXPLORED) {
17967+
if (magic_dead_ja(insn, i > 0))
17968+
continue;
17969+
1794617970
verbose(env, "unreachable insn %d\n", i);
1794717971
ret = -EINVAL;
1794817972
goto err_free;

0 commit comments

Comments
 (0)