Skip to content

Commit

Permalink
bpf: Simplify checking size of helper accesses
Browse files Browse the repository at this point in the history
This patch simplifies the verification of size arguments associated to
pointer arguments to helpers and kfuncs. Many helpers take a pointer
argument followed by the size of the memory access performed to be
performed through that pointer. Before this patch, the handling of the
size argument in check_mem_size_reg() was confusing and wasteful: if the
size register's lower bound was 0, then the verification was done twice:
once considering the size of the access to be the lower-bound of the
respective argument, and once considering the upper bound (even if the
two are the same). The upper bound checking is a super-set of the
lower-bound checking(*), except: the only point of the lower-bound check
is to handle the case where zero-sized-accesses are explicitly not
allowed and the lower-bound is zero. This static condition is now
checked explicitly, replacing a much more complex, expensive and
confusing verification call to check_helper_mem_access().

Error messages change in this patch. Before, messages about illegal
zero-size accesses depended on the type of the pointer and on other
conditions, and sometimes the message was plain wrong: in some tests
that changed you'll see that the old message was something like "R1 min
value is outside of the allowed memory range", where R1 is the pointer
register; the error was wrongly claiming that the pointer was bad
instead of the size being bad. Other times the information that the size
came for a register with a possible range of values was wrong, and the
error presented the size as a fixed zero. Now the errors refer to the
right register. However, the old error messages did contain useful
information about the pointer register which is now lost; recovering
this information was deemed not important enough.

(*) Besides standing to reason that the checks for a bigger size access
are a super-set of the checks for a smaller size access, I have also
mechanically verified this by reading the code for all types of
pointers. I could convince myself that it's true for all but
PTR_TO_BTF_ID (check_ptr_to_btf_access). There, simply looking
line-by-line does not immediately prove what we want. If anyone has any
qualms, let me know.

Signed-off-by: Andrei Matei <andreimatei1@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Acked-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20231221232225.568730-2-andreimatei1@gmail.com
  • Loading branch information
Andrei Matei authored and Andrii Nakryiko committed Jan 3, 2024
1 parent 2ab1efa commit 8a021e7
Show file tree
Hide file tree
Showing 3 changed files with 9 additions and 11 deletions.
10 changes: 4 additions & 6 deletions kernel/bpf/verifier.c
Original file line number Diff line number Diff line change
Expand Up @@ -7279,12 +7279,10 @@ static int check_mem_size_reg(struct bpf_verifier_env *env,
return -EACCES;
}

if (reg->umin_value == 0) {
err = check_helper_mem_access(env, regno - 1, 0,
zero_size_allowed,
meta);
if (err)
return err;
if (reg->umin_value == 0 && !zero_size_allowed) {
verbose(env, "R%d invalid zero-sized read: u64=[%lld,%lld]\n",
regno, reg->umin_value, reg->umax_value);
return -EACCES;
}

if (reg->umax_value >= BPF_MAX_VAR_SIZ) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ l0_%=: exit; \

SEC("tracepoint")
__description("helper access to map: empty range")
__failure __msg("invalid access to map value, value_size=48 off=0 size=0")
__failure __msg("R2 invalid zero-sized read")
__naked void access_to_map_empty_range(void)
{
asm volatile (" \
Expand Down Expand Up @@ -221,7 +221,7 @@ l0_%=: exit; \

SEC("tracepoint")
__description("helper access to adjusted map (via const imm): empty range")
__failure __msg("invalid access to map value, value_size=48 off=4 size=0")
__failure __msg("R2 invalid zero-sized read")
__naked void via_const_imm_empty_range(void)
{
asm volatile (" \
Expand Down Expand Up @@ -386,7 +386,7 @@ l0_%=: exit; \

SEC("tracepoint")
__description("helper access to adjusted map (via const reg): empty range")
__failure __msg("R1 min value is outside of the allowed memory range")
__failure __msg("R2 invalid zero-sized read")
__naked void via_const_reg_empty_range(void)
{
asm volatile (" \
Expand Down Expand Up @@ -556,7 +556,7 @@ l0_%=: exit; \

SEC("tracepoint")
__description("helper access to adjusted map (via variable): empty range")
__failure __msg("R1 min value is outside of the allowed memory range")
__failure __msg("R2 invalid zero-sized read")
__naked void map_via_variable_empty_range(void)
{
asm volatile (" \
Expand Down
2 changes: 1 addition & 1 deletion tools/testing/selftests/bpf/progs/verifier_raw_stack.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ __naked void load_bytes_negative_len_2(void)

SEC("tc")
__description("raw_stack: skb_load_bytes, zero len")
__failure __msg("invalid zero-sized read")
__failure __msg("R4 invalid zero-sized read: u64=[0,0]")
__naked void skb_load_bytes_zero_len(void)
{
asm volatile (" \
Expand Down

0 comments on commit 8a021e7

Please sign in to comment.