On 30/10/17 21:51, Alexei Starovoitov wrote:
> the verifier got progressively smarter over time and size of its internal
> state grew as well. Time to reduce the memory consumption.
> 
> Before:
> sizeof(struct bpf_verifier_state) = 6520
> After:
> sizeof(struct bpf_verifier_state) = 896
Nice!

> It's done by observing that majority of BPF programs use little to
> no stack whereas verifier kept all of 512 stack slots ready always.
> Instead dynamically reallocate struct verifier state when stack
> access is detected.
> Besides memory savings such approach gives few % runtime perf
> improvement as well and small reduction in number of processed insns:
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This worries me.  Not that improving pruning performance is a bad thing,
 but I don't see anything in this patch that should affect it, which
 suggests maybe a bug has been introduced that prunes too much?

> +static void copy_verifier_state(struct bpf_verifier_state *dst,
> +                             const struct bpf_verifier_state *src)
> +{
> +     int stack = dst->allocated_stack;
> +
> +     if (stack < src->allocated_stack) {
> +             WARN_ON_ONCE(stack < src->allocated_stack);
Why not just
    if (WARN_ON_ONCE(stack < src->allocated_stack)) {
?

> +             /* internal bug, make state invalid to reject the program */
> +             memset(dst, 0, sizeof(*dst));
> +             return;
Any reason this function couldn't return int instead?

> +     }
> +     memcpy(dst, src, offsetof(struct bpf_verifier_state,
> +                               stack[src->allocated_stack / BPF_REG_SIZE]));
> +     dst->allocated_stack = stack;
> +}

Then again, I'm not entirely sure I like the idea of an array[0] at the
 end of the struct anyway (which is what makes this copy_verifier_state()
 necessary).  I'd be happier with a *array member pointing to a separate
 allocation; though that would still need separate copying in
 is_state_visited().  I also worry about a stale *parent somewhere
 pointing to the old state; I don't _think_ that can happen, but I'm not
 as sure of that as I'd like to be.  Using a *array instead would mean
 that &state doesn't change when we grow it.
Also, why kzalloc() a new state (in realloc_verifier_state()) rather than
 krealloc()ing the existing one and memset()ing the extra space?  That way
 you wouldn't need to then copy across all the old data.

>  static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx)
>  {
> -     struct bpf_verifier_stack_elem *elem;
> +     struct bpf_verifier_state *cur = env->cur_state;
> +     struct bpf_verifier_stack_elem *elem, *head = env->head;
>       int insn_idx;
>  
>       if (env->head == NULL)
>               return -1;
>  
> -     memcpy(&env->cur_state, &env->head->st, sizeof(env->cur_state));
> -     insn_idx = env->head->insn_idx;
> +     if (cur && cur->allocated_stack < head->st.allocated_stack) {
> +             cur = realloc_verifier_state(cur, head->st.allocated_stack);
> +             if (!cur)
> +                     return -ENOMEM;
I don't think this does what you want.  Calling code in do_check() will
 get insn_idx = -ENOMEM, see that it's < 0, and break out of the loop.
 Prog will then be accepted without further checking.

-Ed

Reply via email to