Skip to content

Commit c69431a

Browse files
lmbAlexei Starovoitov
authored andcommitted
bpf: verifier: Improve function state reallocation
Resizing and copying stack and reference tracking state currently does a lot of kfree / kmalloc when the size of the tracked set changes. The logic in copy_*_state and realloc_*_state is also hard to follow. Refactor this into two core functions. copy_array copies from a source into a destination. It avoids reallocation by taking the allocated size of the destination into account via ksize(). The function is essentially krealloc_array, with the difference that the contents of dst are not preserved. realloc_array changes the size of an array and zeroes newly allocated items. Contrary to krealloc both functions don't free the destination if the size is zero. Instead we rely on free_func_state to clean up. realloc_stack_state is renamed to grow_stack_state to better convey that it never shrinks the stack state. Signed-off-by: Lorenz Bauer <[email protected]> Signed-off-by: Alexei Starovoitov <[email protected]> Link: https://lore.kernel.org/bpf/[email protected]
1 parent b741596 commit c69431a

File tree

1 file changed

+101
-96
lines changed

1 file changed

+101
-96
lines changed

kernel/bpf/verifier.c

Lines changed: 101 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -737,81 +737,104 @@ static void print_verifier_state(struct bpf_verifier_env *env,
737737
verbose(env, "\n");
738738
}
739739

