diff mbox series

[bpf-next] selftests/bpf: validate eliminated global subprog is not freplaceable

Message ID 20231201013006.910349-1-andrii@kernel.org (mailing list archive)
State Superseded
Delegated to: BPF
Headers show
Series [bpf-next] selftests/bpf: validate eliminated global subprog is not freplaceable | expand

Checks

Context Check Description
bpf/vmtest-bpf-next-PR fail PR summary
bpf/vmtest-bpf-next-VM_Test-0 success Logs for Lint
bpf/vmtest-bpf-next-VM_Test-1 success Logs for ShellCheck
bpf/vmtest-bpf-next-VM_Test-2 success Logs for Validate matrix.py
netdev/series_format success Single patches do not need cover letters
netdev/tree_selection success Clearly marked for bpf-next, async
netdev/ynl success SINGLE THREAD; Generated files up to date; no warnings/errors;
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: 8 this patch: 8
netdev/cc_maintainers warning 12 maintainers not CCed: haoluo@google.com jolsa@kernel.org kpsingh@kernel.org martin.lau@linux.dev john.fastabend@gmail.com mykolal@fb.com yonghong.song@linux.dev song@kernel.org eddyz87@gmail.com shuah@kernel.org sdf@google.com linux-kselftest@vger.kernel.org
netdev/build_clang success Errors and warnings before: 8 this patch: 8
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: 8 this patch: 8
netdev/checkpatch fail ERROR: trailing whitespace WARNING: Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? WARNING: line length of 100 exceeds 80 columns WARNING: line length of 84 exceeds 80 columns WARNING: line length of 86 exceeds 80 columns WARNING: line length of 89 exceeds 80 columns WARNING: line length of 91 exceeds 80 columns WARNING: line length of 95 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-VM_Test-3 success Logs for aarch64-gcc / build / build for aarch64 with gcc
bpf/vmtest-bpf-next-VM_Test-8 success Logs for aarch64-gcc / veristat
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-6 fail 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-5 fail 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-15 success Logs for set-matrix
bpf/vmtest-bpf-next-VM_Test-14 success Logs for s390x-gcc / veristat
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-18 fail Logs for x86_64-gcc / test (test_progs, false, 360) / test_progs 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-19 fail 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-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-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-24 success Logs for x86_64-llvm-16 / build / build for x86_64 with llvm-16
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-27 fail 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-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 fail 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-29 success Logs for x86_64-llvm-16 / veristat
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-13 success Logs for s390x-gcc / test (test_verifier, false, 360) / test_verifier on s390x with gcc
bpf/vmtest-bpf-next-VM_Test-12 fail 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-10 success Logs for s390x-gcc / test (test_maps, false, 360) / test_maps on s390x with gcc
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

Andrii Nakryiko Dec. 1, 2023, 1:30 a.m. UTC
Add selftest that establishes dead code-eliminated valid global subprog
(global_dead) and makes sure that it's not possible to freplace it, as
it's effectively not there. This test will fail with unexpected success
before 2afae08c9dcb ("bpf: Validate global subprogs lazily").

Suggested-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 .../bpf/prog_tests/global_func_dead_code.c    | 60 +++++++++++++++++++
 .../bpf/progs/freplace_dead_global_func.c     | 11 ++++
 .../bpf/progs/verifier_global_subprogs.c      | 32 ++++++----
 3 files changed, 92 insertions(+), 11 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c
 create mode 100644 tools/testing/selftests/bpf/progs/freplace_dead_global_func.c

Comments

Eduard Zingerman Dec. 1, 2023, 3:16 p.m. UTC | #1
On Thu, 2023-11-30 at 17:30 -0800, Andrii Nakryiko wrote:
> Add selftest that establishes dead code-eliminated valid global subprog
> (global_dead) and makes sure that it's not possible to freplace it, as
> it's effectively not there. This test will fail with unexpected success
> before 2afae08c9dcb ("bpf: Validate global subprogs lazily").
> 
> Suggested-by: Alexei Starovoitov <ast@kernel.org>
> Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> ---

