diff mbox series

[v3,bpf-next,2/3] selftests/bpf: add negative tests for new VFS based BPF kfuncs

Message ID 20240726085604.2369469-3-mattbobrowski@google.com (mailing list archive)
State Changes Requested
Delegated to: BPF
Headers show
Series introduce new VFS based BPF kfuncs | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success Generated files up to date; no warnings/errors; no diff in generated;
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: 661 this patch: 661
netdev/build_tools success Errors and warnings before: 0 this patch: 0
netdev/cc_maintainers warning 10 maintainers not CCed: shuah@kernel.org haoluo@google.com john.fastabend@gmail.com linux-kselftest@vger.kernel.org yonghong.song@linux.dev martin.lau@linux.dev mykolal@fb.com song@kernel.org eddyz87@gmail.com sdf@fomichev.me
netdev/build_clang success Errors and warnings before: 663 this patch: 663
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: 667 this patch: 667
netdev/checkpatch warning CHECK: extern prototypes should be avoided in .h files WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 100 exceeds 80 columns WARNING: line length of 125 exceeds 80 columns WARNING: line length of 81 exceeds 80 columns WARNING: line length of 90 exceeds 80 columns
netdev/build_clang_rust success No Rust files in patch. Skipping build
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0
bpf/vmtest-bpf-next-PR success PR summary
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Unittests
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-3 success Logs for Validate matrix.py
bpf/vmtest-bpf-next-VM_Test-5 success Logs for aarch64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-4 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-10 success Logs for aarch64-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-11 success Logs for s390x-gcc / build / build for s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 success Logs for s390x-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-17 success Logs for s390x-gcc / veristat
bpf/vmtest-bpf-next-VM_Test-18 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-20 success Logs for x86_64-gcc / build-release
bpf/vmtest-bpf-next-VM_Test-19 success Logs for x86_64-gcc / build / build for x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-28 success Logs for x86_64-llvm-17 / build / build for x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-34 success Logs for x86_64-llvm-17 / veristat
bpf/vmtest-bpf-next-VM_Test-6 success Logs for aarch64-gcc / test (test_maps, false, 360) / test_maps on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-9 success Logs for aarch64-gcc / test (test_verifier, false, 360) / test_verifier on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-13 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-16 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-29 success Logs for x86_64-llvm-17 / build-release / build for x86_64 with llvm-17-O2
bpf/vmtest-bpf-next-VM_Test-36 success Logs for x86_64-llvm-18 / build-release / build for x86_64 with llvm-18-O2
bpf/vmtest-bpf-next-VM_Test-35 success Logs for x86_64-llvm-18 / build / build for x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-42 success Logs for x86_64-llvm-18 / veristat
bpf/vmtest-bpf-next-VM_Test-8 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-7 success Logs for aarch64-gcc / test (test_progs, false, 360) / test_progs on aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / test (test_progs, false, 360) / test_progs on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-15 success Logs for s390x-gcc / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-21 success Logs for x86_64-gcc / test (test_maps, false, 360) / test_maps on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-24 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-25 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-26 success Logs for x86_64-gcc / test (test_verifier, false, 360) / test_verifier on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-27 success Logs for x86_64-gcc / veristat / veristat on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-30 success Logs for x86_64-llvm-17 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-31 success Logs for x86_64-llvm-17 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-33 success Logs for x86_64-llvm-17 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-32 success Logs for x86_64-llvm-17 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-17
bpf/vmtest-bpf-next-VM_Test-22 success Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs on x86_64 with gcc
bpf/vmtest-bpf-next-VM_Test-23 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-38 success Logs for x86_64-llvm-18 / test (test_progs, false, 360) / test_progs on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-37 success Logs for x86_64-llvm-18 / test (test_maps, false, 360) / test_maps on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-40 success Logs for x86_64-llvm-18 / test (test_progs_no_alu32, false, 360) / test_progs_no_alu32 on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-41 success Logs for x86_64-llvm-18 / test (test_verifier, false, 360) / test_verifier on x86_64 with llvm-18
bpf/vmtest-bpf-next-VM_Test-39 success Logs for x86_64-llvm-18 / test (test_progs_cpuv4, false, 360) / test_progs_cpuv4 on x86_64 with llvm-18

