diff mbox series

[v6,bpf-next,1/9] bpf: Expose bpf_dynptr_slice* kfuncs for in kernel use

Message ID 20231024235551.2769174-2-song@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series bpf: File verification with LSM and fsverity | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 2862 this patch: 2860
netdev/cc_maintainers warning 10 maintainers not CCed: rostedt@goodmis.org yonghong.song@linux.dev jolsa@kernel.org mhiramat@kernel.org kpsingh@kernel.org john.fastabend@gmail.com linux-trace-kernel@vger.kernel.org sdf@google.com haoluo@google.com martin.lau@linux.dev
netdev/build_clang success Errors and warnings before: 1537 this patch: 1537
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 2950 this patch: 2948
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 65 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-16 / veristat
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-16 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-16 / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-16 / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-3 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-7 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-15 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-16 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / test (test_progs_no_alu32_parallel, true, 30) / test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_progs_parallel, true, 30) / test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 success Logs for x86_64-llvm-16 / build / build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-25 success Logs for x86_64-llvm-16 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-26 success Logs for x86_64-llvm-16 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-llvm-16 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-16 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-16 / veristat
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-11 fail Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc

Commit Message

Song Liu Oct. 24, 2023, 11:55 p.m. UTC
kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
to access the dynptr data. They are also useful for in kernel functions
that access dynptr data, for example, bpf_verify_pkcs7_signature.

Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
functions can use them instead of accessing dynptr->data directly.

Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
dynptr->data.

Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
that they may return error pointers for BPF_DYNPTR_TYPE_XDP.

Signed-off-by: Song Liu <song@kernel.org>
---
 include/linux/bpf.h      |  4 ++++
 kernel/bpf/helpers.c     | 16 ++++++++--------
 kernel/trace/bpf_trace.c | 15 +++++++++++----
 3 files changed, 23 insertions(+), 12 deletions(-)

Comments

Andrii Nakryiko Nov. 2, 2023, 4:56 p.m. UTC | #1
On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
>
> kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
> to access the dynptr data. They are also useful for in kernel functions
> that access dynptr data, for example, bpf_verify_pkcs7_signature.
>
> Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
> functions can use them instead of accessing dynptr->data directly.
>
> Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
> dynptr->data.
>
> Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
> that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
>
> Signed-off-by: Song Liu <song@kernel.org>
> ---
>  include/linux/bpf.h      |  4 ++++
>  kernel/bpf/helpers.c     | 16 ++++++++--------
>  kernel/trace/bpf_trace.c | 15 +++++++++++----
>  3 files changed, 23 insertions(+), 12 deletions(-)
>
> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> index b4825d3cdb29..3ed3ae37cbdf 100644
> --- a/include/linux/bpf.h
> +++ b/include/linux/bpf.h
> @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
>
>  int bpf_dynptr_check_size(u32 size);
>  u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
> +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> +                      void *buffer__opt, u32 buffer__szk);
> +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> +                           void *buffer__opt, u32 buffer__szk);
>
>  #ifdef CONFIG_BPF_JIT
>  int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> index e46ac288a108..af5059f11e83 100644
> --- a/kernel/bpf/helpers.c
> +++ b/kernel/bpf/helpers.c
> @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
>   * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
>   * the bpf program.
>   *
> - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
> - * data slice (can be either direct pointer to the data or a pointer to the user
> - * provided buffer, with its contents containing the data, if unable to obtain
> - * direct pointer)
> + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer

Hold on, nope, this one shouldn't return error pointer because it's
used from BPF program side and BPF program is checking for NULL only.
Does it actually return error pointer, though?

I'm generally skeptical of allowing to call kfuncs directly from
internal kernel code, tbh, and concerns like this are one reason why.
BPF verifier sets up various conditions that kfuncs have to follow,
and it seems error-prone to mix this up with internal kernel usage.

