Skip to content

Commit 185c226

Browse files
borkmanngregkh
authored andcommitted
bpf: Fix truncation handling for mod32 dst reg wrt zero
commit 9b00f1b upstream. Recently noticed that when mod32 with a known src reg of 0 is performed, then the dst register is 32-bit truncated in verifier: 0: R1=ctx(id=0,off=0,imm=0) R10=fp0 0: (b7) r0 = 0 1: R0_w=inv0 R1=ctx(id=0,off=0,imm=0) R10=fp0 1: (b7) r1 = -1 2: R0_w=inv0 R1_w=inv-1 R10=fp0 2: (b4) w2 = -1 3: R0_w=inv0 R1_w=inv-1 R2_w=inv4294967295 R10=fp0 3: (9c) w1 %= w0 4: R0_w=inv0 R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R2_w=inv4294967295 R10=fp0 4: (b7) r0 = 1 5: R0_w=inv1 R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R2_w=inv4294967295 R10=fp0 5: (1d) if r1 == r2 goto pc+1 R0_w=inv1 R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R2_w=inv4294967295 R10=fp0 6: R0_w=inv1 R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R2_w=inv4294967295 R10=fp0 6: (b7) r0 = 2 7: R0_w=inv2 R1_w=inv(id=0,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R2_w=inv4294967295 R10=fp0 7: (95) exit 7: R0=inv1 R1=inv(id=0,umin_value=4294967295,umax_value=4294967295,var_off=(0x0; 0xffffffff)) R2=inv4294967295 R10=fp0 7: (95) exit However, as a runtime result, we get 2 instead of 1, meaning the dst register does not contain (u32)-1 in this case. The reason is fairly straight forward given the 0 test leaves the dst register as-is: # ./bpftool p d x i 23 0: (b7) r0 = 0 1: (b7) r1 = -1 2: (b4) w2 = -1 3: (16) if w0 == 0x0 goto pc+1 4: (9c) w1 %= w0 5: (b7) r0 = 1 6: (1d) if r1 == r2 goto pc+1 7: (b7) r0 = 2 8: (95) exit This was originally not an issue given the dst register was marked as completely unknown (aka 64 bit unknown). However, after 468f6ea ("bpf: fix 32-bit ALU op verification") the verifier casts the register output to 32 bit, and hence it becomes 32 bit unknown. Note that for the case where the src register is unknown, the dst register is marked 64 bit unknown. After the fix, the register is truncated by the runtime and the test passes: # ./bpftool p d x i 23 0: (b7) r0 = 0 1: (b7) r1 = -1 2: (b4) w2 = -1 3: (16) if w0 == 0x0 goto pc+2 4: (9c) w1 %= w0 5: (05) goto pc+1 6: (bc) w1 = w1 7: (b7) r0 = 1 8: (1d) if r1 == r2 goto pc+1 9: (b7) r0 = 2 10: (95) exit Semantics also match with {R,W}x mod{64,32} 0 -> {R,W}x. Invalid div has always been {R,W}x div{64,32} 0 -> 0. Rewrites are as follows: mod32: mod64: (16) if w0 == 0x0 goto pc+2 (15) if r0 == 0x0 goto pc+1 (9c) w1 %= w0 (9f) r1 %= r0 (05) goto pc+1 (bc) w1 = w1 Fixes: 468f6ea ("bpf: fix 32-bit ALU op verification") Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Reviewed-by: John Fastabend <john.fastabend@gmail.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent fc944dd commit 185c226

1 file changed

Lines changed: 6 additions & 4 deletions

File tree

kernel/bpf/verifier.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9005,7 +9005,7 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
90059005
bool isdiv = BPF_OP(insn->code) == BPF_DIV;
90069006
struct bpf_insn *patchlet;
90079007
struct bpf_insn chk_and_div[] = {
9008-
/* Rx div 0 -> 0 */
9008+
/* [R,W]x div 0 -> 0 */
90099009
BPF_RAW_INSN((is64 ? BPF_JMP : BPF_JMP32) |
90109010
BPF_JNE | BPF_K, insn->src_reg,
90119011
0, 2, 0),
@@ -9014,16 +9014,18 @@ static int fixup_bpf_calls(struct bpf_verifier_env *env)
90149014
*insn,
90159015
};
90169016
struct bpf_insn chk_and_mod[] = {
9017-
/* Rx mod 0 -> Rx */
9017+
/* [R,W]x mod 0 -> [R,W]x */
90189018
BPF_RAW_INSN((is64 ? BPF_JMP : BPF_JMP32) |
90199019
BPF_JEQ | BPF_K, insn->src_reg,
9020-
0, 1, 0),
9020+
0, 1 + (is64 ? 0 : 1), 0),
90219021
*insn,
9022+
BPF_JMP_IMM(BPF_JA, 0, 0, 1),
9023+
BPF_MOV32_REG(insn->dst_reg, insn->dst_reg),
90229024
};
90239025

90249026
patchlet = isdiv ? chk_and_div : chk_and_mod;
90259027
cnt = isdiv ? ARRAY_SIZE(chk_and_div) :
9026-
ARRAY_SIZE(chk_and_mod);
9028+
ARRAY_SIZE(chk_and_mod) - (is64 ? 2 : 0);
90279029

90289030
new_prog = bpf_patch_insn_data(env, i + delta, patchlet, cnt);
90299031
if (!new_prog)

0 commit comments

Comments
 (0)