bpf: Macrofy stack state copy

An upcoming commit will need very similar copy/realloc boilerplate, so
refactor the existing stack copy/realloc functions into macros to
simplify it.

Signed-off-by: Joe Stringer <joe@wand.net.nz>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
Joe Stringer 2018-10-02 13:35:34 -07:00 committed by Daniel Borkmann
parent c64b798328
commit 84dbf35073
1 changed files with 59 additions and 45 deletions

View File

@ -388,60 +388,74 @@ static void print_verifier_state(struct bpf_verifier_env *env,
verbose(env, "\n");
}
static int copy_stack_state(struct bpf_func_state *dst,
const struct bpf_func_state *src)
{
if (!src->stack)
return 0;
if (WARN_ON_ONCE(dst->allocated_stack < src->allocated_stack)) {
/* internal bug, make state invalid to reject the program */
memset(dst, 0, sizeof(*dst));
return -EFAULT;
}
memcpy(dst->stack, src->stack,
sizeof(*src->stack) * (src->allocated_stack / BPF_REG_SIZE));
return 0;
#define COPY_STATE_FN(NAME, COUNT, FIELD, SIZE) \
static int copy_##NAME##_state(struct bpf_func_state *dst, \
const struct bpf_func_state *src) \
{ \
if (!src->FIELD) \
return 0; \
if (WARN_ON_ONCE(dst->COUNT < src->COUNT)) { \
/* internal bug, make state invalid to reject the program */ \
memset(dst, 0, sizeof(*dst)); \
return -EFAULT; \
} \
memcpy(dst->FIELD, src->FIELD, \
sizeof(*src->FIELD) * (src->COUNT / SIZE)); \
return 0; \
}
/* copy_stack_state() */
COPY_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
#undef COPY_STATE_FN
#define REALLOC_STATE_FN(NAME, COUNT, FIELD, SIZE) \
static int realloc_##NAME##_state(struct bpf_func_state *state, int size, \
bool copy_old) \
{ \
u32 old_size = state->COUNT; \
struct bpf_##NAME##_state *new_##FIELD; \
int slot = size / SIZE; \
\
if (size <= old_size || !size) { \
if (copy_old) \
return 0; \
state->COUNT = slot * SIZE; \
if (!size && old_size) { \
kfree(state->FIELD); \
state->FIELD = NULL; \
} \
return 0; \
} \
new_##FIELD = kmalloc_array(slot, sizeof(struct bpf_##NAME##_state), \
GFP_KERNEL); \
if (!new_##FIELD) \
return -ENOMEM; \
if (copy_old) { \
if (state->FIELD) \
memcpy(new_##FIELD, state->FIELD, \
sizeof(*new_##FIELD) * (old_size / SIZE)); \
memset(new_##FIELD + old_size / SIZE, 0, \
sizeof(*new_##FIELD) * (size - old_size) / SIZE); \
} \
state->COUNT = slot * SIZE; \
kfree(state->FIELD); \
state->FIELD = new_##FIELD; \
return 0; \
}
/* realloc_stack_state() */
REALLOC_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
#undef REALLOC_STATE_FN
/* do_check() starts with zero-sized stack in struct bpf_verifier_state to
* make it consume minimal amount of memory. check_stack_write() access from
* the program calls into realloc_func_state() to grow the stack size.
* Note there is a non-zero parent pointer inside each reg of bpf_verifier_state
* which this function copies over. It points to corresponding reg in previous
* bpf_verifier_state which is never reallocated
* Note there is a non-zero 'parent' pointer inside bpf_verifier_state
* which realloc_stack_state() copies over. It points to previous
* bpf_verifier_state which is never reallocated.
*/
static int realloc_func_state(struct bpf_func_state *state, int size,
bool copy_old)
{
u32 old_size = state->allocated_stack;
struct bpf_stack_state *new_stack;
int slot = size / BPF_REG_SIZE;
if (size <= old_size || !size) {
if (copy_old)
return 0;
state->allocated_stack = slot * BPF_REG_SIZE;
if (!size && old_size) {
kfree(state->stack);
state->stack = NULL;
}
return 0;
}
new_stack = kmalloc_array(slot, sizeof(struct bpf_stack_state),
GFP_KERNEL);
if (!new_stack)
return -ENOMEM;
if (copy_old) {
if (state->stack)
memcpy(new_stack, state->stack,
sizeof(*new_stack) * (old_size / BPF_REG_SIZE));
memset(new_stack + old_size / BPF_REG_SIZE, 0,
sizeof(*new_stack) * (size - old_size) / BPF_REG_SIZE);
}
state->allocated_stack = slot * BPF_REG_SIZE;
kfree(state->stack);
state->stack = new_stack;
return 0;
return realloc_stack_state(state, size, copy_old);
}
static void free_func_state(struct bpf_func_state *state)