> + * to a read-only data slice (can be either direct pointer to the data or a
> + * pointer to the user provided buffer, with its contents containing the data,
> + * if unable to obtain direct pointer)
>   */
>  __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
>                                    void *buffer__opt, u32 buffer__szk)
> @@ -2354,10 +2354,10 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
>   * bpf_dynptr_slice_rdwr will not invalidate any ctx->data/data_end pointers in
>   * the bpf program.
>   *
> - * Return: NULL if the call failed (eg invalid dynptr), pointer to a
> - * data slice (can be either direct pointer to the data or a pointer to the user
> - * provided buffer, with its contents containing the data, if unable to obtain
> - * direct pointer)
> + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
> + * to a data slice (can be either direct pointer to the data or a pointer to the
> + * user provided buffer, with its contents containing the data, if unable to
> + * obtain direct pointer)
>   */
>  __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
>                                         void *buffer__opt, u32 buffer__szk)
> diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> index df697c74d519..2626706b6387 100644
> --- a/kernel/trace/bpf_trace.c
> +++ b/kernel/trace/bpf_trace.c
> @@ -1378,6 +1378,7 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
>                                struct bpf_dynptr_kern *sig_ptr,
>                                struct bpf_key *trusted_keyring)
>  {
> +       void *data, *sig;
>         int ret;
>
>         if (trusted_keyring->has_ref) {
> @@ -1394,10 +1395,16 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
>                         return ret;
>         }
>
> -       return verify_pkcs7_signature(data_ptr->data,
> -                                     __bpf_dynptr_size(data_ptr),
> -                                     sig_ptr->data,
> -                                     __bpf_dynptr_size(sig_ptr),
> +       data = bpf_dynptr_slice(data_ptr, 0, NULL, 0);
> +       if (IS_ERR(data))
> +               return PTR_ERR(data);
> +
> +       sig = bpf_dynptr_slice(sig_ptr, 0, NULL, 0);
> +       if (IS_ERR(sig))
> +               return PTR_ERR(sig);
> +
> +       return verify_pkcs7_signature(data, __bpf_dynptr_size(data_ptr),
> +                                     sig, __bpf_dynptr_size(sig_ptr),
>                                       trusted_keyring->key,
>                                       VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
>                                       NULL);
> --
> 2.34.1
>
>
Andrii Nakryiko Nov. 2, 2023, 5:09 p.m. UTC | #2
On Thu, Nov 2, 2023 at 9:56 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
> >
> > kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
> > to access the dynptr data. They are also useful for in kernel functions
> > that access dynptr data, for example, bpf_verify_pkcs7_signature.
> >
> > Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
> > functions can use them instead of accessing dynptr->data directly.
> >
> > Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
> > dynptr->data.
> >
> > Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
> > that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
> >
> > Signed-off-by: Song Liu <song@kernel.org>
> > ---
> >  include/linux/bpf.h      |  4 ++++
> >  kernel/bpf/helpers.c     | 16 ++++++++--------
> >  kernel/trace/bpf_trace.c | 15 +++++++++++----
> >  3 files changed, 23 insertions(+), 12 deletions(-)
> >
> > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > index b4825d3cdb29..3ed3ae37cbdf 100644
> > --- a/include/linux/bpf.h
> > +++ b/include/linux/bpf.h
> > @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
> >
> >  int bpf_dynptr_check_size(u32 size);
> >  u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
> > +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> > +                      void *buffer__opt, u32 buffer__szk);
> > +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> > +                           void *buffer__opt, u32 buffer__szk);
> >
> >  #ifdef CONFIG_BPF_JIT
> >  int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
> > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> > index e46ac288a108..af5059f11e83 100644
> > --- a/kernel/bpf/helpers.c
> > +++ b/kernel/bpf/helpers.c
> > @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
> >   * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
> >   * the bpf program.
> >   *
> > - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
> > - * data slice (can be either direct pointer to the data or a pointer to the user
> > - * provided buffer, with its contents containing the data, if unable to obtain
> > - * direct pointer)
> > + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
>
> Hold on, nope, this one shouldn't return error pointer because it's
> used from BPF program side and BPF program is checking for NULL only.
> Does it actually return error pointer, though?

So I just checked the code (should have done it before replying,
sorry). It is a bug that slipped through when adding bpf_xdp_pointer()
usage. We should always return NULL from this kfunc on error
conditions. Let's fix it separately, but please don't change the
comments.

>
> I'm generally skeptical of allowing to call kfuncs directly from
> internal kernel code, tbh, and concerns like this are one reason why.
> BPF verifier sets up various conditions that kfuncs have to follow,
> and it seems error-prone to mix this up with internal kernel usage.
>

Reading bpf_dynptr_slice_rdwr code, it does look exactly like what you
want, despite the confusingly-looking 0, NULL, 0 arguments. So I guess
I'm fine exposing it directly, but it still feels like it will bite us
at some point later.


