• Daniel Borkmann's avatar
    bpf: Fix truncation handling for mod32 dst reg wrt zero · 9b00f1b7
    Daniel Borkmann authored
    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 468f6eaf
    ("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: 468f6eaf ("bpf: fix 32-bit ALU op verification")
    Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
    Reviewed-by: default avatarJohn Fastabend <john.fastabend@gmail.com>
    Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
    9b00f1b7
verifier.c 347 KB