On 04/03/2019 11:02 PM, Andrey Ignatov wrote:
> Daniel Borkmann <dan...@iogearbox.net> [Wed, 2019-04-03 09:22 -0700]:
>> 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.
> 
> Yeah, I think this will work.
> 
> This will change the logic a bit though.
> 
> E.g. logic in this patch will deny variable offset stack access to
> ARG_PTR_TO_UNINIT_MEM no matter if corresponding stack memory is
> initialized or not.
> 
> But with `meta = NULL` verifier will accept access to
> ARG_PTR_TO_UNINIT_MEM on stack if that part of the stack is fully
> initialized for all possible offsets.
> 
> I think the latter should be fine since if all possible bytes that can
> be accessed are already initialized then there should not be problem on
> return from the helper.
> 
> I'll switch to `meta = NULL` in v3. Though given the difference in the
> logic, let me know if you prefer to keep the one in this patch. Thanks.

Yes I know, I mentioned it in my email wrt more flexibility, but probably
not communicated clear enough. I think that's totally fine.

>>>             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