> > + * to a read-only data slice (can be either direct pointer to the data or a
> > + * pointer to the user provided buffer, with its contents containing the data,
> > + * if unable to obtain direct pointer)
> >   */
> >  __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> >                                    void *buffer__opt, u32 buffer__szk)
> > @@ -2354,10 +2354,10 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
> >   * bpf_dynptr_slice_rdwr will not invalidate any ctx->data/data_end pointers in
> >   * the bpf program.
> >   *
> > - * Return: NULL if the call failed (eg invalid dynptr), pointer to a
> > - * data slice (can be either direct pointer to the data or a pointer to the user
> > - * provided buffer, with its contents containing the data, if unable to obtain
> > - * direct pointer)
> > + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
> > + * to a data slice (can be either direct pointer to the data or a pointer to the
> > + * user provided buffer, with its contents containing the data, if unable to
> > + * obtain direct pointer)
> >   */
> >  __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> >                                         void *buffer__opt, u32 buffer__szk)
> > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> > index df697c74d519..2626706b6387 100644
> > --- a/kernel/trace/bpf_trace.c
> > +++ b/kernel/trace/bpf_trace.c
> > @@ -1378,6 +1378,7 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
> >                                struct bpf_dynptr_kern *sig_ptr,
> >                                struct bpf_key *trusted_keyring)
> >  {
> > +       void *data, *sig;
> >         int ret;
> >
> >         if (trusted_keyring->has_ref) {
> > @@ -1394,10 +1395,16 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
> >                         return ret;
> >         }
> >
> > -       return verify_pkcs7_signature(data_ptr->data,
> > -                                     __bpf_dynptr_size(data_ptr),
> > -                                     sig_ptr->data,
> > -                                     __bpf_dynptr_size(sig_ptr),
> > +       data = bpf_dynptr_slice(data_ptr, 0, NULL, 0);
> > +       if (IS_ERR(data))
> > +               return PTR_ERR(data);
> > +
> > +       sig = bpf_dynptr_slice(sig_ptr, 0, NULL, 0);
> > +       if (IS_ERR(sig))
> > +               return PTR_ERR(sig);
> > +
> > +       return verify_pkcs7_signature(data, __bpf_dynptr_size(data_ptr),
> > +                                     sig, __bpf_dynptr_size(sig_ptr),
> >                                       trusted_keyring->key,
> >                                       VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
> >                                       NULL);
> > --
> > 2.34.1
> >
> >
Andrii Nakryiko Nov. 2, 2023, 5:16 p.m. UTC | #3
On Thu, Nov 2, 2023 at 10:09 AM Andrii Nakryiko
<andrii.nakryiko@gmail.com> wrote:
>
> On Thu, Nov 2, 2023 at 9:56 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
> >
> > On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
> > >
> > > kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
> > > to access the dynptr data. They are also useful for in kernel functions
> > > that access dynptr data, for example, bpf_verify_pkcs7_signature.
> > >
> > > Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
> > > functions can use them instead of accessing dynptr->data directly.
> > >
> > > Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
> > > dynptr->data.
> > >
> > > Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
> > > that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
> > >
> > > Signed-off-by: Song Liu <song@kernel.org>
> > > ---
> > >  include/linux/bpf.h      |  4 ++++
> > >  kernel/bpf/helpers.c     | 16 ++++++++--------
> > >  kernel/trace/bpf_trace.c | 15 +++++++++++----
> > >  3 files changed, 23 insertions(+), 12 deletions(-)
> > >
> > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> > > index b4825d3cdb29..3ed3ae37cbdf 100644
> > > --- a/include/linux/bpf.h
> > > +++ b/include/linux/bpf.h
> > > @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
> > >
> > >  int bpf_dynptr_check_size(u32 size);
> > >  u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
> > > +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> > > +                      void *buffer__opt, u32 buffer__szk);
> > > +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> > > +                           void *buffer__opt, u32 buffer__szk);
> > >
> > >  #ifdef CONFIG_BPF_JIT
> > >  int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
> > > diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> > > index e46ac288a108..af5059f11e83 100644
> > > --- a/kernel/bpf/helpers.c
> > > +++ b/kernel/bpf/helpers.c
> > > @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
> > >   * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
> > >   * the bpf program.
> > >   *
> > > - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
> > > - * data slice (can be either direct pointer to the data or a pointer to the user
> > > - * provided buffer, with its contents containing the data, if unable to obtain
> > > - * direct pointer)
> > > + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
> >
> > Hold on, nope, this one shouldn't return error pointer because it's
> > used from BPF program side and BPF program is checking for NULL only.
> > Does it actually return error pointer, though?
>
> So I just checked the code (should have done it before replying,
> sorry). It is a bug that slipped through when adding bpf_xdp_pointer()
> usage. We should always return NULL from this kfunc on error
> conditions. Let's fix it separately, but please don't change the
> comments.
>
> >
> > I'm generally skeptical of allowing to call kfuncs directly from
> > internal kernel code, tbh, and concerns like this are one reason why.
> > BPF verifier sets up various conditions that kfuncs have to follow,
> > and it seems error-prone to mix this up with internal kernel usage.
> >
>
> Reading bpf_dynptr_slice_rdwr code, it does look exactly like what you
> want, despite the confusingly-looking 0, NULL, 0 arguments. So I guess
> I'm fine exposing it directly, but it still feels like it will bite us
> at some point later.

Ok, now I'm at patch #5. Think about what you are doing here. You are
asking bpf_dynptr_slice_rdrw() if you can get a directly writable
pointer to a data area of length *zero*. So if it's SKB, for example,
then yeah, you'll be granted a pointer. But then you are proceeding to
write up to sizeof(struct fsverity_digest) bytes, and that can cross
into non-contiguous memory.

So I'll take it back, let's not expose this kfunc directly to kernel
code. Let's have a separate internal helper that will return either
valid pointer or NULL for a given dynptr, but will require valid
non-zero max size. Something with the signature like below

void * __bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);

If ptr can provide a direct pointer to memory of length *len*, great.
If not, return NULL. This will be an appropriate internal API for all
the use cases you are adding where we will be writing back into dynptr
from other kernel APIs with the assumption of contiguous memory
region.



>
>
> > > + * to a read-only data slice (can be either direct pointer to the data or a
> > > + * pointer to the user provided buffer, with its contents containing the data,
> > > + * if unable to obtain direct pointer)
> > >   */
> > >  __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> > >                                    void *buffer__opt, u32 buffer__szk)
> > > @@ -2354,10 +2354,10 @@ __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
> > >   * bpf_dynptr_slice_rdwr will not invalidate any ctx->data/data_end pointers in
> > >   * the bpf program.
> > >   *
> > > - * Return: NULL if the call failed (eg invalid dynptr), pointer to a
> > > - * data slice (can be either direct pointer to the data or a pointer to the user
> > > - * provided buffer, with its contents containing the data, if unable to obtain
> > > - * direct pointer)
> > > + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
> > > + * to a data slice (can be either direct pointer to the data or a pointer to the
> > > + * user provided buffer, with its contents containing the data, if unable to
> > > + * obtain direct pointer)
> > >   */
> > >  __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> > >                                         void *buffer__opt, u32 buffer__szk)
> > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
> > > index df697c74d519..2626706b6387 100644
> > > --- a/kernel/trace/bpf_trace.c
> > > +++ b/kernel/trace/bpf_trace.c
> > > @@ -1378,6 +1378,7 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
> > >                                struct bpf_dynptr_kern *sig_ptr,
> > >                                struct bpf_key *trusted_keyring)
> > >  {
> > > +       void *data, *sig;
> > >         int ret;
> > >
> > >         if (trusted_keyring->has_ref) {
> > > @@ -1394,10 +1395,16 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
> > >                         return ret;
> > >         }
> > >
> > > -       return verify_pkcs7_signature(data_ptr->data,
> > > -                                     __bpf_dynptr_size(data_ptr),
> > > -                                     sig_ptr->data,
> > > -                                     __bpf_dynptr_size(sig_ptr),
> > > +       data = bpf_dynptr_slice(data_ptr, 0, NULL, 0);
> > > +       if (IS_ERR(data))
> > > +               return PTR_ERR(data);
> > > +
> > > +       sig = bpf_dynptr_slice(sig_ptr, 0, NULL, 0);
> > > +       if (IS_ERR(sig))
> > > +               return PTR_ERR(sig);
> > > +
> > > +       return verify_pkcs7_signature(data, __bpf_dynptr_size(data_ptr),
> > > +                                     sig, __bpf_dynptr_size(sig_ptr),
> > >                                       trusted_keyring->key,
> > >                                       VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
> > >                                       NULL);
> > > --
> > > 2.34.1
> > >
> > >
Song Liu Nov. 2, 2023, 6:09 p.m. UTC | #4
> On Nov 2, 2023, at 10:16 AM, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> 
> On Thu, Nov 2, 2023 at 10:09 AM Andrii Nakryiko
> <andrii.nakryiko@gmail.com> wrote:
>> 
>> On Thu, Nov 2, 2023 at 9:56 AM Andrii Nakryiko
>> <andrii.nakryiko@gmail.com> wrote:
>>> 
>>> On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
>>>> 
>>>> kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
>>>> to access the dynptr data. They are also useful for in kernel functions
>>>> that access dynptr data, for example, bpf_verify_pkcs7_signature.
>>>> 
>>>> Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
>>>> functions can use them instead of accessing dynptr->data directly.
>>>> 
>>>> Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
>>>> dynptr->data.
>>>> 
>>>> Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
>>>> that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
>>>> 
>>>> Signed-off-by: Song Liu <song@kernel.org>
>>>> ---
>>>> include/linux/bpf.h      |  4 ++++
>>>> kernel/bpf/helpers.c     | 16 ++++++++--------
>>>> kernel/trace/bpf_trace.c | 15 +++++++++++----
>>>> 3 files changed, 23 insertions(+), 12 deletions(-)
>>>> 
>>>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>>>> index b4825d3cdb29..3ed3ae37cbdf 100644
>>>> --- a/include/linux/bpf.h
>>>> +++ b/include/linux/bpf.h
>>>> @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
>>>> 
>>>> int bpf_dynptr_check_size(u32 size);
>>>> u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
>>>> +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
>>>> +                      void *buffer__opt, u32 buffer__szk);
>>>> +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
>>>> +                           void *buffer__opt, u32 buffer__szk);
>>>> 
>>>> #ifdef CONFIG_BPF_JIT
>>>> int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
>>>> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
>>>> index e46ac288a108..af5059f11e83 100644
>>>> --- a/kernel/bpf/helpers.c
>>>> +++ b/kernel/bpf/helpers.c
>>>> @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
>>>>  * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
>>>>  * the bpf program.
>>>>  *
>>>> - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
>>>> - * data slice (can be either direct pointer to the data or a pointer to the user
>>>> - * provided buffer, with its contents containing the data, if unable to obtain
>>>> - * direct pointer)
>>>> + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
>>> 
>>> Hold on, nope, this one shouldn't return error pointer because it's
>>> used from BPF program side and BPF program is checking for NULL only.
>>> Does it actually return error pointer, though?

Right. kfunc should not return error pointer. 

>> 
>> So I just checked the code (should have done it before replying,
>> sorry). It is a bug that slipped through when adding bpf_xdp_pointer()
>> usage. We should always return NULL from this kfunc on error
>> conditions. Let's fix it separately, but please don't change the
>> comments.
>> 
>>> 
>>> I'm generally skeptical of allowing to call kfuncs directly from
>>> internal kernel code, tbh, and concerns like this are one reason why.
>>> BPF verifier sets up various conditions that kfuncs have to follow,
>>> and it seems error-prone to mix this up with internal kernel usage.
>>> 
>> 
>> Reading bpf_dynptr_slice_rdwr code, it does look exactly like what you
>> want, despite the confusingly-looking 0, NULL, 0 arguments. So I guess
>> I'm fine exposing it directly, but it still feels like it will bite us
>> at some point later.
> 
> Ok, now I'm at patch #5. Think about what you are doing here. You are
> asking bpf_dynptr_slice_rdrw() if you can get a directly writable
> pointer to a data area of length *zero*. So if it's SKB, for example,
> then yeah, you'll be granted a pointer. But then you are proceeding to
> write up to sizeof(struct fsverity_digest) bytes, and that can cross
> into non-contiguous memory.
> 
> So I'll take it back, let's not expose this kfunc directly to kernel
> code. Let's have a separate internal helper that will return either
> valid pointer or NULL for a given dynptr, but will require valid
> non-zero max size. Something with the signature like below
> 
> void * __bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
> 
> If ptr can provide a direct pointer to memory of length *len*, great.
> If not, return NULL. This will be an appropriate internal API for all
> the use cases you are adding where we will be writing back into dynptr
> from other kernel APIs with the assumption of contiguous memory
> region.

Sounds good. Fixing this in the next version. 

Thanks,
Song
Andrii Nakryiko Nov. 2, 2023, 6:12 p.m. UTC | #5
On Thu, Nov 2, 2023 at 11:09 AM Song Liu <songliubraving@meta.com> wrote:
>
>
>
> > On Nov 2, 2023, at 10:16 AM, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> >
> > On Thu, Nov 2, 2023 at 10:09 AM Andrii Nakryiko
> > <andrii.nakryiko@gmail.com> wrote:
> >>
> >> On Thu, Nov 2, 2023 at 9:56 AM Andrii Nakryiko
> >> <andrii.nakryiko@gmail.com> wrote:
> >>>
> >>> On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
> >>>>
> >>>> kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
> >>>> to access the dynptr data. They are also useful for in kernel functions
> >>>> that access dynptr data, for example, bpf_verify_pkcs7_signature.
> >>>>
> >>>> Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
> >>>> functions can use them instead of accessing dynptr->data directly.
> >>>>
> >>>> Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
> >>>> dynptr->data.
> >>>>
> >>>> Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
> >>>> that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
> >>>>
> >>>> Signed-off-by: Song Liu <song@kernel.org>
> >>>> ---
> >>>> include/linux/bpf.h      |  4 ++++
> >>>> kernel/bpf/helpers.c     | 16 ++++++++--------
> >>>> kernel/trace/bpf_trace.c | 15 +++++++++++----
> >>>> 3 files changed, 23 insertions(+), 12 deletions(-)
> >>>>
> >>>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> >>>> index b4825d3cdb29..3ed3ae37cbdf 100644
> >>>> --- a/include/linux/bpf.h
> >>>> +++ b/include/linux/bpf.h
> >>>> @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
> >>>>
> >>>> int bpf_dynptr_check_size(u32 size);
> >>>> u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
> >>>> +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> >>>> +                      void *buffer__opt, u32 buffer__szk);
> >>>> +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> >>>> +                           void *buffer__opt, u32 buffer__szk);
> >>>>
> >>>> #ifdef CONFIG_BPF_JIT
> >>>> int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
> >>>> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> >>>> index e46ac288a108..af5059f11e83 100644
> >>>> --- a/kernel/bpf/helpers.c
> >>>> +++ b/kernel/bpf/helpers.c
> >>>> @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
> >>>>  * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
> >>>>  * the bpf program.
> >>>>  *
> >>>> - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
> >>>> - * data slice (can be either direct pointer to the data or a pointer to the user
> >>>> - * provided buffer, with its contents containing the data, if unable to obtain
> >>>> - * direct pointer)
> >>>> + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
> >>>
> >>> Hold on, nope, this one shouldn't return error pointer because it's
> >>> used from BPF program side and BPF program is checking for NULL only.
> >>> Does it actually return error pointer, though?
>
> Right. kfunc should not return error pointer.

Turns out it doesn't, see discussion in [0]. Maybe you can sneak in
that comment in your next revision as a separate lightweight patch :)

  [0] https://lore.kernel.org/bpf/CAEf4Bzb4VbH56S2D_5Sc3u9V=OXOy20JTr4wsObBOiUA32Md2Q@mail.gmail.com/

>
> >>
> >> So I just checked the code (should have done it before replying,
> >> sorry). It is a bug that slipped through when adding bpf_xdp_pointer()
> >> usage. We should always return NULL from this kfunc on error
> >> conditions. Let's fix it separately, but please don't change the
> >> comments.
> >>
> >>>
> >>> I'm generally skeptical of allowing to call kfuncs directly from
> >>> internal kernel code, tbh, and concerns like this are one reason why.
> >>> BPF verifier sets up various conditions that kfuncs have to follow,
> >>> and it seems error-prone to mix this up with internal kernel usage.
> >>>
> >>
> >> Reading bpf_dynptr_slice_rdwr code, it does look exactly like what you
> >> want, despite the confusingly-looking 0, NULL, 0 arguments. So I guess
> >> I'm fine exposing it directly, but it still feels like it will bite us
> >> at some point later.
> >
> > Ok, now I'm at patch #5. Think about what you are doing here. You are
> > asking bpf_dynptr_slice_rdrw() if you can get a directly writable
> > pointer to a data area of length *zero*. So if it's SKB, for example,
> > then yeah, you'll be granted a pointer. But then you are proceeding to
> > write up to sizeof(struct fsverity_digest) bytes, and that can cross
> > into non-contiguous memory.
> >
> > So I'll take it back, let's not expose this kfunc directly to kernel
> > code. Let's have a separate internal helper that will return either
> > valid pointer or NULL for a given dynptr, but will require valid
> > non-zero max size. Something with the signature like below
> >
> > void * __bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
> >
> > If ptr can provide a direct pointer to memory of length *len*, great.
> > If not, return NULL. This will be an appropriate internal API for all
> > the use cases you are adding where we will be writing back into dynptr
> > from other kernel APIs with the assumption of contiguous memory
> > region.
>
> Sounds good. Fixing this in the next version.
>
> Thanks,
> Song
>
Song Liu Nov. 2, 2023, 6:16 p.m. UTC | #6
> On Nov 2, 2023, at 11:09 AM, Song Liu <songliubraving@meta.com> wrote:
> 
> 
> 
>> On Nov 2, 2023, at 10:16 AM, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
>> 
>> On Thu, Nov 2, 2023 at 10:09 AM Andrii Nakryiko
>> <andrii.nakryiko@gmail.com> wrote:
>>> 
>>> On Thu, Nov 2, 2023 at 9:56 AM Andrii Nakryiko
>>> <andrii.nakryiko@gmail.com> wrote:
>>>> 
>>>> On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
>>>>> 
>>>>> kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
>>>>> to access the dynptr data. They are also useful for in kernel functions
>>>>> that access dynptr data, for example, bpf_verify_pkcs7_signature.
>>>>> 
>>>>> Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
>>>>> functions can use them instead of accessing dynptr->data directly.
>>>>> 
>>>>> Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
>>>>> dynptr->data.
>>>>> 
>>>>> Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
>>>>> that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
>>>>> 
>>>>> Signed-off-by: Song Liu <song@kernel.org>
>>>>> ---
>>>>> include/linux/bpf.h      |  4 ++++
>>>>> kernel/bpf/helpers.c     | 16 ++++++++--------
>>>>> kernel/trace/bpf_trace.c | 15 +++++++++++----
>>>>> 3 files changed, 23 insertions(+), 12 deletions(-)
>>>>> 
>>>>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
>>>>> index b4825d3cdb29..3ed3ae37cbdf 100644
>>>>> --- a/include/linux/bpf.h
>>>>> +++ b/include/linux/bpf.h
>>>>> @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
>>>>> 
>>>>> int bpf_dynptr_check_size(u32 size);
>>>>> u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
>>>>> +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
>>>>> +                      void *buffer__opt, u32 buffer__szk);
>>>>> +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
>>>>> +                           void *buffer__opt, u32 buffer__szk);
>>>>> 
>>>>> #ifdef CONFIG_BPF_JIT
>>>>> int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
>>>>> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
>>>>> index e46ac288a108..af5059f11e83 100644
>>>>> --- a/kernel/bpf/helpers.c
>>>>> +++ b/kernel/bpf/helpers.c
>>>>> @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
>>>>> * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
>>>>> * the bpf program.
>>>>> *
>>>>> - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
>>>>> - * data slice (can be either direct pointer to the data or a pointer to the user
>>>>> - * provided buffer, with its contents containing the data, if unable to obtain
>>>>> - * direct pointer)
>>>>> + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
>>>> 
>>>> Hold on, nope, this one shouldn't return error pointer because it's
>>>> used from BPF program side and BPF program is checking for NULL only.
>>>> Does it actually return error pointer, though?
> 
> Right. kfunc should not return error pointer. 
> 
>>> 
>>> So I just checked the code (should have done it before replying,
>>> sorry). It is a bug that slipped through when adding bpf_xdp_pointer()
>>> usage. We should always return NULL from this kfunc on error
>>> conditions. Let's fix it separately, but please don't change the
>>> comments.
>>> 
>>>> 
>>>> I'm generally skeptical of allowing to call kfuncs directly from
>>>> internal kernel code, tbh, and concerns like this are one reason why.
>>>> BPF verifier sets up various conditions that kfuncs have to follow,
>>>> and it seems error-prone to mix this up with internal kernel usage.
>>>> 
>>> 
>>> Reading bpf_dynptr_slice_rdwr code, it does look exactly like what you
>>> want, despite the confusingly-looking 0, NULL, 0 arguments. So I guess
>>> I'm fine exposing it directly, but it still feels like it will bite us
>>> at some point later.
>> 
>> Ok, now I'm at patch #5. Think about what you are doing here. You are
>> asking bpf_dynptr_slice_rdrw() if you can get a directly writable
>> pointer to a data area of length *zero*. So if it's SKB, for example,
>> then yeah, you'll be granted a pointer. But then you are proceeding to
>> write up to sizeof(struct fsverity_digest) bytes, and that can cross
>> into non-contiguous memory.

By the way, the syntax and comment of bpf_dynptr_slice() is confusing:

 * @buffer__opt: User-provided buffer to copy contents into.  May be NULL
 * @buffer__szk: Size (in bytes) of the buffer if present. This is the
 *               length of the requested slice. This must be a constant.

It reads (to me) as, "if buffer__opt is NULL, buffer__szk should be 0". 

Is this just my confusion, or is there a real issue? 

Thanks,
Song


