Skip to content

Commit d3bd741

Browse files
borkmannAlexei Starovoitov
authored andcommitted
bpf: fix sanitation of alu op with pointer / scalar type from different paths
While 979d63d ("bpf: prevent out of bounds speculation on pointer arithmetic") took care of rejecting alu op on pointer when e.g. pointer came from two different map values with different map properties such as value size, Jann reported that a case was not covered yet when a given alu op is used in both "ptr_reg += reg" and "numeric_reg += reg" from different branches where we would incorrectly try to sanitize based on the pointer's limit. Catch this corner case and reject the program instead. Fixes: 979d63d ("bpf: prevent out of bounds speculation on pointer arithmetic") Reported-by: Jann Horn <jannh@google.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org>
1 parent 466f89e commit d3bd741

File tree

2 files changed

+49
-13
lines changed

2 files changed

+49
-13
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ struct bpf_verifier_state_list {
172172
#define BPF_ALU_SANITIZE_SRC 1U
173173
#define BPF_ALU_SANITIZE_DST 2U
174174
#define BPF_ALU_NEG_VALUE (1U << 2)
175+
#define BPF_ALU_NON_POINTER (1U << 3)
175176
#define BPF_ALU_SANITIZE (BPF_ALU_SANITIZE_SRC | \
176177
BPF_ALU_SANITIZE_DST)
177178

kernel/bpf/verifier.c

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3103,6 +3103,40 @@ static int retrieve_ptr_limit(const struct bpf_reg_state *ptr_reg,
31033103
}
31043104
}
31053105

3106+
static bool can_skip_alu_sanitation(const struct bpf_verifier_env *env,
3107+
const struct bpf_insn *insn)
3108+
{
3109+
return env->allow_ptr_leaks || BPF_SRC(insn->code) == BPF_K;
3110+
}
3111+
3112+
static int update_alu_sanitation_state(struct bpf_insn_aux_data *aux,
3113+
u32 alu_state, u32 alu_limit)
3114+
{
3115+
/* If we arrived here from different branches with different
3116+
* state or limits to sanitize, then this won't work.
3117+
*/
3118+
if (aux->alu_state &&
3119+
(aux->alu_state != alu_state ||
3120+
aux->alu_limit != alu_limit))
3121+
return -EACCES;
3122+
3123+
/* Corresponding fixup done in fixup_bpf_calls(). */
3124+
aux->alu_state = alu_state;
3125+
aux->alu_limit = alu_limit;
3126+
return 0;
3127+
}
3128+
3129+
static int sanitize_val_alu(struct bpf_verifier_env *env,
3130+
struct bpf_insn *insn)
3131+
{
3132+
struct bpf_insn_aux_data *aux = cur_aux(env);
3133+
3134+
if (can_skip_alu_sanitation(env, insn))
3135+
return 0;
3136+
3137+
return update_alu_sanitation_state(aux, BPF_ALU_NON_POINTER, 0);
3138+
}
3139+
31063140
static int sanitize_ptr_alu(struct bpf_verifier_env *env,
31073141
struct bpf_insn *insn,
31083142
const struct bpf_reg_state *ptr_reg,
@@ -3117,7 +3151,7 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
31173151
struct bpf_reg_state tmp;
31183152
bool ret;
31193153

3120-
if (env->allow_ptr_leaks || BPF_SRC(insn->code) == BPF_K)
3154+
if (can_skip_alu_sanitation(env, insn))
31213155
return 0;
31223156

31233157
/* We already marked aux for masking from non-speculative
@@ -3133,19 +3167,8 @@ static int sanitize_ptr_alu(struct bpf_verifier_env *env,
31333167

31343168
if (retrieve_ptr_limit(ptr_reg, &alu_limit, opcode, off_is_neg))
31353169
return 0;
3136-
3137-
/* If we arrived here from different branches with different
3138-
* limits to sanitize, then this won't work.
3139-
*/
3140-
if (aux->alu_state &&
3141-
(aux->alu_state != alu_state ||
3142-
aux->alu_limit != alu_limit))
3170+
if (update_alu_sanitation_state(aux, alu_state, alu_limit))
31433171
return -EACCES;
3144-
3145-
/* Corresponding fixup done in fixup_bpf_calls(). */
3146-
aux->alu_state = alu_state;
3147-
aux->alu_limit = alu_limit;
3148-
31493172
do_sim:
31503173
/* Simulate and find potential out-of-bounds access under
31513174
* speculative execution from truncation as a result of
@@ -3418,6 +3441,8 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
34183441
s64 smin_val, smax_val;
34193442
u64 umin_val, umax_val;
34203443
u64 insn_bitness = (BPF_CLASS(insn->code) == BPF_ALU64) ? 64 : 32;
3444+
u32 dst = insn->dst_reg;
3445+
int ret;
34213446

34223447
if (insn_bitness == 32) {
34233448
/* Relevant for 32-bit RSH: Information can propagate towards
@@ -3452,6 +3477,11 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
34523477

34533478
switch (opcode) {
34543479
case BPF_ADD:
3480+
ret = sanitize_val_alu(env, insn);
3481+
if (ret < 0) {
3482+
verbose(env, "R%d tried to add from different pointers or scalars\n", dst);
3483+
return ret;
3484+
}
34553485
if (signed_add_overflows(dst_reg->smin_value, smin_val) ||
34563486
signed_add_overflows(dst_reg->smax_value, smax_val)) {
34573487
dst_reg->smin_value = S64_MIN;
@@ -3471,6 +3501,11 @@ static int adjust_scalar_min_max_vals(struct bpf_verifier_env *env,
34713501
dst_reg->var_off = tnum_add(dst_reg->var_off, src_reg.var_off);
34723502
break;
34733503
case BPF_SUB:
3504+
ret = sanitize_val_alu(env, insn);
3505+
if (ret < 0) {
3506+
verbose(env, "R%d tried to sub from different pointers or scalars\n", dst);
3507+
return ret;
3508+
}
34743509
if (signed_sub_overflows(dst_reg->smin_value, smax_val) ||
34753510
signed_sub_overflows(dst_reg->smax_value, smin_val)) {
34763511
/* Overflow possible, we know nothing */

0 commit comments

Comments
 (0)