On 04/02/2019 10:19 PM, Andrey Ignatov wrote:
> It's hard to guarantee that whole memory is marked as initialized on
> helper return if uninitialized stack is accessed with variable offset
> since specific bounds are unknown to verifier. This may cause
> uninitialized stack leaking.
> 
> Reject such an access in check_stack_boundary to prevent possible
> leaking.
> 
> There are no known use-cases for indirect uninitialized stack access
> with variable offset so it shouldn't break anything.
> 
> Fixes: 2011fccfb61b ("bpf: Support variable offset stack access from helpers")
> Reported-by: Daniel Borkmann <dan...@iogearbox.net>
> Signed-off-by: Andrey Ignatov <r...@fb.com>
> ---
>  kernel/bpf/verifier.c | 25 +++++++++++++++++++------
>  1 file changed, 19 insertions(+), 6 deletions(-)
> 
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index b7a7a9caa82f..12b84307ffa8 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -2212,7 +2212,26 @@ static int check_stack_boundary(struct 
> bpf_verifier_env *env, int regno,
>                                            zero_size_allowed);
>               if (err)
>                       return err;
> +             if (meta && meta->raw_mode) {
> +                     meta->access_size = access_size;
> +                     meta->regno = regno;
> +                     return 0;
> +             }
>       } else {
> +             /* Only initialized buffer on stack is allowed to be accessed
> +              * with variable offset. With uninitialized buffer it's hard to
> +              * guarantee that whole memory is marked as initialized on
> +              * helper return since specific bounds are unknown what may
> +              * cause uninitialized stack leaking.
> +              */
> +             if (meta && meta->raw_mode) {
> +                     char tn_buf[48];
> +
> +                     tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off);
> +                     verbose(env, "R%d invalid indirect access to 
> uninitialized stack var_off=%s\n",
> +                             regno, tn_buf);
> +                     return -EACCES;
> +             }

Hmm, I think we should probably handle this in similar way like we do
in case of variable stack access when it comes to stack size:

               if (!tnum_is_const(reg->var_off))
                        /* For unprivileged variable accesses, disable raw
                         * mode so that the program is required to
                         * initialize all the memory that the helper could
                         * just partially fill up.
                         */
                        meta = NULL;

So we error out naturally on the loop later where we also mark for
liveness, and also allow for more flexibility if we know stack must
already be initialized in this range.

>               min_off = reg->smin_value + reg->off;
>               max_off = reg->umax_value + reg->off;
>               err = __check_stack_boundary(env, regno, min_off, access_size,
> @@ -2225,12 +2244,6 @@ static int check_stack_boundary(struct 
> bpf_verifier_env *env, int regno,
>                       return err;
>       }
>  
> -     if (meta && meta->raw_mode) {
> -             meta->access_size = access_size;
> -             meta->regno = regno;
> -             return 0;
> -     }

This can then also stay as-is.

>       for (i = min_off; i < max_off + access_size; i++) {
>               u8 *stype;
>  
> 

Reply via email to