Commit Message

Matt Bobrowski July 26, 2024, 8:56 a.m. UTC
Add a bunch of negative selftests responsible for asserting that the
BPF verifier successfully rejects a BPF program load when the
underlying BPF program misuses one of the newly introduced VFS based
BPF kfuncs.

The following VFS based BPF kfuncs are extensively tested within this
new selftest:

* struct file *bpf_get_task_exe_file(struct task_struct *);
* void bpf_put_file(struct file *);
* int bpf_path_d_path(struct path *, char *, size_t);

Signed-off-by: Matt Bobrowski <mattbobrowski@google.com>
---
 .../testing/selftests/bpf/bpf_experimental.h  |  26 +++
 .../selftests/bpf/prog_tests/verifier.c       |   2 +
 .../selftests/bpf/progs/verifier_vfs_reject.c | 196 ++++++++++++++++++
 3 files changed, 224 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/progs/verifier_vfs_reject.c

Comments

Song Liu July 26, 2024, 11:38 p.m. UTC | #1
On Fri, Jul 26, 2024 at 1:56 AM Matt Bobrowski <mattbobrowski@google.com> wrote:
>
> Add a bunch of negative selftests responsible for asserting that the
> BPF verifier successfully rejects a BPF program load when the
> underlying BPF program misuses one of the newly introduced VFS based
> BPF kfuncs.

Negative tests are great. Thanks for adding them.

A few nitpicks below.

[...]

> diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
> index 828556cdc2f0..8a1ed62b4ed1 100644
> --- a/tools/testing/selftests/bpf/bpf_experimental.h
> +++ b/tools/testing/selftests/bpf/bpf_experimental.h
> @@ -195,6 +195,32 @@ extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym;
>   */
>  extern void bpf_throw(u64 cookie) __ksym;
>
> +/* Description
> + *     Acquire a reference on the exe_file member field belonging to the
> + *     mm_struct that is nested within the supplied task_struct. The supplied
> + *     task_struct must be trusted/referenced.
> + * Returns
> + *     A referenced file pointer pointing to the exe_file member field of the
> + *     mm_struct nested in the supplied task_struct, or NULL.
> + */
> +extern struct file *bpf_get_task_exe_file(struct task_struct *task) __ksym;
> +
> +/* Description
> + *     Release a reference on the supplied file. The supplied file must be
> + *     trusted/referenced.

Probably replace "trusted/referenced" with "acquired".

> + */
> +extern void bpf_put_file(struct file *file) __ksym;
> +
> +/* Description
> + *     Resolve a pathname for the supplied path and store it in the supplied
> + *     buffer. The supplied path must be trusted/referenced.
> + * Returns
> + *     A positive integer corresponding to the length of the resolved pathname,
> + *     including the NULL termination character, stored in the supplied
> + *     buffer. On error, a negative integer is returned.
> + */
> +extern int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) __ksym;
> +

In my environment, we already have these declarations in vmlinux.h.
So maybe we don't need to add them manually?


