diff options
Diffstat (limited to 'kernel/bpf/verifier.c')
| -rw-r--r-- | kernel/bpf/verifier.c | 54 | 
1 files changed, 45 insertions, 9 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 47dcd3aa6e23..039d866fd36a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -907,6 +907,23 @@ static int check_call(struct verifier_env *env, int func_id)  			fn->ret_type, func_id);  		return -EINVAL;  	} + +	if (map && map->map_type == BPF_MAP_TYPE_PROG_ARRAY && +	    func_id != BPF_FUNC_tail_call) +		/* prog_array map type needs extra care: +		 * only allow to pass it into bpf_tail_call() for now. +		 * bpf_map_delete_elem() can be allowed in the future, +		 * while bpf_map_update_elem() must only be done via syscall +		 */ +		return -EINVAL; + +	if (func_id == BPF_FUNC_tail_call && +	    map->map_type != BPF_MAP_TYPE_PROG_ARRAY) +		/* don't allow any other map type to be passed into +		 * bpf_tail_call() +		 */ +		return -EINVAL; +  	return 0;  } @@ -1675,6 +1692,8 @@ static int do_check(struct verifier_env *env)  			}  		} else if (class == BPF_STX) { +			enum bpf_reg_type dst_reg_type; +  			if (BPF_MODE(insn->code) == BPF_XADD) {  				err = check_xadd(env, insn);  				if (err) @@ -1683,11 +1702,6 @@ static int do_check(struct verifier_env *env)  				continue;  			} -			if (BPF_MODE(insn->code) != BPF_MEM || -			    insn->imm != 0) { -				verbose("BPF_STX uses reserved fields\n"); -				return -EINVAL; -			}  			/* check src1 operand */  			err = check_reg_arg(regs, insn->src_reg, SRC_OP);  			if (err) @@ -1697,6 +1711,8 @@ static int do_check(struct verifier_env *env)  			if (err)  				return err; +			dst_reg_type = regs[insn->dst_reg].type; +  			/* check that memory (dst_reg + off) is writeable */  			err = check_mem_access(env, insn->dst_reg, insn->off,  					       BPF_SIZE(insn->code), BPF_WRITE, @@ -1704,6 +1720,15 @@ static int do_check(struct verifier_env *env)  			if (err)  				return err; +			if (insn->imm == 0) { +				insn->imm = dst_reg_type; +			} else if (dst_reg_type != insn->imm && +				   (dst_reg_type == PTR_TO_CTX || +				    insn->imm == PTR_TO_CTX)) { +				verbose("same insn cannot be used with different pointers\n"); +				return -EINVAL; +			} +  		} else if (class == BPF_ST) {  			if (BPF_MODE(insn->code) != BPF_MEM ||  			    insn->src_reg != BPF_REG_0) { @@ -1822,12 +1847,18 @@ static int replace_map_fd_with_map_ptr(struct verifier_env *env)  	for (i = 0; i < insn_cnt; i++, insn++) {  		if (BPF_CLASS(insn->code) == BPF_LDX && -		    (BPF_MODE(insn->code) != BPF_MEM || -		     insn->imm != 0)) { +		    (BPF_MODE(insn->code) != BPF_MEM || insn->imm != 0)) {  			verbose("BPF_LDX uses reserved fields\n");  			return -EINVAL;  		} +		if (BPF_CLASS(insn->code) == BPF_STX && +		    ((BPF_MODE(insn->code) != BPF_MEM && +		      BPF_MODE(insn->code) != BPF_XADD) || insn->imm != 0)) { +			verbose("BPF_STX uses reserved fields\n"); +			return -EINVAL; +		} +  		if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {  			struct bpf_map *map;  			struct fd f; @@ -1950,12 +1981,17 @@ static int convert_ctx_accesses(struct verifier_env *env)  	struct bpf_prog *new_prog;  	u32 cnt;  	int i; +	enum bpf_access_type type;  	if (!env->prog->aux->ops->convert_ctx_access)  		return 0;  	for (i = 0; i < insn_cnt; i++, insn++) { -		if (insn->code != (BPF_LDX | BPF_MEM | BPF_W)) +		if (insn->code == (BPF_LDX | BPF_MEM | BPF_W)) +			type = BPF_READ; +		else if (insn->code == (BPF_STX | BPF_MEM | BPF_W)) +			type = BPF_WRITE; +		else  			continue;  		if (insn->imm != PTR_TO_CTX) { @@ -1965,7 +2001,7 @@ static int convert_ctx_accesses(struct verifier_env *env)  		}  		cnt = env->prog->aux->ops-> -			convert_ctx_access(insn->dst_reg, insn->src_reg, +			convert_ctx_access(type, insn->dst_reg, insn->src_reg,  					   insn->off, insn_buf);  		if (cnt == 0 || cnt >= ARRAY_SIZE(insn_buf)) {  			verbose("bpf verifier is misconfigured\n");  |