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 <a.s.protopopov@xxxxxxxxx> --- kernel/bpf/verifier.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index fba553f844f1..2e4116c71f4b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -17792,6 +17792,27 @@ static bool insn_is_gotox(struct bpf_insn *insn) BPF_SRC(insn->code) == BPF_X; } +static bool insn_is_ja(struct bpf_insn *insn) +{ + return BPF_CLASS(insn->code) == BPF_JMP && + BPF_OP(insn->code) == BPF_JA && + BPF_SRC(insn->code) == BPF_K; +} + +/* + * This is a workaround to overcome a LLVM "bug". The problem is that + * sometimes LLVM would generate code like + * + * gotox rX + * goto +offset + * + * even though rX never points to the goto +offset instruction. + */ +static inline bool magic_dead_ja(struct bpf_insn *insn, bool have_prev) +{ + return have_prev && insn_is_gotox(insn - 1) && insn_is_ja(insn); +} + /* non-recursive depth-first-search to detect loops in BPF program * loop == back-edge in directed graph */ @@ -17866,6 +17887,9 @@ static int check_cfg(struct bpf_verifier_env *env) struct bpf_insn *insn = &env->prog->insnsi[i]; if (insn_state[i] != EXPLORED) { + if (magic_dead_ja(insn, i > 0)) + continue; + verbose(env, "unreachable insn %d\n", i); ret = -EINVAL; goto err_free; -- 2.34.1