From: Yonghong Song <yonghong.song@linux.dev>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>,
David Vernet <void@manifault.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>
Cc: Matt Bobrowski <mattbobrowski@google.com>,
bpf <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>, Song Liu <song@kernel.org>,
KP Singh <kpsingh@kernel.org>,
Stanislav Fomichev <sdf@google.com>, Hao Luo <haoluo@google.com>,
Jiri Olsa <jolsa@kernel.org>
Subject: Re: [RFC] bpf: allowing PTR_TO_BTF_ID | PTR_TRUSTED w/ non-zero fixed offset to selected KF_TRUSTED_ARGS BPF kfuncs
Date: Tue, 23 Apr 2024 15:16:32 -0700 [thread overview]
Message-ID: <18bbc8d9-5a6b-4a7f-8805-d7565baf0d7c@linux.dev> (raw)
In-Reply-To: <CAADnVQ+z5w4GaMudrLXw3LAq1B3Ong7FhQHdkJN7m8svkCpMgA@mail.gmail.com>
On 4/18/24 8:03 PM, Alexei Starovoitov wrote:
> On Wed, Apr 17, 2024 at 5:11 PM Yonghong Song <yonghong.song@linux.dev> wrote:
>>
>> On 4/17/24 1:19 PM, Matt Bobrowski wrote:
>>> On Mon, Apr 15, 2024 at 09:43:42AM -0700, Yonghong Song wrote:
>>>> On 4/12/24 4:31 AM, Matt Bobrowski wrote:
>>>>> Hi,
>>>>>
>>>>> Currently, if a BPF kfunc has been annotated with KF_TRUSTED_ARGS, any
>>>>> supplied PTR_TO_BTF_ID | PTR_TRUSTED argument to that BPF kfunc must
>>>>> have it's fixed offset set to zero, or else the BPF program being
>>>>> loaded will be outright rejected by the BPF verifier.
>>>>>
>>>>> This non-zero fixed offset restriction in most cases makes a lot of
>>>>> sense, as it's considered to be a robust means of assuring that the
>>>>> supplied PTR_TO_BTF_ID to the KF_TRUSTED_ARGS annotated BPF kfunc
>>>>> upholds it's PTR_TRUSTED property. However, I believe that there are
>>>>> also cases out there whereby a PTR_TO_BTF_ID | PTR_TRUSTED w/ a fixed
>>>>> offset can still be considered as something which posses the
>>>>> PTR_TRUSTED property, and could be safely passed to a BPF kfunc that
>>>>> is annotated w/ KF_TRUSTED_ARGS. I believe that this can particularly
>>>>> hold true for selected embedded data structure members present within
>>>>> given PTR_TO_BTF_ID | PTR_TRUSTED types i.e. struct
>>>>> task_struct.thread_info, struct file.nf_path.
>>>>>
>>>>> Take for example the struct thread_info which is embedded within
>>>>> struct task_struct. In a BPF program, if we happened to acquire a
>>>>> PTR_TO_BTF_ID | PTR_TRUSTED for a struct task_struct via
>>>>> bpf_get_current_task_btf(), and then constructed a pointer of type
>>>>> struct thread_info which was assigned the address of the embedded
>>>>> struct task_struct.thread_info member, we'd have ourselves a
>>>>> PTR_TO_BTF_ID | PTR_TRUSTED w/ a fixed offset. Now, let's
>>>>> hypothetically also say that we had a BPF kfunc that took a struct
>>>>> thread_info pointer as an argument and the BPF kfunc was also
>>>>> annotated w/ KF_TRUSTED_ARGS. If we attempted to pass the constructed
>>>>> PTR_TO_BTF_ID | PTR_TRUSTED w/ fixed offset to this hypothetical BPF
>>>>> kfunc, the BPF program would be rejected by the BPF verifier. This is
>>>>> irrespective of the fact that supplying pointers to such embedded data
>>>>> structure members of a PTR_TO_BTF_ID | PTR_TRUSTED may be considered
>>>>> to be safe.
>>>>>
>>>>> One of the ideas that I had in mind to workaround the non-zero fixed
>>>>> offset restriction was to simply introduce a new BPF kfunc annotation
>>>>> i.e. __offset_allowed that could be applied on selected BPF kfunc
>>>>> arguments that are expected to be KF_TRUSTED_ARGS. Such an annotation
>>>>> would effectively control whether we enforce the non-zero offset
>>>>> restriction or not in check_kfunc_args(), check_func_arg_reg_off(),
>>>>> and __check_ptr_off_reg(). Although, now I'm second guessing myself
>>>>> and I am wondering whether introducing something like the
>>>>> __offset_allowed annotation for BPF kfunc arguments could lead to
>>>>> compromising any of the safety guarantees that are provided by the BPF
>>>>> verifier. Does anyone see an immediate problem with using such an
>>>>> approach? I raise concerns, because it feels like we're effectively
>>>>> punching a hole in the BPF verifier, but it may also be perfectly safe
>>>>> to do on carefully selected PTR_TO_BTF_ID | PTR_TRUSTED types
>>>>> i.e. struct thread_info, struct file, and it's just my paranoia
>>>>> getting the better of me. Or, maybe someone has another idea to
>>>>> support PTR_TO_BTF_ID | PTR_TRUSTED w/ fixed offset safely and a
>>>>> little more generally without the need to actually make use of any
>>>>> other BPF kfunc annotations?
>>>> In verifier.c, we have BTF_TYPE_SAFE_TRUSTED to indidate that
>>>> a pointer of a particular struct is safe and trusted if the point
>>>> of that struct is trusted, e.g.,
>>>>
>>>> BTF_TYPE_SAFE_TRUSTED(struct file) {
>>>> struct inode *f_inode;
>>>> };
>>>>
>>>> We do the above since gcc does not support btf_tag yet.
>>> Yes, I'm rather familiar with this construct.
>>>
>>>> I guess you could do
>>>>
>>>> BTF_TYPE_SAFE_TRUSTED(struct file) {
>>>> struct path f_path;
>>>> };
>>>>
>>>> and enhance verifier with the above information.
>>>>
>>>> But the above 'struct path f_path' may unnecessary
>>>> consume extra memory since we only care about field
>>>> 'f_path'. Maybe create a new construct like
>>>>
>>>> /* pointee is a field of the struct */
>>>> BTF_TYPE_SAFE_FIELD_TRUSTED(struct file) {
>>>> struct path *f_path;
>>>> };
>>> I don't fully understand how something like
>>> BTF_TYPE_SAFE_FIELD_TRUSTED could work in practice. Do you mind
>>> elaborating on that a little?
>>>
>>> What I'm currently thinking is that with something like
>>> BTF_TYPE_SAFE_FIELD_TRUSTED, if the BPF verifier sees a PTR_TO_BTF_ID
>>> | PTR_TRUSTED w/ a fixed offset supplied to a BPF kfunc, then the BPF
>>> verifier can also check that fixed offset for the supplied
>>> PTR_TO_BTF_ID | PTR_TRUSTED actually accesses a member that has been
>>> explicitly annotated as being trusted via
>>> BTF_TYPE_SAFE_FIELD_TRUSTED. Maybe that would be better then making
>>> use of an __offset_allowed annotation, which would solely rely on the
>>> btf_struct_ids_match() check for its safety.
>> Right. What you described in the above is what I think as well.
> I believe BTF_TYPE_SAFE_* or __offset_allowed annotations
> are not necessary.
>
> In this case thread_info is the first field of struct task_struct
> and I suspect the verifier already allows:
>
> bpf_kfunc void do_stuff_with_thread(struct thread_info *ti) KF_TRUSTED_ARGS
> and use it as:
> task = bpf_get_current_task_btf();
> do_stuff_with_thread(&task->thread_info);
>
> We have similar setup with:
> struct bpf_cpumask {
> cpumask_t cpumask;
> ...
> };
>
> and kfunc that accepts trusted cpumask_t * will accept
> trusted struct bpf_cpumask *.
> The other way around should be rejected, of course.
> Similar approach should work with file/path.
> The only difference is that the offset will be non-zero.
>
> process_kf_arg_ptr_to_btf_id() needs to get smarter.
>
> David Vernet added that check:
>
> WARN_ON_ONCE(is_kfunc_trusted_args(meta) && reg->off);
> as part of commit b613d335a743c.
>
> iirc the reg->off==0 check is there, as an extra caution.
>
> We can allow off!=0 and it won't confuse btf_type_ids_nocast_alias.
>
> struct nf_conn___init {
> int another_field_at_off_zero;
> struct nf_conn ct;
> };
>
> will still trigger strict_type_match as expected.
>
> Maybe other places in the verifier need to get smarter too
> to allow non-zero offset into kf_trusted_args.
So IIUC, for a trusted pointer, any member or nested member
access (without pointer tracing) is trusted.
For example
struct p2 {
struct t1 *s1;
struct t2 s2;
};
struct p {
struct p1 f1;
struct p2 f2;
struct p3 f3;
}
struct p *trust_p;
So here &trust_p->f1, &trust_p->f2, &trust_p->f3, &trust_p->f2.s2
are all trusted, right?
next prev parent reply other threads:[~2024-04-23 22:16 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-04-12 11:31 [RFC] bpf: allowing PTR_TO_BTF_ID | PTR_TRUSTED w/ non-zero fixed offset to selected KF_TRUSTED_ARGS BPF kfuncs Matt Bobrowski
2024-04-15 16:43 ` Yonghong Song
2024-04-17 20:19 ` Matt Bobrowski
2024-04-18 0:11 ` Yonghong Song
2024-04-19 3:03 ` Alexei Starovoitov
2024-04-23 22:16 ` Yonghong Song [this message]
2024-04-23 23:47 ` Alexei Starovoitov
2024-04-24 5:50 ` David Vernet
2024-04-24 18:36 ` Alexei Starovoitov
2024-04-25 15:59 ` David Vernet
2024-04-25 16:06 ` Kumar Kartikeya Dwivedi
2024-04-26 9:48 ` Matt Bobrowski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=18bbc8d9-5a6b-4a7f-8805-d7565baf0d7c@linux.dev \
--to=yonghong.song@linux.dev \
--cc=alexei.starovoitov@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=haoluo@google.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=mattbobrowski@google.com \
--cc=memxor@gmail.com \
--cc=sdf@google.com \
--cc=song@kernel.org \
--cc=void@manifault.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.