>> 
>> So I'll take it back, let's not expose this kfunc directly to kernel
>> code. Let's have a separate internal helper that will return either
>> valid pointer or NULL for a given dynptr, but will require valid
>> non-zero max size. Something with the signature like below
>> 
>> void * __bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
>> 
>> If ptr can provide a direct pointer to memory of length *len*, great.
>> If not, return NULL. This will be an appropriate internal API for all
>> the use cases you are adding where we will be writing back into dynptr
>> from other kernel APIs with the assumption of contiguous memory
>> region.
>
Andrii Nakryiko Nov. 2, 2023, 7:06 p.m. UTC | #7
On Thu, Nov 2, 2023 at 11:16 AM Song Liu <songliubraving@meta.com> wrote:
>
>
>
> > On Nov 2, 2023, at 11:09 AM, Song Liu <songliubraving@meta.com> wrote:
> >
> >
> >
> >> On Nov 2, 2023, at 10:16 AM, Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote:
> >>
> >> On Thu, Nov 2, 2023 at 10:09 AM Andrii Nakryiko
> >> <andrii.nakryiko@gmail.com> wrote:
> >>>
> >>> On Thu, Nov 2, 2023 at 9:56 AM Andrii Nakryiko
> >>> <andrii.nakryiko@gmail.com> wrote:
> >>>>
> >>>> On Tue, Oct 24, 2023 at 4:56 PM Song Liu <song@kernel.org> wrote:
> >>>>>
> >>>>> kfuncs bpf_dynptr_slice and bpf_dynptr_slice_rdwr are used by BPF programs
> >>>>> to access the dynptr data. They are also useful for in kernel functions
> >>>>> that access dynptr data, for example, bpf_verify_pkcs7_signature.
> >>>>>
> >>>>> Add bpf_dynptr_slice and bpf_dynptr_slice_rdwr to bpf.h so that kernel
> >>>>> functions can use them instead of accessing dynptr->data directly.
> >>>>>
> >>>>> Update bpf_verify_pkcs7_signature to use bpf_dynptr_slice instead of
> >>>>> dynptr->data.
> >>>>>
> >>>>> Also, update the comments for bpf_dynptr_slice and bpf_dynptr_slice_rdwr
> >>>>> that they may return error pointers for BPF_DYNPTR_TYPE_XDP.
> >>>>>
> >>>>> Signed-off-by: Song Liu <song@kernel.org>
> >>>>> ---
> >>>>> include/linux/bpf.h      |  4 ++++
> >>>>> kernel/bpf/helpers.c     | 16 ++++++++--------
> >>>>> kernel/trace/bpf_trace.c | 15 +++++++++++----
> >>>>> 3 files changed, 23 insertions(+), 12 deletions(-)
> >>>>>
> >>>>> diff --git a/include/linux/bpf.h b/include/linux/bpf.h
> >>>>> index b4825d3cdb29..3ed3ae37cbdf 100644
> >>>>> --- a/include/linux/bpf.h
> >>>>> +++ b/include/linux/bpf.h
> >>>>> @@ -1222,6 +1222,10 @@ enum bpf_dynptr_type {
> >>>>>
> >>>>> int bpf_dynptr_check_size(u32 size);
> >>>>> u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
> >>>>> +void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
> >>>>> +                      void *buffer__opt, u32 buffer__szk);
> >>>>> +void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
> >>>>> +                           void *buffer__opt, u32 buffer__szk);
> >>>>>
> >>>>> #ifdef CONFIG_BPF_JIT
> >>>>> int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
> >>>>> diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
> >>>>> index e46ac288a108..af5059f11e83 100644
> >>>>> --- a/kernel/bpf/helpers.c
> >>>>> +++ b/kernel/bpf/helpers.c
> >>>>> @@ -2270,10 +2270,10 @@ __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
> >>>>> * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
> >>>>> * the bpf program.
> >>>>> *
> >>>>> - * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
> >>>>> - * data slice (can be either direct pointer to the data or a pointer to the user
> >>>>> - * provided buffer, with its contents containing the data, if unable to obtain
> >>>>> - * direct pointer)
> >>>>> + * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
> >>>>
> >>>> Hold on, nope, this one shouldn't return error pointer because it's
> >>>> used from BPF program side and BPF program is checking for NULL only.
> >>>> Does it actually return error pointer, though?
> >
> > Right. kfunc should not return error pointer.
> >
> >>>
> >>> So I just checked the code (should have done it before replying,
> >>> sorry). It is a bug that slipped through when adding bpf_xdp_pointer()
> >>> usage. We should always return NULL from this kfunc on error
> >>> conditions. Let's fix it separately, but please don't change the
> >>> comments.
> >>>
> >>>>
> >>>> I'm generally skeptical of allowing to call kfuncs directly from
> >>>> internal kernel code, tbh, and concerns like this are one reason why.
> >>>> BPF verifier sets up various conditions that kfuncs have to follow,
> >>>> and it seems error-prone to mix this up with internal kernel usage.
> >>>>
> >>>
> >>> Reading bpf_dynptr_slice_rdwr code, it does look exactly like what you
> >>> want, despite the confusingly-looking 0, NULL, 0 arguments. So I guess
> >>> I'm fine exposing it directly, but it still feels like it will bite us
> >>> at some point later.
> >>
> >> Ok, now I'm at patch #5. Think about what you are doing here. You are
> >> asking bpf_dynptr_slice_rdrw() if you can get a directly writable
> >> pointer to a data area of length *zero*. So if it's SKB, for example,
> >> then yeah, you'll be granted a pointer. But then you are proceeding to
> >> write up to sizeof(struct fsverity_digest) bytes, and that can cross
> >> into non-contiguous memory.
>
> By the way, the syntax and comment of bpf_dynptr_slice() is confusing:
>
>  * @buffer__opt: User-provided buffer to copy contents into.  May be NULL
>  * @buffer__szk: Size (in bytes) of the buffer if present. This is the
>  *               length of the requested slice. This must be a constant.
>
> It reads (to me) as, "if buffer__opt is NULL, buffer__szk should be 0".
>
> Is this just my confusion, or is there a real issue?