Acked-by: Eduard Zingerman <eddyz87@gmail.com>

[...]
> diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c b/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c
[...]
> +void test_global_func_dead_code(void)
> +{
[...]
> +	ASSERT_HAS_SUBSTR(log_buf, "Subprog global_dead doesn't exist", "dead_subprog_missing_msg");

Nit: the log is not printed if verbose tests execution is requested.

[...]

> index a0a5efd1caa1..7f9b21a1c5a7 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> @@ -10,25 +10,31 @@
>  
>  int arr[1];
>  int unkn_idx;
> +const volatile bool call_dead_subprog = false;
>  
> -__noinline long global_bad(void)
> +__noinline long global_bad(int x)
>  {
> -	return arr[unkn_idx]; /* BOOM */
> +	return arr[unkn_idx] + x; /* BOOM */
>  }

Nit/question:
  Why change prototype from (void) to (int) here and elsewhere?
  Does not seem necessary for test logic.

[...]
Andrii Nakryiko Dec. 1, 2023, 7:17 p.m. UTC | #2
On Fri, Dec 1, 2023 at 7:16 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Thu, 2023-11-30 at 17:30 -0800, Andrii Nakryiko wrote:
> > Add selftest that establishes dead code-eliminated valid global subprog
> > (global_dead) and makes sure that it's not possible to freplace it, as
> > it's effectively not there. This test will fail with unexpected success
> > before 2afae08c9dcb ("bpf: Validate global subprogs lazily").
> >
> > Suggested-by: Alexei Starovoitov <ast@kernel.org>
> > Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
> > ---
>
> Acked-by: Eduard Zingerman <eddyz87@gmail.com>

Oops, didn't see your reply before sending v2. But there will be v3 anyway :)

>
> [...]
> > diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c b/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c
> [...]
> > +void test_global_func_dead_code(void)
> > +{
> [...]
> > +     ASSERT_HAS_SUBSTR(log_buf, "Subprog global_dead doesn't exist", "dead_subprog_missing_msg");
>
> Nit: the log is not printed if verbose tests execution is requested.

I'm not sure I understand. What do you expect to happen that's not
happening in verbose mode?

>
> [...]
>
> > index a0a5efd1caa1..7f9b21a1c5a7 100644
> > --- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> > +++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
> > @@ -10,25 +10,31 @@
> >
> >  int arr[1];
> >  int unkn_idx;
> > +const volatile bool call_dead_subprog = false;
> >
> > -__noinline long global_bad(void)
> > +__noinline long global_bad(int x)
> >  {
> > -     return arr[unkn_idx]; /* BOOM */
> > +     return arr[unkn_idx] + x; /* BOOM */
> >  }
>
> Nit/question:
>   Why change prototype from (void) to (int) here and elsewhere?
>   Does not seem necessary for test logic.

I had some troubles attaching freplace initially, but my freplace
skills were rusty :) I can try undoing this and leaving it as is.

>
> [...]
Eduard Zingerman Dec. 1, 2023, 7:20 p.m. UTC | #3
On Fri, 2023-12-01 at 11:17 -0800, Andrii Nakryiko wrote:
[...]
> > Acked-by: Eduard Zingerman <eddyz87@gmail.com>
> 
> Oops, didn't see your reply before sending v2. But there will be v3 anyway :)

np

[...]
> > Nit: the log is not printed if verbose tests execution is requested.
> 
> I'm not sure I understand. What do you expect to happen that's not
> happening in verbose mode?

I tried running this test -vvv and it did not print verification log
(admittedly this is the case with many tests in prog_tests/*.c).

[...]

> > Nit/question:
> >   Why change prototype from (void) to (int) here and elsewhere?
> >   Does not seem necessary for test logic.
> 
> I had some troubles attaching freplace initially, but my freplace
> skills were rusty :) I can try undoing this and leaving it as is.

No strong opinion, just curious.
Andrii Nakryiko Dec. 1, 2023, 7:26 p.m. UTC | #4
On Fri, Dec 1, 2023 at 11:20 AM Eduard Zingerman <eddyz87@gmail.com> wrote:
>
> On Fri, 2023-12-01 at 11:17 -0800, Andrii Nakryiko wrote:
> [...]
> > > Acked-by: Eduard Zingerman <eddyz87@gmail.com>
> >
> > Oops, didn't see your reply before sending v2. But there will be v3 anyway :)
>
> np
>
> [...]
> > > Nit: the log is not printed if verbose tests execution is requested.
> >
> > I'm not sure I understand. What do you expect to happen that's not
> > happening in verbose mode?
>
> I tried running this test -vvv and it did not print verification log
> (admittedly this is the case with many tests in prog_tests/*.c).

I think that's the test_loader.c feature, plus maybe some other tests
support this. This is not expected to magically work for all tests.
But also in this case we explicitly intercept the log, so it would be
too much trouble to both intercept and print it at the same time, IMO.
But if this assertion fails, we'll see the log, which is the most
important part. Also one can use veristat to get the log.

>
> [...]
>
> > > Nit/question:
> > >   Why change prototype from (void) to (int) here and elsewhere?
> > >   Does not seem necessary for test logic.
> >
> > I had some troubles attaching freplace initially, but my freplace
> > skills were rusty :) I can try undoing this and leaving it as is.
>
> No strong opinion, just curious.

I undid it, it all works now. As I said, I had freplace troubles and
was poking around with different aspects.
Eduard Zingerman Dec. 1, 2023, 8:02 p.m. UTC | #5
On Fri, 2023-12-01 at 11:26 -0800, Andrii Nakryiko wrote:
> > [...]
> > > > Nit: the log is not printed if verbose tests execution is requested.
> > > 
> > > I'm not sure I understand. What do you expect to happen that's not
> > > happening in verbose mode?
> > 
> > I tried running this test -vvv and it did not print verification log
> > (admittedly this is the case with many tests in prog_tests/*.c).
> 
> I think that's the test_loader.c feature, plus maybe some other tests
> support this. This is not expected to magically work for all tests.
> But also in this case we explicitly intercept the log, so it would be
> too much trouble to both intercept and print it at the same time, IMO.
> But if this assertion fails, we'll see the log, which is the most
> important part. Also one can use veristat to get the log.

Well, yes, that was the point of my rumbling.
When it's necessary to debug some such test one needs to modify it to
use *_opts() load variant etc. Veristat makes sense, however, so not
an issue.

> > > > Nit/question:
> > > >   Why change prototype from (void) to (int) here and elsewhere?
> > > >   Does not seem necessary for test logic.
> > > 
> > > I had some troubles attaching freplace initially, but my freplace
> > > skills were rusty :) I can try undoing this and leaving it as is.
> > 
> > No strong opinion, just curious.
> 
> I undid it, it all works now. As I said, I had freplace troubles and
> was poking around with different aspects.

Thank you.
diff mbox series

Patch

diff --git a/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c b/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c
new file mode 100644
index 000000000000..2716f6ccfe22
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/global_func_dead_code.c
@@ -0,0 +1,60 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+
+#include <test_progs.h>
+#include "verifier_global_subprogs.skel.h"
+#include "freplace_dead_global_func.skel.h"
+
+void test_global_func_dead_code(void)
+{
+	struct verifier_global_subprogs *tgt_skel = NULL;
+	struct freplace_dead_global_func *skel = NULL;
+	char log_buf[4096];
+	int err, tgt_fd;
+
+	/* first, try to load target with good global subprog */
+	tgt_skel = verifier_global_subprogs__open();
+	if (!ASSERT_OK_PTR(tgt_skel, "tgt_skel_good_open"))
+		return;
+
+	bpf_program__set_autoload(tgt_skel->progs.chained_global_func_calls_success, true);
+
+	err = verifier_global_subprogs__load(tgt_skel);
+	if (!ASSERT_OK(err, "tgt_skel_good_load"))
+		goto out;
+
+	tgt_fd = bpf_program__fd(tgt_skel->progs.chained_global_func_calls_success);
+
+	/* Attach to good non-eliminated subprog */
+	skel = freplace_dead_global_func__open();
+	if (!ASSERT_OK_PTR(skel, "skel_good_open"))
+		goto out;
+
+	bpf_program__set_attach_target(skel->progs.freplace_prog, tgt_fd, "global_good");
+	ASSERT_OK(err, "attach_target_good");
+	
+	err = freplace_dead_global_func__load(skel);
+	if (!ASSERT_OK(err, "skel_good_load"))
+		goto out;
+
+	freplace_dead_global_func__destroy(skel);
+
+	/* Try attaching to dead code-eliminated subprog */
+	skel = freplace_dead_global_func__open();
+	if (!ASSERT_OK_PTR(skel, "skel_dead_open"))
+		goto out;
+
+	bpf_program__set_log_buf(skel->progs.freplace_prog, log_buf, sizeof(log_buf));
+	err = bpf_program__set_attach_target(skel->progs.freplace_prog, tgt_fd, "global_dead");
+	ASSERT_OK(err, "attach_target_dead");
+	
+	err = freplace_dead_global_func__load(skel);
+	if (!ASSERT_ERR(err, "skel_dead_load"))
+		goto out;
+
+	ASSERT_HAS_SUBSTR(log_buf, "Subprog global_dead doesn't exist", "dead_subprog_missing_msg");
+
+out:
+	verifier_global_subprogs__destroy(tgt_skel);
+	freplace_dead_global_func__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/freplace_dead_global_func.c b/tools/testing/selftests/bpf/progs/freplace_dead_global_func.c
new file mode 100644
index 000000000000..808738eac578
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/freplace_dead_global_func.c
@@ -0,0 +1,11 @@ 
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("freplace")
+int freplace_prog(int x)
+{
+	return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
index a0a5efd1caa1..7f9b21a1c5a7 100644
--- a/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
+++ b/tools/testing/selftests/bpf/progs/verifier_global_subprogs.c
@@ -10,25 +10,31 @@ 
 
 int arr[1];
 int unkn_idx;
+const volatile bool call_dead_subprog = false;
 
-__noinline long global_bad(void)
+__noinline long global_bad(int x)
 {
-	return arr[unkn_idx]; /* BOOM */
+	return arr[unkn_idx] + x; /* BOOM */
 }
 
-__noinline long global_good(void)
+__noinline long global_good(int x)
 {
-	return arr[0];
+	return arr[0] + x;
 }
 
-__noinline long global_calls_bad(void)
+__noinline long global_calls_bad(int x)
 {
-	return global_good() + global_bad() /* does BOOM indirectly */;
+	return global_good(x) + global_bad(x) /* does BOOM indirectly */;
 }
 
-__noinline long global_calls_good_only(void)
+__noinline long global_calls_good_only(int x)
 {
-	return global_good();
+	return global_good(x);
+}
+
+__noinline long global_dead(int x)
+{
+	return x * 2;
 }
 
 SEC("?raw_tp")
@@ -41,19 +47,23 @@  __msg("Validating global_good() func")
 __msg("('global_good') is safe for any args that match its prototype")
 int chained_global_func_calls_success(void)
 {
-	return global_calls_good_only();
+	int sum = 0;
+
+	if (call_dead_subprog)
+		sum += global_dead(42);
+	return global_calls_good_only(42) + sum;
 }
 
 SEC("?raw_tp")
 __failure __log_level(2)
 /* main prog validated successfully first */
-__msg("1: (95) exit")
+__msg("2: (95) exit")
 /* eventually we validate global_bad() and fail */
 __msg("Validating global_bad() func")
 __msg("math between map_value pointer and register") /* BOOM */
 int chained_global_func_calls_bad(void)
 {
-	return global_calls_bad();
+	return global_calls_bad(13);
 }
 
 /* do out of bounds access forcing verifier to fail verification if this