mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	bpf: improve verifier ARG_CONST_SIZE_OR_ZERO semantics
For helpers, the argument type ARG_CONST_SIZE_OR_ZERO permits the
access size to be 0 when accessing the previous argument (arg).
Right now, it requires the arg needs to be NULL when size passed
is 0 or could be 0. It also requires a non-NULL arg when the size
is proved to be non-0.
This patch changes verifier ARG_CONST_SIZE_OR_ZERO behavior
such that for size-0 or possible size-0, it is not required
the arg equal to NULL.
There are a couple of reasons for this semantics change, and
all of them intends to simplify user bpf programs which
may improve user experience and/or increase chances of
verifier acceptance. Together with the next patch which
changes bpf_probe_read arg2 type from ARG_CONST_SIZE to
ARG_CONST_SIZE_OR_ZERO, the following two examples, which
fail the verifier currently, are able to get verifier acceptance.
Example 1:
   unsigned long len = pend - pstart;
   len = len > MAX_PAYLOAD_LEN ? MAX_PAYLOAD_LEN : len;
   len &= MAX_PAYLOAD_LEN;
   bpf_probe_read(data->payload, len, pstart);
It does not have test for "len > 0" and it failed the verifier.
Users may not be aware that they have to add this test.
Converting the bpf_probe_read helper to have
ARG_CONST_SIZE_OR_ZERO helps the above code get
verifier acceptance.
Example 2:
  Here is one example where llvm "messed up" the code and
  the verifier fails.
......
   unsigned long len = pend - pstart;
   if (len > 0 && len <= MAX_PAYLOAD_LEN)
     bpf_probe_read(data->payload, len, pstart);
......
The compiler generates the following code and verifier fails:
......
39: (79) r2 = *(u64 *)(r10 -16)
40: (1f) r2 -= r8
41: (bf) r1 = r2
42: (07) r1 += -1
43: (25) if r1 > 0xffe goto pc+3
  R0=inv(id=0) R1=inv(id=0,umax_value=4094,var_off=(0x0; 0xfff))
  R2=inv(id=0) R6=map_value(id=0,off=0,ks=4,vs=4095,imm=0) R7=inv(id=0)
  R8=inv(id=0) R9=inv0 R10=fp0
44: (bf) r1 = r6
45: (bf) r3 = r8
46: (85) call bpf_probe_read#45
R2 min value is negative, either use unsigned or 'var &= const'
......
The compiler optimization is correct. If r1 = 0,
r1 - 1 = 0xffffffffffffffff > 0xffe.  If r1 != 0, r1 - 1 will not wrap.
r1 > 0xffe at insn #43 can actually capture
both "r1 > 0" and "len <= MAX_PAYLOAD_LEN".
This however causes an issue in verifier as the value range of arg2
"r2" does not properly get refined and lead to verification failure.
Relaxing bpf_prog_read arg2 from ARG_CONST_SIZE to ARG_CONST_SIZE_OR_ZERO
allows the following simplied code:
   unsigned long len = pend - pstart;
   if (len <= MAX_PAYLOAD_LEN)
     bpf_probe_read(data->payload, len, pstart);
