[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <cb47dd6a-30e2-dc7f-529f-53820acb285d@iogearbox.net>
Date: Thu, 4 Apr 2019 00:31:22 +0200
From: Daniel Borkmann <daniel@...earbox.net>
To: Andrey Ignatov <rdna@...com>
Cc: "netdev@...r.kernel.org" <netdev@...r.kernel.org>,
"ast@...nel.org" <ast@...nel.org>, Kernel Team <Kernel-team@...com>
Subject: Re: [PATCH bpf-next 1/5] bpf: Reject indirect var_off stack access in
raw mode
On 04/03/2019 11:02 PM, Andrey Ignatov wrote:
> Daniel Borkmann <daniel@...earbox.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 <daniel@...earbox.net>
>>> Signed-off-by: Andrey Ignatov <rdna@...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;
>>>
>>>
>>
>
Powered by blists - more mailing lists