Commit 8e2fe1d9 authored by Daniel Borkmann's avatar Daniel Borkmann Committed by David S. Miller

bpf: add new arg_type that allows for 0 sized stack buffer

Currently, when we pass a buffer from the eBPF stack into a helper
function, the function proto indicates argument types as ARG_PTR_TO_STACK
and ARG_CONST_STACK_SIZE pair. If R<X> contains the former, then R<X+1>
must be of the latter type. Then, verifier checks whether the buffer
points into eBPF stack, is initialized, etc. The verifier also guarantees
that the constant value passed in R<X+1> is greater than 0, so helper
functions don't need to test for it and can always assume a non-NULL
initialized buffer as well as non-0 buffer size.

This patch adds a new argument types ARG_CONST_STACK_SIZE_OR_ZERO that
allows to also pass NULL as R<X> and 0 as R<X+1> into the helper function.
Such helper functions, of course, need to be able to handle these cases
internally then. Verifier guarantees that either R<X> == NULL && R<X+1> == 0
or R<X> != NULL && R<X+1> != 0 (like the case of ARG_CONST_STACK_SIZE), any
other combinations are not possible to load.

I went through various options of extending the verifier, and introducing
the type ARG_CONST_STACK_SIZE_OR_ZERO seems to have most minimal changes
needed to the verifier.
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8b393f83
...@@ -65,6 +65,7 @@ enum bpf_arg_type { ...@@ -65,6 +65,7 @@ enum bpf_arg_type {
*/ */
ARG_PTR_TO_STACK, /* any pointer to eBPF program stack */ ARG_PTR_TO_STACK, /* any pointer to eBPF program stack */
ARG_CONST_STACK_SIZE, /* number of bytes accessed from stack */ ARG_CONST_STACK_SIZE, /* number of bytes accessed from stack */
ARG_CONST_STACK_SIZE_OR_ZERO, /* number of bytes accessed from stack or 0 */
ARG_PTR_TO_CTX, /* pointer to context */ ARG_PTR_TO_CTX, /* pointer to context */
ARG_ANYTHING, /* any (initialized) argument is ok */ ARG_ANYTHING, /* any (initialized) argument is ok */
......
...@@ -779,15 +779,24 @@ static int check_xadd(struct verifier_env *env, struct bpf_insn *insn) ...@@ -779,15 +779,24 @@ static int check_xadd(struct verifier_env *env, struct bpf_insn *insn)
* bytes from that pointer, make sure that it's within stack boundary * bytes from that pointer, make sure that it's within stack boundary
* and all elements of stack are initialized * and all elements of stack are initialized
*/ */
static int check_stack_boundary(struct verifier_env *env, static int check_stack_boundary(struct verifier_env *env, int regno,
int regno, int access_size) int access_size, bool zero_size_allowed)
{ {
struct verifier_state *state = &env->cur_state; struct verifier_state *state = &env->cur_state;
struct reg_state *regs = state->regs; struct reg_state *regs = state->regs;
int off, i; int off, i;
if (regs[regno].type != PTR_TO_STACK) if (regs[regno].type != PTR_TO_STACK) {
if (zero_size_allowed && access_size == 0 &&
regs[regno].type == CONST_IMM &&
regs[regno].imm == 0)
return 0;
verbose("R%d type=%s expected=%s\n", regno,
reg_type_str[regs[regno].type],
reg_type_str[PTR_TO_STACK]);
return -EACCES; return -EACCES;
}
off = regs[regno].imm; off = regs[regno].imm;
if (off >= 0 || off < -MAX_BPF_STACK || off + access_size > 0 || if (off >= 0 || off < -MAX_BPF_STACK || off + access_size > 0 ||
...@@ -830,15 +839,24 @@ static int check_func_arg(struct verifier_env *env, u32 regno, ...@@ -830,15 +839,24 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
return 0; return 0;
} }
if (arg_type == ARG_PTR_TO_STACK || arg_type == ARG_PTR_TO_MAP_KEY || if (arg_type == ARG_PTR_TO_MAP_KEY ||
arg_type == ARG_PTR_TO_MAP_VALUE) { arg_type == ARG_PTR_TO_MAP_VALUE) {
expected_type = PTR_TO_STACK; expected_type = PTR_TO_STACK;
} else if (arg_type == ARG_CONST_STACK_SIZE) { } else if (arg_type == ARG_CONST_STACK_SIZE ||
arg_type == ARG_CONST_STACK_SIZE_OR_ZERO) {
expected_type = CONST_IMM; expected_type = CONST_IMM;
} else if (arg_type == ARG_CONST_MAP_PTR) { } else if (arg_type == ARG_CONST_MAP_PTR) {
expected_type = CONST_PTR_TO_MAP; expected_type = CONST_PTR_TO_MAP;
} else if (arg_type == ARG_PTR_TO_CTX) { } else if (arg_type == ARG_PTR_TO_CTX) {
expected_type = PTR_TO_CTX; expected_type = PTR_TO_CTX;
} else if (arg_type == ARG_PTR_TO_STACK) {
expected_type = PTR_TO_STACK;
/* One exception here. In case function allows for NULL to be
* passed in as argument, it's a CONST_IMM type. Final test
* happens during stack boundary checking.
*/
if (reg->type == CONST_IMM && reg->imm == 0)
expected_type = CONST_IMM;
} else { } else {
verbose("unsupported arg_type %d\n", arg_type); verbose("unsupported arg_type %d\n", arg_type);
return -EFAULT; return -EFAULT;
...@@ -868,8 +886,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno, ...@@ -868,8 +886,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
verbose("invalid map_ptr to access map->key\n"); verbose("invalid map_ptr to access map->key\n");
return -EACCES; return -EACCES;
} }
err = check_stack_boundary(env, regno, (*mapp)->key_size); err = check_stack_boundary(env, regno, (*mapp)->key_size,
false);
} else if (arg_type == ARG_PTR_TO_MAP_VALUE) { } else if (arg_type == ARG_PTR_TO_MAP_VALUE) {
/* bpf_map_xxx(..., map_ptr, ..., value) call: /* bpf_map_xxx(..., map_ptr, ..., value) call:
* check [value, value + map->value_size) validity * check [value, value + map->value_size) validity
...@@ -879,9 +897,12 @@ static int check_func_arg(struct verifier_env *env, u32 regno, ...@@ -879,9 +897,12 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
verbose("invalid map_ptr to access map->value\n"); verbose("invalid map_ptr to access map->value\n");
return -EACCES; return -EACCES;
} }
err = check_stack_boundary(env, regno, (*mapp)->value_size); err = check_stack_boundary(env, regno, (*mapp)->value_size,
false);
} else if (arg_type == ARG_CONST_STACK_SIZE ||
arg_type == ARG_CONST_STACK_SIZE_OR_ZERO) {
bool zero_size_allowed = (arg_type == ARG_CONST_STACK_SIZE_OR_ZERO);
} else if (arg_type == ARG_CONST_STACK_SIZE) {
/* bpf_xxx(..., buf, len) call will access 'len' bytes /* bpf_xxx(..., buf, len) call will access 'len' bytes
* from stack pointer 'buf'. Check it * from stack pointer 'buf'. Check it
* note: regno == len, regno - 1 == buf * note: regno == len, regno - 1 == buf
...@@ -891,7 +912,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno, ...@@ -891,7 +912,8 @@ static int check_func_arg(struct verifier_env *env, u32 regno,
verbose("ARG_CONST_STACK_SIZE cannot be first argument\n"); verbose("ARG_CONST_STACK_SIZE cannot be first argument\n");
return -EACCES; return -EACCES;
} }
err = check_stack_boundary(env, regno - 1, reg->imm); err = check_stack_boundary(env, regno - 1, reg->imm,
zero_size_allowed);
} }
return err; return err;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment