Re: [PATCH bpf-next 1/2] [bpf] allow s32/u32 return types in verifier for bpf helpers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sun, Nov 17, 2019 at 10:27:04AM -0800, Yonghong Song wrote:
> Currently, for all helpers with integer return type,
> the verifier permits a single return type, RET_INTEGER,
> which represents 64-bit return value from the helper,
> and the verifier will assign 64-bit value ranges for these
> return values. Such an assumption is different
> from what compiler sees and the generated code with
> llvm alu32 mode, and may lead verification failure.
> 
> For example, with latest llvm, selftest test_progs
> failed for program raw_tracepoint/kfree_skb.
> The source code looks like below:
> 
>   static __always_inline uint64_t read_str_var(...)
>   {
>     len = bpf_probe_read_user_str(payload, STROBE_MAX_STR_LEN, value->ptr);
>     if (len > STROBE_MAX_STR_LEN)
>       return 0;
>     ...
>     return len;
>   }
>   for (int i = 0; i < STROBE_MAX_STRS; ++i) {
>     payload += read_str_var(cfg, i, tls_base, &value, data, payload);
>   }
> 
> In the above, "cfg" refers to map "strobemeta_cfgs" and the signature
> for bpf_probe_read_user_str() is:
>  static int (*bpf_probe_read_user_str)(void *dst, __u32 size, const void *unsafe_ptr) = (void *) 114;
> in tools/lib/bpf/bpf_helper_defs.h.
> 
> The code path causing verification failure looks like below:
>   193: call bpf_probe_read_user_str
>   194: if (w0 > 0x1) goto pc + 2
>   195: *(u16 *)(r7 +80) = r0
>   196: r6 = r0
>   ...
>   199: r8 = *(u64 *)(r10 -416)
>   200: r8 += r6
>   R1 unbounded memory access, make sure to bounds check any array access into a map
> 
> After insn 193, the current verifier assumes r0 can be any 64-bit value.
>   R0=inv(id=0)
> At insn 194, the compiler assumes bpf_probe_read_user_str() will return
>   an __s32 value hences uses subregister w0 at insn 194 and at
>   branch false target, the w0 range can be refined to unsigned values <= 1.
>   But since insn 193 marks r0 with non-empty upper 32bit value, the new
>   umax_value becomes 0xffffffff00000001.
>   R0_w=inv(id=0,umax_value=18446744069414584321)
> At insn 196, the register r0 is assigned to r6.
> At insn 199, map pointer to strobemeta_cfgs map is restored and further refined
>   at insn 200 and 201.
> At insn 200, the verifier complains r8 + r6 unbounded memory access since r6 has
>   R6_rw=invP(id=0,umax_value=18446744069414584321)
> 
> The pattern can be roughly described by the following steps:
>   . helper return 32bit value, but return value marked conservatively.
>   . sub-registeer w0 is refined.
>   . r0 is used and the refined w0 range is lost.
>   . later use of r0 may trigger some kind of unbound failure.
> 
> To remove such failure, r0 range at insn 193 should be more aligned with
> what user expect based on function prototype in bpf_helper_defs.h, i.e.,
> its value range should be with 32-bit value. This patches distinguished
> 32-bit from 64-bit return values in verifier with proper return value
> ranges in r0. In the above case,
> after insn 193, the verifier will give r0 range as
>   R0_w=inv(id=0,smin_value=-2147483648,smax_value=2147483647,umax_value=4294967295,var_off=(0x0; 0xffffffff))
> after insn 194, the new r0 range will be
>   R0_w=inv(id=0,umax_value=1,var_off=(0x0; 0x1))
> This better register range avoids above verification failure.

conceptually looks good. please refactor the code though.

> +static void mark_ret_reg_unknown(struct bpf_verifier_env *env,
> +				 struct bpf_reg_state *regs,
> +				 enum bpf_return_type ret_type)
> +{
> +	struct bpf_reg_state *reg = regs + BPF_REG_0;
> +
> +	memset(reg, 0, offsetof(struct bpf_reg_state, var_off));

only __mark_reg_known and __mark_reg_unknown should be allowed to do this.

> +	reg->type = SCALAR_VALUE;

> +
> +	if (ret_type == RET_INT64) {
> +		reg->var_off.mask = -1;

This is internal details of tnum_unknown. The code should not assign var_off directly.

> +		__mark_reg_unbounded(reg);
> +	} else {
> +		reg->var_off.mask = 0xffffffffULL;

same thing. Please introduce tnum_unknown32 instead

> +		reg->smin_value = S32_MIN;
> +		reg->smax_value = S32_MAX;
> +		reg->umin_value = 0;
> +		reg->umax_value = U32_MAX;

and this can be derived from tnum_unknown32.

> +	}
> +
> +	regs->precise = env->subprog_cnt > 1 || !env->allow_ptr_leaks ?
> +			true : false;

Doesn't belong here. Should really be one helper that does this.




[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux