diff mbox series

[v1,bpf-next] bpf: Update kfunc __sz documentation

Message ID 20230214043350.3497406-1-joannelkoong@gmail.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series [v1,bpf-next] bpf: Update kfunc __sz documentation | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR success PR summary
netdev/tree_selection success Clearly marked for bpf-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Single patches do not need cover letters
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 9 maintainers not CCed: john.fastabend@gmail.com sdf@google.com corbet@lwn.net linux-doc@vger.kernel.org jolsa@kernel.org song@kernel.org haoluo@google.com yhs@fb.com kpsingh@kernel.org
netdev/build_clang success Errors and warnings before: 0 this patch: 0
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
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: 0 this patch: 0
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 41 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-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-7 success Logs for llvm-toolchain
bpf/vmtest-bpf-next-VM_Test-8 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-5 success Logs for build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-6 success Logs for build for x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-2 success Logs for build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-3 success Logs for build for aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-4 success Logs for build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for test_maps on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-12 success Logs for test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for test_maps on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-14 success Logs for test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-17 success Logs for test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-18 success Logs for test_progs on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-19 success Logs for test_progs_no_alu32 on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-22 success Logs for test_progs_no_alu32 on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 success Logs for test_progs_no_alu32 on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-24 success Logs for test_progs_no_alu32_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-25 success Logs for test_progs_no_alu32_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-27 success Logs for test_progs_no_alu32_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for test_progs_no_alu32_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-29 success Logs for test_progs_parallel on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-32 success Logs for test_progs_parallel on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-33 success Logs for test_progs_parallel on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-34 success Logs for test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-35 success Logs for test_verifier on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-37 success Logs for test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-38 success Logs for test_verifier on x86_64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-15 fail Logs for test_progs on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-20 success Logs for test_progs_no_alu32 on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-30 success Logs for test_progs_parallel on aarch64 with llvm-16
bpf/vmtest-bpf-next-VM_Test-36 success Logs for test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-31 success Logs for test_progs_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-26 success Logs for test_progs_no_alu32_parallel on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-11 success Logs for test_maps on s390x with gcc

Commit Message

Joanne Koong Feb. 14, 2023, 4:33 a.m. UTC
A bpf program calling a kfunc with a __sz-annotated arg must explicitly
initialize the stack themselves if the pointer to the memory region is
a pointer to the stack. This is because in the verifier, we do not
explicitly initialize the stack space for reg type PTR_TO_STACK
kfunc args. Thus, the verifier will reject the program with:

invalid indirect read from stack
arg#0 arg#1 memory, len pair leads to invalid memory access

Alternatively, the verifier could support initializing the stack
space on behalf of the program for KF_ARG_PTR_TO_MEM_SIZE args,
but this has some drawbacks. For example this would not allow the
verifier to reject a program for passing in an uninitialized
PTR_TO_STACK for an arg that should have valid data. Another example is
that since there's no current way in a kfunc to differentiate between
whether the arg should be treated as uninitialized or not, additional
check_mem_access calls would need to be called even on PTR_TO_STACKs
that have been initialized, which is inefficient. Please note
that non-kfuncs don't have this problem because of the MEM_UNINIT tag;
only if the arg is tagged as MEM_UNINIT, then do we call
check_mem_access byte-by-byte for the size of the buffer.

Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
---
 Documentation/bpf/kfuncs.rst | 35 +++++++++++++++++++++++++++++++----
 1 file changed, 31 insertions(+), 4 deletions(-)

Comments

