Merge branch 'bpf-fix-verification-of-indirect-var-off-stack-access'
Andrei Matei says: ==================== bpf: fix verification of indirect var-off stack access V4 to V5: - split the test into a separate patch V3 to V4: - include a test per Eduard's request - target bpf-next per Alexei's request (patches didn't change) V2 to V3: - simplify checks for max_off (don't call check_stack_slot_within_bounds for it) - append a commit to protect against overflow in the addition of the register and the offset V1 to V2: - fix max_off calculation for access size = 0 ==================== Link: https://lore.kernel.org/r/20231207041150.229139-1-andreimatei1@gmail.com Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
This commit is contained in:
commit
483af466e4
@ -6577,7 +6577,7 @@ static int check_ptr_to_map_access(struct bpf_verifier_env *env,
|
||||
* The minimum valid offset is -MAX_BPF_STACK for writes, and
|
||||
* -state->allocated_stack for reads.
|
||||
*/
|
||||
static int check_stack_slot_within_bounds(int off,
|
||||
static int check_stack_slot_within_bounds(s64 off,
|
||||
struct bpf_func_state *state,
|
||||
enum bpf_access_type t)
|
||||
{
|
||||
@ -6606,7 +6606,7 @@ static int check_stack_access_within_bounds(
|
||||
struct bpf_reg_state *regs = cur_regs(env);
|
||||
struct bpf_reg_state *reg = regs + regno;
|
||||
struct bpf_func_state *state = func(env, reg);
|
||||
int min_off, max_off;
|
||||
s64 min_off, max_off;
|
||||
int err;
|
||||
char *err_extra;
|
||||
|
||||
@ -6619,11 +6619,8 @@ static int check_stack_access_within_bounds(
|
||||
err_extra = " write to";
|
||||
|
||||
if (tnum_is_const(reg->var_off)) {
|
||||
min_off = reg->var_off.value + off;
|
||||
if (access_size > 0)
|
||||
max_off = min_off + access_size - 1;
|
||||
else
|
||||
max_off = min_off;
|
||||
min_off = (s64)reg->var_off.value + off;
|
||||
max_off = min_off + access_size;
|
||||
} else {
|
||||
if (reg->smax_value >= BPF_MAX_VAR_OFF ||
|
||||
reg->smin_value <= -BPF_MAX_VAR_OFF) {
|
||||
@ -6632,15 +6629,12 @@ static int check_stack_access_within_bounds(
|
||||
return -EACCES;
|
||||
}
|
||||
min_off = reg->smin_value + off;
|
||||
if (access_size > 0)
|
||||
max_off = reg->smax_value + off + access_size - 1;
|
||||
else
|
||||
max_off = min_off;
|
||||
max_off = reg->smax_value + off + access_size;
|
||||
}
|
||||
|
||||
err = check_stack_slot_within_bounds(min_off, state, type);
|
||||
if (!err)
|
||||
err = check_stack_slot_within_bounds(max_off, state, type);
|
||||
if (!err && max_off > 0)
|
||||
err = -EINVAL; /* out of stack access into non-negative offsets */
|
||||
|
||||
if (err) {
|
||||
if (tnum_is_const(reg->var_off)) {
|
||||
|
@ -224,6 +224,35 @@ __naked void access_max_out_of_bound(void)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
/* Similar to the test above, but this time check the special case of a
|
||||
* zero-sized stack access. We used to have a bug causing crashes for zero-sized
|
||||
* out-of-bounds accesses.
|
||||
*/
|
||||
SEC("socket")
|
||||
__description("indirect variable-offset stack access, zero-sized, max out of bound")
|
||||
__failure __msg("invalid variable-offset indirect access to stack R1")
|
||||
__naked void zero_sized_access_max_out_of_bound(void)
|
||||
{
|
||||
asm volatile (" \
|
||||
r0 = 0; \
|
||||
/* Fill some stack */ \
|
||||
*(u64*)(r10 - 16) = r0; \
|
||||
*(u64*)(r10 - 8) = r0; \
|
||||
/* Get an unknown value */ \
|
||||
r1 = *(u32*)(r1 + 0); \
|
||||
r1 &= 63; \
|
||||
r1 += -16; \
|
||||
/* r1 is now anywhere in [-16,48) */ \
|
||||
r1 += r10; \
|
||||
r2 = 0; \
|
||||
r3 = 0; \
|
||||
call %[bpf_probe_read_kernel]; \
|
||||
exit; \
|
||||
" :
|
||||
: __imm(bpf_probe_read_kernel)
|
||||
: __clobber_all);
|
||||
}
|
||||
|
||||
SEC("lwt_in")
|
||||
__description("indirect variable-offset stack access, min out of bound")
|
||||
__failure __msg("invalid variable-offset indirect access to stack R2")
|
||||
|
Loading…
x
Reference in New Issue
Block a user