diff options
| -rw-r--r-- | include/linux/bpf_verifier.h | 2 | ||||
| -rw-r--r-- | kernel/bpf/verifier.c | 27 | 
2 files changed, 28 insertions, 1 deletions
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h index b61482d354a2..c561b986bab0 100644 --- a/include/linux/bpf_verifier.h +++ b/include/linux/bpf_verifier.h @@ -115,7 +115,7 @@ struct bpf_insn_aux_data {  		struct bpf_map *map_ptr;	/* pointer for call insn into lookup_elem */  	};  	int ctx_field_size; /* the ctx field size for load insn, maybe 0 */ -	int converted_op_size; /* the valid value width after perceived conversion */ +	bool seen; /* this insn was processed by the verifier */  };  #define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */ diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 308b0638ec5d..d4593571c404 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -3827,6 +3827,7 @@ static int do_check(struct bpf_verifier_env *env)  			return err;  		regs = cur_regs(env); +		env->insn_aux_data[insn_idx].seen = true;  		if (class == BPF_ALU || class == BPF_ALU64) {  			err = check_alu_op(env, insn);  			if (err) @@ -4022,6 +4023,7 @@ process_bpf_exit:  					return err;  				insn_idx++; +				env->insn_aux_data[insn_idx].seen = true;  			} else {  				verbose(env, "invalid BPF_LD mode\n");  				return -EINVAL; @@ -4204,6 +4206,7 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,  				u32 off, u32 cnt)  {  	struct bpf_insn_aux_data *new_data, *old_data = env->insn_aux_data; +	int i;  	if (cnt == 1)  		return 0; @@ -4213,6 +4216,8 @@ static int adjust_insn_aux_data(struct bpf_verifier_env *env, u32 prog_len,  	memcpy(new_data, old_data, sizeof(struct bpf_insn_aux_data) * off);  	memcpy(new_data + off + cnt - 1, old_data + off,  	       sizeof(struct bpf_insn_aux_data) * (prog_len - off - cnt + 1)); +	for (i = off; i < off + cnt - 1; i++) +		new_data[i].seen = true;  	env->insn_aux_data = new_data;  	vfree(old_data);  	return 0; @@ -4231,6 +4236,25 @@ static struct bpf_prog *bpf_patch_insn_data(struct bpf_verifier_env *env, u32 of  	return new_prog;  } +/* The verifier does more data flow analysis than llvm and will not explore + * branches that are dead at run time. Malicious programs can have dead code + * too. Therefore replace all dead at-run-time code with nops. + */ +static void sanitize_dead_code(struct bpf_verifier_env *env) +{ +	struct bpf_insn_aux_data *aux_data = env->insn_aux_data; +	struct bpf_insn nop = BPF_MOV64_REG(BPF_REG_0, BPF_REG_0); +	struct bpf_insn *insn = env->prog->insnsi; +	const int insn_cnt = env->prog->len; +	int i; + +	for (i = 0; i < insn_cnt; i++) { +		if (aux_data[i].seen) +			continue; +		memcpy(insn + i, &nop, sizeof(nop)); +	} +} +  /* convert load instructions that access fields of 'struct __sk_buff'   * into sequence of instructions that access fields of 'struct sk_buff'   */ @@ -4558,6 +4582,9 @@ skip_full_check:  	free_states(env);  	if (ret == 0) +		sanitize_dead_code(env); + +	if (ret == 0)  		/* program is valid, convert *(u32*)(ctx + off) accesses */  		ret = convert_ctx_accesses(env);  | 