Alexei Starovoitov Feb. 14, 2023, 8:57 p.m. UTC | #1
On Mon, Feb 13, 2023 at 8:35 PM Joanne Koong <joannelkoong@gmail.com> wrote:
>
> A bpf program calling a kfunc with a __sz-annotated arg must explicitly
> initialize the stack themselves if the pointer to the memory region is
> a pointer to the stack. This is because in the verifier, we do not
> explicitly initialize the stack space for reg type PTR_TO_STACK
> kfunc args. Thus, the verifier will reject the program with:
>
> invalid indirect read from stack
> arg#0 arg#1 memory, len pair leads to invalid memory access
>
> Alternatively, the verifier could support initializing the stack
> space on behalf of the program for KF_ARG_PTR_TO_MEM_SIZE args,
> but this has some drawbacks. For example this would not allow the
> verifier to reject a program for passing in an uninitialized
> PTR_TO_STACK for an arg that should have valid data. Another example is
> that since there's no current way in a kfunc to differentiate between
> whether the arg should be treated as uninitialized or not, additional
> check_mem_access calls would need to be called even on PTR_TO_STACKs
> that have been initialized, which is inefficient. Please note
> that non-kfuncs don't have this problem because of the MEM_UNINIT tag;
> only if the arg is tagged as MEM_UNINIT, then do we call
> check_mem_access byte-by-byte for the size of the buffer.
>
> Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> ---
>  Documentation/bpf/kfuncs.rst | 35 +++++++++++++++++++++++++++++++----
>  1 file changed, 31 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
> index ca96ef3f6896..97497a7879d6 100644
> --- a/Documentation/bpf/kfuncs.rst
> +++ b/Documentation/bpf/kfuncs.rst
> @@ -71,10 +71,37 @@ An example is given below::
>          ...
>          }
>
> -Here, the verifier will treat first argument as a PTR_TO_MEM, and second
> -argument as its size. By default, without __sz annotation, the size of the type
> -of the pointer is used. Without __sz annotation, a kfunc cannot accept a void
> -pointer.
> +Here, the verifier will treat first argument (KF_ARG_PTR_TO_MEM_SIZE) as a
> +pointer to the memory region and second argument as its size. By default,
> +without __sz annotation, the size of the type of the pointer is used. Without
> +__sz annotation, a kfunc cannot accept a void pointer.
> +
> +Please note that if the memory is on the stack, the stack space must be
> +explicitly initialized by the program. For example:
> +
> +.. code-block:: c
> +
> +       SEC("tc")
> +       int prog(struct __sk_buff *skb)
> +       {
> +               char buf[8];
> +
> +               bpf_memzero(buf, sizeof(buf));
> +       ...
> +       }
> +
> +should be
> +
> +.. code-block:: c
> +
> +       SEC("tc")
> +       int prog(struct __sk_buff *skb)
> +       {
> +               char buf[8] = {};
> +
> +               bpf_memzero(buf, sizeof(buf));

Actually we might go the other way.
Instead of asking users to explicitly init things
we will allow uninit memory.
See this discussion:
https://lore.kernel.org/bpf/082fd8451321a832f334882a1872b5cee240d811.camel@gmail.com/

Eduard, is about to send those verifier patches.

In parallel we can relax __sz to accept uninit under allow_uninit_stack.
Joanne Koong Feb. 18, 2023, 1:24 a.m. UTC | #2
On Tue, Feb 14, 2023 at 12:57 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
>
> On Mon, Feb 13, 2023 at 8:35 PM Joanne Koong <joannelkoong@gmail.com> wrote:
> >
> > A bpf program calling a kfunc with a __sz-annotated arg must explicitly
> > initialize the stack themselves if the pointer to the memory region is
> > a pointer to the stack. This is because in the verifier, we do not
> > explicitly initialize the stack space for reg type PTR_TO_STACK
> > kfunc args. Thus, the verifier will reject the program with:
> >
> > invalid indirect read from stack
> > arg#0 arg#1 memory, len pair leads to invalid memory access
> >
> > Alternatively, the verifier could support initializing the stack
> > space on behalf of the program for KF_ARG_PTR_TO_MEM_SIZE args,
> > but this has some drawbacks. For example this would not allow the
> > verifier to reject a program for passing in an uninitialized
> > PTR_TO_STACK for an arg that should have valid data. Another example is
> > that since there's no current way in a kfunc to differentiate between
> > whether the arg should be treated as uninitialized or not, additional
> > check_mem_access calls would need to be called even on PTR_TO_STACKs
> > that have been initialized, which is inefficient. Please note
> > that non-kfuncs don't have this problem because of the MEM_UNINIT tag;
> > only if the arg is tagged as MEM_UNINIT, then do we call
> > check_mem_access byte-by-byte for the size of the buffer.
> >
> > Signed-off-by: Joanne Koong <joannelkoong@gmail.com>
> > ---
> >  Documentation/bpf/kfuncs.rst | 35 +++++++++++++++++++++++++++++++----
> >  1 file changed, 31 insertions(+), 4 deletions(-)
> >
> > diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
> > index ca96ef3f6896..97497a7879d6 100644
> > --- a/Documentation/bpf/kfuncs.rst
> > +++ b/Documentation/bpf/kfuncs.rst
> > @@ -71,10 +71,37 @@ An example is given below::
> >          ...
> >          }
> >
> > -Here, the verifier will treat first argument as a PTR_TO_MEM, and second
> > -argument as its size. By default, without __sz annotation, the size of the type
> > -of the pointer is used. Without __sz annotation, a kfunc cannot accept a void
> > -pointer.
> > +Here, the verifier will treat first argument (KF_ARG_PTR_TO_MEM_SIZE) as a
> > +pointer to the memory region and second argument as its size. By default,
> > +without __sz annotation, the size of the type of the pointer is used. Without
> > +__sz annotation, a kfunc cannot accept a void pointer.
> > +
> > +Please note that if the memory is on the stack, the stack space must be
> > +explicitly initialized by the program. For example:
> > +
> > +.. code-block:: c
> > +
> > +       SEC("tc")
> > +       int prog(struct __sk_buff *skb)
> > +       {
> > +               char buf[8];
> > +
> > +               bpf_memzero(buf, sizeof(buf));
> > +       ...
> > +       }
> > +
> > +should be
> > +
> > +.. code-block:: c
> > +
> > +       SEC("tc")
> > +       int prog(struct __sk_buff *skb)
> > +       {
> > +               char buf[8] = {};
> > +
> > +               bpf_memzero(buf, sizeof(buf));
>
> Actually we might go the other way.
> Instead of asking users to explicitly init things
> we will allow uninit memory.
> See this discussion:
> https://lore.kernel.org/bpf/082fd8451321a832f334882a1872b5cee240d811.camel@gmail.com/
>
> Eduard, is about to send those verifier patches.
>
> In parallel we can relax __sz to accept uninit under allow_uninit_stack.

in that case, for kfuncs it needs to grow the stack state if the
buffer + __sz is beyond the current allocated stack, or else
check_stack_range_initialized() will automatically fail if the user
tries to pass in an uninitialized buffer. i have a local patch for
this in my tree, I'll tidy it up and submit it next week
diff mbox series

Patch

diff --git a/Documentation/bpf/kfuncs.rst b/Documentation/bpf/kfuncs.rst
index ca96ef3f6896..97497a7879d6 100644
--- a/Documentation/bpf/kfuncs.rst
+++ b/Documentation/bpf/kfuncs.rst
@@ -71,10 +71,37 @@  An example is given below::
         ...
         }
 
-Here, the verifier will treat first argument as a PTR_TO_MEM, and second
-argument as its size. By default, without __sz annotation, the size of the type
-of the pointer is used. Without __sz annotation, a kfunc cannot accept a void
-pointer.
+Here, the verifier will treat first argument (KF_ARG_PTR_TO_MEM_SIZE) as a
+pointer to the memory region and second argument as its size. By default,
+without __sz annotation, the size of the type of the pointer is used. Without
+__sz annotation, a kfunc cannot accept a void pointer.
+
+Please note that if the memory is on the stack, the stack space must be
+explicitly initialized by the program. For example:
+
+.. code-block:: c
+
+	SEC("tc")
+	int prog(struct __sk_buff *skb)
+	{
+		char buf[8];
+
+		bpf_memzero(buf, sizeof(buf));
+	...
+	}
+
+should be
+
+.. code-block:: c
+
+	SEC("tc")
+	int prog(struct __sk_buff *skb)
+	{
+		char buf[8] = {};
+
+		bpf_memzero(buf, sizeof(buf));
+	...
+	}
 
 2.2.2 __k Annotation
 --------------------