740-
#define COPY_STATE_FN(NAME, COUNT, FIELD, SIZE) \
741-
static int copy_##NAME##_state(struct bpf_func_state *dst, \
742-
const struct bpf_func_state *src) \
743-
{ \
744-
if (!src->FIELD) \
745-
return 0; \
746-
if (WARN_ON_ONCE(dst->COUNT < src->COUNT)) { \
747-
/* internal bug, make state invalid to reject the program */ \
748-
memset(dst, 0, sizeof(*dst)); \
749-
return -EFAULT; \
750-
} \
751-
memcpy(dst->FIELD, src->FIELD, \
752-
sizeof(*src->FIELD) * (src->COUNT / SIZE)); \
753-
return 0; \
754-
}
755-
/* copy_reference_state() */
756-
COPY_STATE_FN(reference, acquired_refs, refs, 1)
757-
/* copy_stack_state() */
758-
COPY_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
759-
#undef COPY_STATE_FN
760-
761-
#define REALLOC_STATE_FN(NAME, COUNT, FIELD, SIZE) \
762-
static int realloc_##NAME##_state(struct bpf_func_state *state, int size, \
763-
bool copy_old) \
764-
{ \
765-
u32 old_size = state->COUNT; \
766-
struct bpf_##NAME##_state *new_##FIELD; \
767-
int slot = size / SIZE; \
768-
\
769-
if (size <= old_size || !size) { \
770-
if (copy_old) \
771-
return 0; \
772-
state->COUNT = slot * SIZE; \
773-
if (!size && old_size) { \
774-
kfree(state->FIELD); \
775-
state->FIELD = NULL; \
776-
} \
777-
return 0; \
778-
} \
779-
new_##FIELD = kmalloc_array(slot, sizeof(struct bpf_##NAME##_state), \
780-
GFP_KERNEL); \
781-
if (!new_##FIELD) \
782-
return -ENOMEM; \
783-
if (copy_old) { \
784-
if (state->FIELD) \
785-
memcpy(new_##FIELD, state->FIELD, \
786-
sizeof(*new_##FIELD) * (old_size / SIZE)); \
787-
memset(new_##FIELD + old_size / SIZE, 0, \
788-
sizeof(*new_##FIELD) * (size - old_size) / SIZE); \
789-
} \
790-
state->COUNT = slot * SIZE; \
791-
kfree(state->FIELD); \
792-
state->FIELD = new_##FIELD; \
793-
return 0; \
794-
}
795-
/* realloc_reference_state() */
796-
REALLOC_STATE_FN(reference, acquired_refs, refs, 1)
797-
/* realloc_stack_state() */
798-
REALLOC_STATE_FN(stack, allocated_stack, stack, BPF_REG_SIZE)
799-
#undef REALLOC_STATE_FN
800-
801-
/* do_check() starts with zero-sized stack in struct bpf_verifier_state to
802-
* make it consume minimal amount of memory. check_stack_write() access from
803-
* the program calls into realloc_func_state() to grow the stack size.
804-
* Note there is a non-zero 'parent' pointer inside bpf_verifier_state
805-
* which realloc_stack_state() copies over. It points to previous
806-
* bpf_verifier_state which is never reallocated.
740+
/* copy array src of length n * size bytes to dst. dst is reallocated if it's too
741+
* small to hold src. This is different from krealloc since we don't want to preserve
742+
* the contents of dst.
743+
*
744+
* Leaves dst untouched if src is NULL or length is zero. Returns NULL if memory could
745+
* not be allocated.
807746
*/
808-
static int realloc_func_state(struct bpf_func_state *state, int stack_size,
809-
int refs_size, bool copy_old)
747+
static void *copy_array(void *dst, const void *src, size_t n, size_t size, gfp_t flags)
810748
{
811-
int err = realloc_reference_state(state, refs_size, copy_old);
812-
if (err)
813-
return err;
814-
return realloc_stack_state(state, stack_size, copy_old);
749+
size_t bytes;
750+
751+
if (ZERO_OR_NULL_PTR(src))
752+
goto out;
753+
754+
if (unlikely(check_mul_overflow(n, size, &bytes)))
755+
return NULL;
756+
757+
if (ksize(dst) < bytes) {
758+
kfree(dst);
759+
dst = kmalloc_track_caller(bytes, flags);
760+
if (!dst)
761+
return NULL;
762+
}
763+
764+
memcpy(dst, src, bytes);
765+
out:
766+
return dst ? dst : ZERO_SIZE_PTR;
767+
}
768+
769+
/* resize an array from old_n items to new_n items. the array is reallocated if it's too
770+
* small to hold new_n items. new items are zeroed out if the array grows.
771+
*
772+
* Contrary to krealloc_array, does not free arr if new_n is zero.
773+
*/
774+
static void *realloc_array(void *arr, size_t old_n, size_t new_n, size_t size)
775+
{
776+
if (!new_n || old_n == new_n)
777+
goto out;
778+
779+
arr = krealloc_array(arr, new_n, size, GFP_KERNEL);
780+
if (!arr)
781+
return NULL;
782+
783+
if (new_n > old_n)
784+
memset(arr + old_n * size, 0, (new_n - old_n) * size);
785+
786+
out:
787+
return arr ? arr : ZERO_SIZE_PTR;
788+
}
789+
790+
static int copy_reference_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
791+
{
792+
dst->refs = copy_array(dst->refs, src->refs, src->acquired_refs,
793+
sizeof(struct bpf_reference_state), GFP_KERNEL);
794+
if (!dst->refs)
795+
return -ENOMEM;
796+
797+
dst->acquired_refs = src->acquired_refs;
798+
return 0;
799+
}
800+
801+
static int copy_stack_state(struct bpf_func_state *dst, const struct bpf_func_state *src)
802+
{
803+
size_t n = src->allocated_stack / BPF_REG_SIZE;
804+
805+
dst->stack = copy_array(dst->stack, src->stack, n, sizeof(struct bpf_stack_state),
806+
GFP_KERNEL);
807+
if (!dst->stack)
808+
return -ENOMEM;
809+
810+
dst->allocated_stack = src->allocated_stack;
811+
return 0;
812+
}
813+
814+
static int resize_reference_state(struct bpf_func_state *state, size_t n)
815+
{
816+
state->refs = realloc_array(state->refs, state->acquired_refs, n,
817+
sizeof(struct bpf_reference_state));
818+
if (!state->refs)
819+
return -ENOMEM;
820+
821+
state->acquired_refs = n;
822+
return 0;
823+
}
824+
825+
static int grow_stack_state(struct bpf_func_state *state, int size)
826+
{
827+
size_t old_n = state->allocated_stack / BPF_REG_SIZE, n = size / BPF_REG_SIZE;
828+
829+
if (old_n >= n)
830+
return 0;
831+
832+
state->stack = realloc_array(state->stack, old_n, n, sizeof(struct bpf_stack_state));
833+
if (!state->stack)
834+
return -ENOMEM;
835+
836+
state->allocated_stack = size;
837+
return 0;
815838
}
816839

817840
/* Acquire a pointer id from the env and update the state->refs to include
@@ -825,7 +848,7 @@ static int acquire_reference_state(struct bpf_verifier_env *env, int insn_idx)
825848
int new_ofs = state->acquired_refs;
826849
int id, err;
827850

828-
err = realloc_reference_state(state, state->acquired_refs + 1, true);
851+
err = resize_reference_state(state, state->acquired_refs + 1);
829852
if (err)
830853
return err;
831854
id = ++env->id_gen;
@@ -854,18 +877,6 @@ static int release_reference_state(struct bpf_func_state *state, int ptr_id)
854877
return -EINVAL;
855878
}
856879

857-
static int transfer_reference_state(struct bpf_func_state *dst,
858-
struct bpf_func_state *src)
859-
{
860-
int err = realloc_reference_state(dst, src->acquired_refs, false);
861-
if (err)
862-
return err;
863-
err = copy_reference_state(dst, src);
864-
if (err)
865-
return err;
866-
return 0;
867-
}
868-
869880
static void free_func_state(struct bpf_func_state *state)
870881
{
871882
if (!state)
@@ -904,10 +915,6 @@ static int copy_func_state(struct bpf_func_state *dst,
904915
{
905916
int err;
906917

907-
err = realloc_func_state(dst, src->allocated_stack, src->acquired_refs,
908-
false);
909-
if (err)
910-
return err;
911918
memcpy(dst, src, offsetof(struct bpf_func_state, acquired_refs));
912919
err = copy_reference_state(dst, src);
913920
if (err)
@@ -2590,8 +2597,7 @@ static int check_stack_write_fixed_off(struct bpf_verifier_env *env,
25902597
u32 dst_reg = env->prog->insnsi[insn_idx].dst_reg;
25912598
struct bpf_reg_state *reg = NULL;
25922599

2593-
err = realloc_func_state(state, round_up(slot + 1, BPF_REG_SIZE),
2594-
state->acquired_refs, true);
2600+
err = grow_stack_state(state, round_up(slot + 1, BPF_REG_SIZE));
25952601
if (err)
25962602
return err;
25972603
/* caller checked that off % size == 0 and -MAX_BPF_STACK <= off < 0,
@@ -2753,8 +2759,7 @@ static int check_stack_write_var_off(struct bpf_verifier_env *env,
27532759
if (value_reg && register_is_null(value_reg))
27542760
writing_zero = true;
27552761

2756-
err = realloc_func_state(state, round_up(-min_off, BPF_REG_SIZE),
2757-
state->acquired_refs, true);
2762+
err = grow_stack_state(state, round_up(-min_off, BPF_REG_SIZE));
27582763
if (err)
27592764
return err;
27602765

@@ -5629,7 +5634,7 @@ static int __check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn
56295634
subprog /* subprog number within this prog */);
56305635

56315636
/* Transfer references to the callee */
5632-
err = transfer_reference_state(callee, caller);
5637+
err = copy_reference_state(callee, caller);
56335638
if (err)
56345639
return err;
56355640

@@ -5780,7 +5785,7 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
57805785
}
57815786

57825787
/* Transfer references to the caller */
5783-
err = transfer_reference_state(caller, callee);
5788+
err = copy_reference_state(caller, callee);
57845789
if (err)
57855790
return err;
57865791

0 commit comments

Comments
 (0)