>  /* This macro must be used to mark the exception callback corresponding to the
>   * main program. For example:
>   *
> diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
> index 67a49d12472c..14d74ba2188e 100644
> --- a/tools/testing/selftests/bpf/prog_tests/verifier.c
> +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
> @@ -85,6 +85,7 @@
>  #include "verifier_value_or_null.skel.h"
>  #include "verifier_value_ptr_arith.skel.h"
>  #include "verifier_var_off.skel.h"
> +#include "verifier_vfs_reject.skel.h"
>  #include "verifier_xadd.skel.h"
>  #include "verifier_xdp.skel.h"
>  #include "verifier_xdp_direct_packet_access.skel.h"
> @@ -205,6 +206,7 @@ void test_verifier_value(void)                { RUN(verifier_value); }
>  void test_verifier_value_illegal_alu(void)    { RUN(verifier_value_illegal_alu); }
>  void test_verifier_value_or_null(void)        { RUN(verifier_value_or_null); }
>  void test_verifier_var_off(void)              { RUN(verifier_var_off); }
> +void test_verifier_vfs_reject(void)          { RUN(verifier_vfs_reject); }
>  void test_verifier_xadd(void)                 { RUN(verifier_xadd); }
>  void test_verifier_xdp(void)                  { RUN(verifier_xdp); }
>  void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
> diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
> new file mode 100644
> index 000000000000..27666a8ef78a
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
> @@ -0,0 +1,196 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2024 Google LLC. */
> +
> +#include <vmlinux.h>
> +#include <bpf/bpf_helpers.h>
> +#include <bpf/bpf_tracing.h>
> +#include <linux/limits.h>
> +
> +#include "bpf_misc.h"
> +#include "bpf_experimental.h"
> +
> +static char buf[PATH_MAX];
> +
> +SEC("lsm.s/file_open")
> +__failure __msg("Possibly NULL pointer passed to trusted arg0")
> +int BPF_PROG(get_task_exe_file_kfunc_null)
> +{
> +       struct file *acquired;
> +
> +       /* Can't pass a NULL pointer to bpf_get_task_exe_file(). */
> +       acquired = bpf_get_task_exe_file(NULL);
> +       if (!acquired)
> +               return 0;
> +
> +       bpf_put_file(acquired);
> +       return 0;
> +}
> +
> +SEC("lsm.s/inode_getxattr")
> +__failure __msg("arg#0 pointer type STRUCT task_struct must point to scalar, or struct with scalar")
> +int BPF_PROG(get_task_exe_file_kfunc_fp)
> +{
> +       u64 x;
> +       struct file *acquired;
> +       struct task_struct *fp;

"fp" is a weird name for a task_struct pointer.

Other than these:

Acked-by: Song Liu <song@kernel.org>

> +
> +       fp = (struct task_struct *)&x;
> +       /* Can't pass random frame pointer to bpf_get_task_exe_file(). */
> +       acquired = bpf_get_task_exe_file(fp);
> +       if (!acquired)
> +               return 0;
> +
> +       bpf_put_file(acquired);
> +       return 0;
> +}
> +
[...]
Matt Bobrowski July 28, 2024, 7:34 p.m. UTC | #2
On Fri, Jul 26, 2024 at 04:38:32PM -0700, Song Liu wrote:
> On Fri, Jul 26, 2024 at 1:56 AM Matt Bobrowski <mattbobrowski@google.com> wrote:
> >
> > Add a bunch of negative selftests responsible for asserting that the
> > BPF verifier successfully rejects a BPF program load when the
> > underlying BPF program misuses one of the newly introduced VFS based
> > BPF kfuncs.
> 
> Negative tests are great. Thanks for adding them.
> 
> A few nitpicks below.

Thanks for the review!

> > diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
> > index 828556cdc2f0..8a1ed62b4ed1 100644
> > --- a/tools/testing/selftests/bpf/bpf_experimental.h
> > +++ b/tools/testing/selftests/bpf/bpf_experimental.h
> > @@ -195,6 +195,32 @@ extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym;
> >   */
> >  extern void bpf_throw(u64 cookie) __ksym;
> >
> > +/* Description
> > + *     Acquire a reference on the exe_file member field belonging to the
> > + *     mm_struct that is nested within the supplied task_struct. The supplied
> > + *     task_struct must be trusted/referenced.
> > + * Returns
> > + *     A referenced file pointer pointing to the exe_file member field of the
> > + *     mm_struct nested in the supplied task_struct, or NULL.
> > + */
> > +extern struct file *bpf_get_task_exe_file(struct task_struct *task) __ksym;
> > +
> > +/* Description
> > + *     Release a reference on the supplied file. The supplied file must be
> > + *     trusted/referenced.
> 
> Probably replace "trusted/referenced" with "acquired".

Done.

> > + */
> > +extern void bpf_put_file(struct file *file) __ksym;
> > +
> > +/* Description
> > + *     Resolve a pathname for the supplied path and store it in the supplied
> > + *     buffer. The supplied path must be trusted/referenced.
> > + * Returns
> > + *     A positive integer corresponding to the length of the resolved pathname,
> > + *     including the NULL termination character, stored in the supplied
> > + *     buffer. On error, a negative integer is returned.
> > + */
> > +extern int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) __ksym;
> > +
> 
> In my environment, we already have these declarations in vmlinux.h.
> So maybe we don't need to add them manually?

Right, but that's probably when building vmlinux.h using the latest
pahole I imagine? Those not using the latest pahole will probably
won't already see these BPF kfuncs within the generated vmlinux.h.

> >  /* This macro must be used to mark the exception callback corresponding to the
> >   * main program. For example:
> >   *
> > diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
> > index 67a49d12472c..14d74ba2188e 100644
> > --- a/tools/testing/selftests/bpf/prog_tests/verifier.c
> > +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
> > @@ -85,6 +85,7 @@
> >  #include "verifier_value_or_null.skel.h"
> >  #include "verifier_value_ptr_arith.skel.h"
> >  #include "verifier_var_off.skel.h"
> > +#include "verifier_vfs_reject.skel.h"
> >  #include "verifier_xadd.skel.h"
> >  #include "verifier_xdp.skel.h"
> >  #include "verifier_xdp_direct_packet_access.skel.h"
> > @@ -205,6 +206,7 @@ void test_verifier_value(void)                { RUN(verifier_value); }
> >  void test_verifier_value_illegal_alu(void)    { RUN(verifier_value_illegal_alu); }
> >  void test_verifier_value_or_null(void)        { RUN(verifier_value_or_null); }
> >  void test_verifier_var_off(void)              { RUN(verifier_var_off); }
> > +void test_verifier_vfs_reject(void)          { RUN(verifier_vfs_reject); }
> >  void test_verifier_xadd(void)                 { RUN(verifier_xadd); }
> >  void test_verifier_xdp(void)                  { RUN(verifier_xdp); }
> >  void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
> > diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
> > new file mode 100644
> > index 000000000000..27666a8ef78a
> > --- /dev/null
> > +++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
> > @@ -0,0 +1,196 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2024 Google LLC. */
> > +
> > +#include <vmlinux.h>
> > +#include <bpf/bpf_helpers.h>
> > +#include <bpf/bpf_tracing.h>
> > +#include <linux/limits.h>
> > +
> > +#include "bpf_misc.h"
> > +#include "bpf_experimental.h"
> > +
> > +static char buf[PATH_MAX];
> > +
> > +SEC("lsm.s/file_open")
> > +__failure __msg("Possibly NULL pointer passed to trusted arg0")
> > +int BPF_PROG(get_task_exe_file_kfunc_null)
> > +{
> > +       struct file *acquired;
> > +
> > +       /* Can't pass a NULL pointer to bpf_get_task_exe_file(). */
> > +       acquired = bpf_get_task_exe_file(NULL);
> > +       if (!acquired)
> > +               return 0;
> > +
> > +       bpf_put_file(acquired);
> > +       return 0;
> > +}
> > +
> > +SEC("lsm.s/inode_getxattr")
> > +__failure __msg("arg#0 pointer type STRUCT task_struct must point to scalar, or struct with scalar")
> > +int BPF_PROG(get_task_exe_file_kfunc_fp)
> > +{
> > +       u64 x;
> > +       struct file *acquired;
> > +       struct task_struct *fp;
> 
> "fp" is a weird name for a task_struct pointer.

OK, just want to make it clear that it was a pointer to something that
exists on the current stack frame. Happy to change the name to task or
something. Done.

> Other than these:
> 
> Acked-by: Song Liu <song@kernel.org>
> 
> > +
> > +       fp = (struct task_struct *)&x;
> > +       /* Can't pass random frame pointer to bpf_get_task_exe_file(). */
> > +       acquired = bpf_get_task_exe_file(fp);
> > +       if (!acquired)
> > +               return 0;
> > +
> > +       bpf_put_file(acquired);
> > +       return 0;
> > +}
> > +
> [...]
/M
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/bpf_experimental.h b/tools/testing/selftests/bpf/bpf_experimental.h
index 828556cdc2f0..8a1ed62b4ed1 100644
--- a/tools/testing/selftests/bpf/bpf_experimental.h
+++ b/tools/testing/selftests/bpf/bpf_experimental.h
@@ -195,6 +195,32 @@  extern void bpf_iter_task_vma_destroy(struct bpf_iter_task_vma *it) __ksym;
  */
 extern void bpf_throw(u64 cookie) __ksym;
 
+/* Description
+ *	Acquire a reference on the exe_file member field belonging to the
+ *	mm_struct that is nested within the supplied task_struct. The supplied
+ *	task_struct must be trusted/referenced.
+ * Returns
+ *	A referenced file pointer pointing to the exe_file member field of the
+ *	mm_struct nested in the supplied task_struct, or NULL.
+ */
+extern struct file *bpf_get_task_exe_file(struct task_struct *task) __ksym;
+
+/* Description
+ *	Release a reference on the supplied file. The supplied file must be
+ *	trusted/referenced.
+ */
+extern void bpf_put_file(struct file *file) __ksym;
+
+/* Description
+ *	Resolve a pathname for the supplied path and store it in the supplied
+ *	buffer. The supplied path must be trusted/referenced.
+ * Returns
+ *	A positive integer corresponding to the length of the resolved pathname,
+ *	including the NULL termination character, stored in the supplied
+ *	buffer. On error, a negative integer is returned.
+ */
+extern int bpf_path_d_path(struct path *path, char *buf, size_t buf__sz) __ksym;
+
 /* This macro must be used to mark the exception callback corresponding to the
  * main program. For example:
  *
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 67a49d12472c..14d74ba2188e 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -85,6 +85,7 @@ 
 #include "verifier_value_or_null.skel.h"
 #include "verifier_value_ptr_arith.skel.h"
 #include "verifier_var_off.skel.h"
+#include "verifier_vfs_reject.skel.h"
 #include "verifier_xadd.skel.h"
 #include "verifier_xdp.skel.h"
 #include "verifier_xdp_direct_packet_access.skel.h"
@@ -205,6 +206,7 @@  void test_verifier_value(void)                { RUN(verifier_value); }
 void test_verifier_value_illegal_alu(void)    { RUN(verifier_value_illegal_alu); }
 void test_verifier_value_or_null(void)        { RUN(verifier_value_or_null); }
 void test_verifier_var_off(void)              { RUN(verifier_var_off); }
+void test_verifier_vfs_reject(void)	      { RUN(verifier_vfs_reject); }
 void test_verifier_xadd(void)                 { RUN(verifier_xadd); }
 void test_verifier_xdp(void)                  { RUN(verifier_xdp); }
 void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); }
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
new file mode 100644
index 000000000000..27666a8ef78a
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -0,0 +1,196 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Google LLC. */
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <linux/limits.h>
+
+#include "bpf_misc.h"
+#include "bpf_experimental.h"
+
+static char buf[PATH_MAX];
+
+SEC("lsm.s/file_open")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
+int BPF_PROG(get_task_exe_file_kfunc_null)
+{
+	struct file *acquired;
+
+	/* Can't pass a NULL pointer to bpf_get_task_exe_file(). */
+	acquired = bpf_get_task_exe_file(NULL);
+	if (!acquired)
+		return 0;
+
+	bpf_put_file(acquired);
+	return 0;
+}
+
+SEC("lsm.s/inode_getxattr")
+__failure __msg("arg#0 pointer type STRUCT task_struct must point to scalar, or struct with scalar")
+int BPF_PROG(get_task_exe_file_kfunc_fp)
+{
+	u64 x;
+	struct file *acquired;
+	struct task_struct *fp;
+
+	fp = (struct task_struct *)&x;
+	/* Can't pass random frame pointer to bpf_get_task_exe_file(). */
+	acquired = bpf_get_task_exe_file(fp);
+	if (!acquired)
+		return 0;
+
+	bpf_put_file(acquired);
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("R1 must be referenced or trusted")
+int BPF_PROG(get_task_exe_file_kfunc_untrusted)
+{
+	struct file *acquired;
+	struct task_struct *parent;
+
+	/* Walking a trusted struct task_struct returned from
+	 * bpf_get_current_task_btf() yields an untrusted pointer.
+	 */
+	parent = bpf_get_current_task_btf()->parent;
+	/* Can't pass untrusted pointer to bpf_get_task_exe_file(). */
+	acquired = bpf_get_task_exe_file(parent);
+	if (!acquired)
+		return 0;
+
+	bpf_put_file(acquired);
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("Unreleased reference")
+int BPF_PROG(get_task_exe_file_kfunc_unreleased)
+{
+	struct file *acquired;
+
+	acquired = bpf_get_task_exe_file(bpf_get_current_task_btf());
+	if (!acquired)
+		return 0;
+
+	/* Acquired but never released. */
+	return 0;
+}
+
+SEC("lsm/file_open")
+__failure __msg("program must be sleepable to call sleepable kfunc bpf_get_task_exe_file")
+int BPF_PROG(put_file_kfunc_not_sleepable, struct file *file)
+{
+	struct file *acquired;
+
+	/* Can't call bpf_get_task_file() from non-sleepable BPF LSM program
+	 * types.
+	 */
+	acquired = bpf_get_task_exe_file(bpf_get_current_task_btf());
+	if (!acquired)
+		return 0;
+
+	bpf_put_file(acquired);
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("release kernel function bpf_put_file expects")
+int BPF_PROG(put_file_kfunc_unacquired, struct file *file)
+{
+	/* Can't release an unacquired pointer. */
+	bpf_put_file(file);
+	return 0;
+}
+
+SEC("tp_btf/task_newtask")
+__failure __msg("calling kernel function bpf_get_task_exe_file is not allowed")
+int BPF_PROG(get_task_exe_file_kfunc_not_lsm_prog, struct task_struct *task)
+{
+	struct file *acquired;
+
+	/* bpf_get_task_exe_file() can only be called from a sleepable BPF LSM
+	 * program type.
+	 */
+	acquired = bpf_get_task_exe_file(bpf_get_current_task_btf());
+	if (!acquired)
+		return 0;
+
+	bpf_put_file(acquired);
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("Possibly NULL pointer passed to trusted arg0")
+int BPF_PROG(path_d_path_kfunc_null)
+{
+	/* Can't pass NULL value to bpf_path_d_path() kfunc. */
+	bpf_path_d_path(NULL, buf, sizeof(buf));
+	return 0;
+}
+
+SEC("fentry/vfs_open")
+__failure __msg("calling kernel function bpf_path_d_path is not allowed")
+int BPF_PROG(path_d_path_kfunc_non_lsm, struct path *path, struct file *f)
+{
+	/* Calling bpf_path_d_path() from a non-sleepable and non-LSM
+	 * BPF program isn't permitted.
+	 */
+	bpf_path_d_path(path, buf, sizeof(buf));
+	return 0;
+}
+
+SEC("lsm.s/task_alloc")
+__failure __msg("R1 must be referenced or trusted")
+int BPF_PROG(path_d_path_kfunc_untrusted_from_argument, struct task_struct *task)
+{
+	struct path *root;
+
+	/* Walking a trusted argument typically yields an untrusted
+	 * pointer. This is one example of that.
+	 */
+	root = &task->fs->root;
+	bpf_path_d_path(root, buf, sizeof(buf));
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("R1 must be referenced or trusted")
+int BPF_PROG(path_d_path_kfunc_untrusted_from_current)
+{
+	struct path *pwd;
+	struct task_struct *current;
+
+	current = bpf_get_current_task_btf();
+	/* Walking a trusted pointer returned from bpf_get_current_task_btf()
+	 * yields an untrusted pointer.
+	 */
+	pwd = &current->fs->pwd;
+	bpf_path_d_path(pwd, buf, sizeof(buf));
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("kernel function bpf_path_d_path args#0 expected pointer to STRUCT path but R1 has a pointer to STRUCT file")
+int BPF_PROG(path_d_path_kfunc_type_mismatch, struct file *file)
+{
+	bpf_path_d_path((struct path *)&file->f_task_work, buf, sizeof(buf));
+	return 0;
+}
+
+SEC("lsm.s/file_open")
+__failure __msg("invalid access to map value, value_size=4096 off=0 size=8192")
+int BPF_PROG(path_d_path_kfunc_invalid_buf_sz, struct file *file)
+{
+	/* bpf_path_d_path() enforces a constraint on the buffer size supplied
+	 * by the BPF LSM program via the __sz annotation. buf here is set to
+	 * PATH_MAX, so let's ensure that the BPF verifier rejects BPF_PROG_LOAD
+	 * attempts if the supplied size and the actual size of the buffer
+	 * mismatches.
+	 */
+	bpf_path_d_path(&file->f_path, buf, PATH_MAX * 2);
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";