It's a bit confusing, but no, that size needs to be a "how much data
do I want to read/write", so even if buffer is NULL, size has to be
specified.

"This is the length of the requested slice." is the most important
part in that comment.

>
> Thanks,
> Song
>
>
> >>
> >> So I'll take it back, let's not expose this kfunc directly to kernel
> >> code. Let's have a separate internal helper that will return either
> >> valid pointer or NULL for a given dynptr, but will require valid
> >> non-zero max size. Something with the signature like below
> >>
> >> void * __bpf_dynptr_data_rw(const struct bpf_dynptr_kern *ptr, u32 len);
> >>
> >> If ptr can provide a direct pointer to memory of length *len*, great.
> >> If not, return NULL. This will be an appropriate internal API for all
> >> the use cases you are adding where we will be writing back into dynptr
> >> from other kernel APIs with the assumption of contiguous memory
> >> region.
> >
>
diff mbox series

Patch

diff --git a/include/linux/bpf.h b/include/linux/bpf.h
index b4825d3cdb29..3ed3ae37cbdf 100644
--- a/include/linux/bpf.h
+++ b/include/linux/bpf.h
@@ -1222,6 +1222,10 @@  enum bpf_dynptr_type {
 
 int bpf_dynptr_check_size(u32 size);
 u32 __bpf_dynptr_size(const struct bpf_dynptr_kern *ptr);
+void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
+		       void *buffer__opt, u32 buffer__szk);
+void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
+			    void *buffer__opt, u32 buffer__szk);
 
 #ifdef CONFIG_BPF_JIT
 int bpf_trampoline_link_prog(struct bpf_tramp_link *link, struct bpf_trampoline *tr);
diff --git a/kernel/bpf/helpers.c b/kernel/bpf/helpers.c
index e46ac288a108..af5059f11e83 100644
--- a/kernel/bpf/helpers.c
+++ b/kernel/bpf/helpers.c
@@ -2270,10 +2270,10 @@  __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
  * bpf_dynptr_slice will not invalidate any ctx->data/data_end pointers in
  * the bpf program.
  *
- * Return: NULL if the call failed (eg invalid dynptr), pointer to a read-only
- * data slice (can be either direct pointer to the data or a pointer to the user
- * provided buffer, with its contents containing the data, if unable to obtain
- * direct pointer)
+ * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
+ * to a read-only data slice (can be either direct pointer to the data or a
+ * pointer to the user provided buffer, with its contents containing the data,
+ * if unable to obtain direct pointer)
  */
 __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset,
 				   void *buffer__opt, u32 buffer__szk)
@@ -2354,10 +2354,10 @@  __bpf_kfunc void *bpf_dynptr_slice(const struct bpf_dynptr_kern *ptr, u32 offset
  * bpf_dynptr_slice_rdwr will not invalidate any ctx->data/data_end pointers in
  * the bpf program.
  *
- * Return: NULL if the call failed (eg invalid dynptr), pointer to a
- * data slice (can be either direct pointer to the data or a pointer to the user
- * provided buffer, with its contents containing the data, if unable to obtain
- * direct pointer)
+ * Return: NULL or error pointer if the call failed (eg invalid dynptr), pointer
+ * to a data slice (can be either direct pointer to the data or a pointer to the
+ * user provided buffer, with its contents containing the data, if unable to
+ * obtain direct pointer)
  */
 __bpf_kfunc void *bpf_dynptr_slice_rdwr(const struct bpf_dynptr_kern *ptr, u32 offset,
 					void *buffer__opt, u32 buffer__szk)
diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c
index df697c74d519..2626706b6387 100644
--- a/kernel/trace/bpf_trace.c
+++ b/kernel/trace/bpf_trace.c
@@ -1378,6 +1378,7 @@  __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
 			       struct bpf_dynptr_kern *sig_ptr,
 			       struct bpf_key *trusted_keyring)
 {
+	void *data, *sig;
 	int ret;
 
 	if (trusted_keyring->has_ref) {
@@ -1394,10 +1395,16 @@  __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr_kern *data_ptr,
 			return ret;
 	}
 
-	return verify_pkcs7_signature(data_ptr->data,
-				      __bpf_dynptr_size(data_ptr),
-				      sig_ptr->data,
-				      __bpf_dynptr_size(sig_ptr),
+	data = bpf_dynptr_slice(data_ptr, 0, NULL, 0);
+	if (IS_ERR(data))
+		return PTR_ERR(data);
+
+	sig = bpf_dynptr_slice(sig_ptr, 0, NULL, 0);
+	if (IS_ERR(sig))
+		return PTR_ERR(sig);
+
+	return verify_pkcs7_signature(data, __bpf_dynptr_size(data_ptr),
+				      sig, __bpf_dynptr_size(sig_ptr),
 				      trusted_keyring->key,
 				      VERIFYING_UNSPECIFIED_SIGNATURE, NULL,
 				      NULL);