Skip to content

Commit 2a5418a

Browse files
borkmannAlexei Starovoitov
authored andcommitted
bpf: improve dead code sanitizing
Given we recently had c131187 ("bpf: fix branch pruning logic") and 95a762e ("bpf: fix incorrect sign extension in check_alu_op()") in particular where before verifier skipped verification of the wrongly assumed dead branch, we should not just replace the dead code parts with nops (mov r0,r0). If there is a bug such as fixed in 95a762e in future again, where runtime could execute those insns, then one of the potential issues with the current setting would be that given the nops would be at the end of the program, we could execute out of bounds at some point. The best in such case would be to just exit the BPF program altogether and return an exception code. However, given this would require two instructions, and such a dead code gap could just be a single insn long, we would need to place 'r0 = X; ret' snippet at the very end after the user program or at the start before the program (where we'd skip that region on prog entry), and then place unconditional ja's into the dead code gap. While more complex but possible, there's still another block in the road that currently prevents from this, namely BPF to BPF calls. The issue here is that such exception could be returned from a callee, but the caller would not know that it's an exception that needs to be propagated further down. Alternative that has little complexity is to just use a ja-1 code for now which will trap the execution here instead of silently doing bad things if we ever get there due to bugs. Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 1d62167 commit 2a5418a

File tree

1 file changed

+12
-5
lines changed

1 file changed

+12
-5
lines changed

kernel/bpf/verifier.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5064,22 +5064,29 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of
50645064
return new_prog;
50655065
}
50665066

5067-
/* The verifier does more data flow analysis than llvm and will not explore
5068-
* branches that are dead at run time. Malicious programs can have dead code
5069-
* too. Therefore replace all dead at-run-time code with nops.
5067+
/* The verifier does more data flow analysis than llvm and will not
5068+
* explore branches that are dead at run time. Malicious programs can
5069+
* have dead code too. Therefore replace all dead at-run-time code
5070+
* with 'ja -1'.
5071+
*
5072+
* Just nops are not optimal, e.g. if they would sit at the end of the
5073+
* program and through another bug we would manage to jump there, then
5074+
* we'd execute beyond program memory otherwise. Returning exception
5075+
* code also wouldn't work since we can have subprogs where the dead
5076+
* code could be located.
50705077
*/
50715078
static void sanitize_dead_code(struct bpf_verifier_env *env)
50725079
{
50735080
struct bpf_insn_aux_data *aux_data = env->insn_aux_data;
5074-
struct bpf_insn nop = BPF_MOV64_REG(BPF_REG_0, BPF_REG_0);
5081+
struct bpf_insn trap = BPF_JMP_IMM(BPF_JA, 0, 0, -1);
50755082
struct bpf_insn *insn = env->prog->insnsi;
50765083
const int insn_cnt = env->prog->len;
50775084
int i;
50785085

50795086
for (i = 0; i < insn_cnt; i++) {
50805087
if (aux_data[i].seen)
50815088
continue;
5082-
memcpy(insn + i, &nop, sizeof(nop));
5089+
memcpy(insn + i, &trap, sizeof(trap));
50835090
}
50845091
}
50855092

0 commit comments

Comments
 (0)