Skip to content

Commit b8d6ce0

Browse files
committed
bpf: Clarify sanitize_check_bounds()
As is, it appears as if ptr alu is allowed for everything except PTR_TO_{STACK,MAP_VALUE} if one only looks at sanitize_check_bounds(). However, this is misleading as the function only works together with retrieve_ptr_limit() and the two must be kept in sync. This patch documents the interdependency and adds a check to ensure they stay in sync. Because the preceeding switch returns -EACCES for every opcode except for ADD/SUB, the sanitize_needed() following the sanitize_check_bounds() call is always true if reached. This means, unless sanitize_check_bounds() detected that the pointer goes OOB because of the ADD/SUB (and returns -EACCES), sanitize_ptr_alu() always executes after sanitize_check_bounds(). In sanitize_ptr_alu(commit_window = true), we always run retrieve_ptr_limit() unless: * can_skip_alu_sanititation() is true, noteably `BPF_SRC(insn->code) == BPF_K`. BPF_K is fine because it means that there is no scalar register (which could be subject to spec. scalar confusion due to Spectre v4) that goes into the alu op. The ptr reg can not be subject to v4-based value confusion due to the nospec added. Thus, in this case it would have been fine to also skip sanitize_check_bounds(). * We are on a speculative path (`vstate->speculative`). In the second "commit" phase, sanitize_ptr_alu() always just returns 0. This makes sense because there are no alu sanitization limits to be learned from speculative paths. Furthermore, because the sanitization will ensure that ptr arith stays in (architectural) bounds, the sanitize_check_bounds() on the spec. path could also be skipped. TODO, through case two, is it fine that we allow alu with other ptr type (non-stack/ap) on spec path retrieve_ptr_limit() only allows the ALU op if the involved ptr reg (can be either src or dst for ADD) is PTR_TO_STACK or PTR_TO_MAP_VALUE. Otherwise it returns -EOPNOTSUPP. In summary, sanitize_check_bounds() returning 0 for non-PTR_TO_{STACK,MAP_VALUE} is fine because retrieve_ptr_limit() also runs for all relevant cases and prevents unsafe ops. We therefore allow unsanitized ptr arith with 64-bit ADD/SUB for * ptr -=/+= imm32, i.e. `BPF_SRC(insn->code) == BPF_K` * PTR_TO_{STACK,MAP_VALUE} -= scalar * PTR_TO_{STACK,MAP_VALUE} += scalar * scalar += PTR_TO_{STACK,MAP_VALUE} if the requirements from retrieve_ptr_limit() AND sanitize_check_bounds() hold. To document this interdependency between sanitize_check_bounds() and retrieve_ptr_limit(), add a verifier_bug_if() to make sure they stay in sync. Further background info: * There are two calls to sanitize_ptr_alu(), on the first call we spawn the path to simulate truncation and TODO, why spawn the path before simulating the alu op TODO, why two calls to sanitize_ptr_alu Signed-off-by: Luis Gerhorst <[email protected]>
1 parent 5ead949 commit b8d6ce0

File tree

1 file changed

+14
-4
lines changed

1 file changed

+14
-4
lines changed

kernel/bpf/verifier.c

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14297,7 +14297,7 @@ static int sanitize_check_bounds(struct bpf_verifier_env *env,
1429714297
}
1429814298
break;
1429914299
default:
14300-
break;
14300+
return -EOPNOTSUPP;
1430114301
}
1430214302

1430314303
return 0;
@@ -14324,7 +14324,7 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
1432414324
struct bpf_sanitize_info info = {};
1432514325
u8 opcode = BPF_OP(insn->code);
1432614326
u32 dst = insn->dst_reg;
14327-
int ret;
14327+
int ret, bounds_ret;
1432814328

1432914329
dst_reg = &regs[dst];
1433014330

@@ -14524,11 +14524,21 @@ static int adjust_ptr_min_max_vals(struct bpf_verifier_env *env,
1452414524
if (!check_reg_sane_offset(env, dst_reg, ptr_reg->type))
1452514525
return -EINVAL;
1452614526
reg_bounds_sync(dst_reg);
14527-
if (sanitize_check_bounds(env, insn, dst_reg) < 0)
14528-
return -EACCES;
14527+
bounds_ret = sanitize_check_bounds(env, insn, dst_reg);
14528+
if (bounds_ret == -EACCES)
14529+
return bounds_ret;
14530+
if (verifier_bug_if(!sanitize_needed(opcode), env, "x55")) {
14531+
return -EFAULT;
14532+
}
1452914533
if (sanitize_needed(opcode)) {
1453014534
ret = sanitize_ptr_alu(env, insn, dst_reg, off_reg, dst_reg,
1453114535
&info, true);
14536+
if (verifier_bug_if(!can_skip_alu_sanitation(env, insn)
14537+
&& !env->cur_state->speculative /* TODO: ok? */
14538+
&& bounds_ret == -EOPNOTSUPP && !ret,
14539+
env, "Pointer type unsupported by sanitize_check_bounds() not rejected by retrieve_ptr_limit() as required")) {
14540+
return -EFAULT;
14541+
}
1453214542
if (ret < 0)
1453314543
return sanitize_err(env, insn, ret, off_reg, dst_reg);
1453414544
}

0 commit comments

Comments
 (0)