• John Fastabend's avatar
    bpf: Fix a verifier issue when assigning 32bit reg states to 64bit ones · 3a71dc36
    John Fastabend authored
    With the latest trunk llvm (llvm 11), I hit a verifier issue for
    test_prog subtest test_verif_scale1.
    
    The following simplified example illustrate the issue:
        w9 = 0  /* R9_w=inv0 */
        r8 = *(u32 *)(r1 + 80)  /* __sk_buff->data_end */
        r7 = *(u32 *)(r1 + 76)  /* __sk_buff->data */
        ......
        w2 = w9 /* R2_w=inv0 */
        r6 = r7 /* R6_w=pkt(id=0,off=0,r=0,imm=0) */
        r6 += r2 /* R6_w=inv(id=0) */
        r3 = r6 /* R3_w=inv(id=0) */
        r3 += 14 /* R3_w=inv(id=0) */
        if r3 > r8 goto end
        r5 = *(u32 *)(r6 + 0) /* R6_w=inv(id=0) */
           <== error here: R6 invalid mem access 'inv'
        ...
      end:
    
    In real test_verif_scale1 code, "w9 = 0" and "w2 = w9" are in
    different basic blocks.
    
    In the above, after "r6 += r2", r6 becomes a scalar, which eventually
    caused the memory access error. The correct register state should be
    a pkt pointer.
    
    The inprecise register state starts at "w2 = w9".
    The 32bit register w9 is 0, in __reg_assign_32_into_64(),
    the 64bit reg->smax_value is assigned to be U32_MAX.
    The 64bit reg->smin_value is 0 and the 64bit register
    itself remains constant based on reg->var_off.
    
    In adjust_ptr_min_max_vals(), the verifier checks for a known constant,
    smin_val must be equal to smax_val. Since they are not equal,
    the verifier decides r6 is a unknown scalar, which caused later failure.
    
    The llvm10 does not have this issue as it generates different code:
        w9 = 0  /* R9_w=inv0 */
        r8 = *(u32 *)(r1 + 80)  /* __sk_buff->data_end */
        r7 = *(u32 *)(r1 + 76)  /* __sk_buff->data */
        ......
        r6 = r7 /* R6_w=pkt(id=0,off=0,r=0,imm=0) */
        r6 += r9 /* R6_w=pkt(id=0,off=0,r=0,imm=0) */
        r3 = r6 /* R3_w=pkt(id=0,off=0,r=0,imm=0) */
        r3 += 14 /* R3_w=pkt(id=0,off=14,r=0,imm=0) */
        if r3 > r8 goto end
        ...
    
    To fix the above issue, we can include zero in the test condition for
    assigning the s32_max_value and s32_min_value to their 64-bit equivalents
    smax_value and smin_value.
    
    Further, fix the condition to avoid doing zero extension bounds checks
    when s32_min_value <= 0. This could allow for the case where bounds
    32-bit bounds (-1,1) get incorrectly translated to (0,1) 64-bit bounds.
    When in-fact the -1 min value needs to force U32_MAX bound.
    
    Fixes: 3f50f132 ("bpf: Verifier, do explicit ALU32 bounds tracking")
    Signed-off-by: default avatarJohn Fastabend <john.fastabend@gmail.com>
    Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
    Acked-by: default avatarYonghong Song <yhs@fb.com>
    Link: https://lore.kernel.org/bpf/159077331983.6014.5758956193749002737.stgit@john-Precision-5820-Tower
    3a71dc36
verifier.c 311 KB