The llvm compiler will generate less complex code and the
verifier is able to verify that the program is okay.
Signed-off-by: Yonghong Song <yhs@fb.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									3a9b76fd0d
								
							
						
					
					
						commit
						9fd29c08e5
					
				
					 1 changed files with 24 additions and 16 deletions
				
			
		| 
						 | 
				
			
			@ -799,12 +799,13 @@ static int check_stack_read(struct bpf_verifier_env *env,
 | 
			
		|||
 | 
			
		||||
/* check read/write into map element returned by bpf_map_lookup_elem() */
 | 
			
		||||
static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off,
 | 
			
		||||
			    int size)
 | 
			
		||||
			      int size, bool zero_size_allowed)
 | 
			
		||||
{
 | 
			
		||||
	struct bpf_reg_state *regs = cur_regs(env);
 | 
			
		||||
	struct bpf_map *map = regs[regno].map_ptr;
 | 
			
		||||
 | 
			
		||||
	if (off < 0 || size <= 0 || off + size > map->value_size) {
 | 
			
		||||
	if (off < 0 || size < 0 || (size == 0 && !zero_size_allowed) ||
 | 
			
		||||
	    off + size > map->value_size) {
 | 
			
		||||
		verbose(env, "invalid access to map value, value_size=%d off=%d size=%d\n",
 | 
			
		||||
			map->value_size, off, size);
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
| 
						 | 
				
			
			@ -814,7 +815,7 @@ static int __check_map_access(struct bpf_verifier_env *env, u32 regno, int off,
 | 
			
		|||
 | 
			
		||||
/* check read/write into a map element with possible variable offset */
 | 
			
		||||
static int check_map_access(struct bpf_verifier_env *env, u32 regno,
 | 
			
		||||
			    int off, int size)
 | 
			
		||||
			    int off, int size, bool zero_size_allowed)
 | 
			
		||||
{
 | 
			
		||||
	struct bpf_verifier_state *state = env->cur_state;
 | 
			
		||||
	struct bpf_reg_state *reg = &state->regs[regno];
 | 
			
		||||
| 
						 | 
				
			
			@ -837,7 +838,8 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno,
 | 
			
		|||
			regno);
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
	}
 | 
			
		||||
	err = __check_map_access(env, regno, reg->smin_value + off, size);
 | 
			
		||||
	err = __check_map_access(env, regno, reg->smin_value + off, size,
 | 
			
		||||
				 zero_size_allowed);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		verbose(env, "R%d min value is outside of the array range\n",
 | 
			
		||||
			regno);
 | 
			
		||||
| 
						 | 
				
			
			@ -853,7 +855,8 @@ static int check_map_access(struct bpf_verifier_env *env, u32 regno,
 | 
			
		|||
			regno);
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
	}
 | 
			
		||||
	err = __check_map_access(env, regno, reg->umax_value + off, size);
 | 
			
		||||
	err = __check_map_access(env, regno, reg->umax_value + off, size,
 | 
			
		||||
				 zero_size_allowed);
 | 
			
		||||
	if (err)
 | 
			
		||||
		verbose(env, "R%d max value is outside of the array range\n",
 | 
			
		||||
			regno);
 | 
			
		||||
| 
						 | 
				
			
			@ -889,12 +892,13 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static int __check_packet_access(struct bpf_verifier_env *env, u32 regno,
 | 
			
		||||
				 int off, int size)
 | 
			
		||||
				 int off, int size, bool zero_size_allowed)
 | 
			
		||||
{
 | 
			
		||||
	struct bpf_reg_state *regs = cur_regs(env);
 | 
			
		||||
	struct bpf_reg_state *reg = ®s[regno];
 | 
			
		||||
 | 
			
		||||
	if (off < 0 || size <= 0 || (u64)off + size > reg->range) {
 | 
			
		||||
	if (off < 0 || size < 0 || (size == 0 && !zero_size_allowed) ||
 | 
			
		||||
	    (u64)off + size > reg->range) {
 | 
			
		||||
		verbose(env, "invalid access to packet, off=%d size=%d, R%d(id=%d,off=%d,r=%d)\n",
 | 
			
		||||
			off, size, regno, reg->id, reg->off, reg->range);
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
| 
						 | 
				
			
			@ -903,7 +907,7 @@ static int __check_packet_access(struct bpf_verifier_env *env, u32 regno,
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
 | 
			
		||||
			       int size)
 | 
			
		||||
			       int size, bool zero_size_allowed)
 | 
			
		||||
{
 | 
			
		||||
	struct bpf_reg_state *regs = cur_regs(env);
 | 
			
		||||
	struct bpf_reg_state *reg = ®s[regno];
 | 
			
		||||
| 
						 | 
				
			
			@ -922,7 +926,7 @@ static int check_packet_access(struct bpf_verifier_env *env, u32 regno, int off,
 | 
			
		|||
			regno);
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
	}
 | 
			
		||||
	err = __check_packet_access(env, regno, off, size);
 | 
			
		||||
	err = __check_packet_access(env, regno, off, size, zero_size_allowed);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		verbose(env, "R%d offset is outside of the packet\n", regno);
 | 
			
		||||
		return err;
 | 
			
		||||
| 
						 | 
				
			
			@ -1097,7 +1101,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
 | 
			
		|||
			return -EACCES;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		err = check_map_access(env, regno, off, size);
 | 
			
		||||
		err = check_map_access(env, regno, off, size, false);
 | 
			
		||||
		if (!err && t == BPF_READ && value_regno >= 0)
 | 
			
		||||
			mark_reg_unknown(env, regs, value_regno);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1184,7 +1188,7 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn
 | 
			
		|||
				value_regno);
 | 
			
		||||
			return -EACCES;
 | 
			
		||||
		}
 | 
			
		||||
		err = check_packet_access(env, regno, off, size);
 | 
			
		||||
		err = check_packet_access(env, regno, off, size, false);
 | 
			
		||||
		if (!err && t == BPF_READ && value_regno >= 0)
 | 
			
		||||
			mark_reg_unknown(env, regs, value_regno);
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1281,7 +1285,7 @@ static int check_stack_boundary(struct bpf_verifier_env *env, int regno,
 | 
			
		|||
	}
 | 
			
		||||
	off = regs[regno].off + regs[regno].var_off.value;
 | 
			
		||||
	if (off >= 0 || off < -MAX_BPF_STACK || off + access_size > 0 ||
 | 
			
		||||
	    access_size <= 0) {
 | 
			
		||||
	    access_size < 0 || (access_size == 0 && !zero_size_allowed)) {
 | 
			
		||||
		verbose(env, "invalid stack type R%d off=%d access_size=%d\n",
 | 
			
		||||
			regno, off, access_size);
 | 
			
		||||
		return -EACCES;
 | 
			
		||||
| 
						 | 
				
			
			@ -1319,9 +1323,11 @@ static int check_helper_mem_access(struct bpf_verifier_env *env, int regno,
 | 
			
		|||
	switch (reg->type) {
 | 
			
		||||
	case PTR_TO_PACKET:
 | 
			
		||||
	case PTR_TO_PACKET_META:
 | 
			
		||||
		return check_packet_access(env, regno, reg->off, access_size);
 | 
			
		||||
		return check_packet_access(env, regno, reg->off, access_size,
 | 
			
		||||
					   zero_size_allowed);
 | 
			
		||||
	case PTR_TO_MAP_VALUE:
 | 
			
		||||
		return check_map_access(env, regno, reg->off, access_size);
 | 
			
		||||
		return check_map_access(env, regno, reg->off, access_size,
 | 
			
		||||
					zero_size_allowed);
 | 
			
		||||
	default: /* scalar_value|ptr_to_stack or invalid ptr */
 | 
			
		||||
		return check_stack_boundary(env, regno, access_size,
 | 
			
		||||
					    zero_size_allowed, meta);
 | 
			
		||||
| 
						 | 
				
			
			@ -1415,7 +1421,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
 | 
			
		|||
		}
 | 
			
		||||
		if (type_is_pkt_pointer(type))
 | 
			
		||||
			err = check_packet_access(env, regno, reg->off,
 | 
			
		||||
						  meta->map_ptr->key_size);
 | 
			
		||||
						  meta->map_ptr->key_size,
 | 
			
		||||
						  false);
 | 
			
		||||
		else
 | 
			
		||||
			err = check_stack_boundary(env, regno,
 | 
			
		||||
						   meta->map_ptr->key_size,
 | 
			
		||||
| 
						 | 
				
			
			@ -1431,7 +1438,8 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 regno,
 | 
			
		|||
		}
 | 
			
		||||
		if (type_is_pkt_pointer(type))
 | 
			
		||||
			err = check_packet_access(env, regno, reg->off,
 | 
			
		||||
						  meta->map_ptr->value_size);
 | 
			
		||||
						  meta->map_ptr->value_size,
 | 
			
		||||
						  false);
 | 
			
		||||
		else
 | 
			
		||||
			err = check_stack_boundary(env, regno,
 | 
			
		||||
						   meta->map_ptr->value_size,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue