All of lore.kernel.org
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: bpf@vger.kernel.org, ast@kernel.org
Cc: andrii@kernel.org, daniel@iogearbox.net, martin.lau@linux.dev,
	kernel-team@fb.com, yhs@fb.com,
	Eduard Zingerman <eddyz87@gmail.com>
Subject: [PATCH bpf-next 04/43] selftests/bpf: Tests execution support for test_loader.c
Date: Sat, 25 Mar 2023 04:54:45 +0200	[thread overview]
Message-ID: <20230325025524.144043-5-eddyz87@gmail.com> (raw)
In-Reply-To: <20230325025524.144043-1-eddyz87@gmail.com>

Extends test_loader.c:test_loader__run_subtests() by allowing to
execute BPF_PROG_TEST_RUN bpf command for selected programs.
This is similar to functionality provided by test_verifier.

Adds the following new attributes controlling test_loader behavior:

  __retval(...)
  __retval_unpriv(...)

* If any of these attributes is present, the annotated program would
  be executed using libbpf's bpf_prog_test_run_opts() function.
* If __retval is present, the test run would be done for program
  loaded in privileged mode.
* If __retval_unpriv is present, the test run would be done for
  program loaded in unprivileged mode.
* To mimic test_verifier behavior, the actual run is initiated in
  privileged mode.
* The value returned by a test run is compared against retval
  parameter.

The retval attribute takes one of the following parameters:
- a decimal number
- a hexadecimal number (must start from '0x')
- any of a three special literals (provided for compatibility with
  test_verifier):
  - INT_MIN
  - POINTER_VALUE
  - TEST_DATA_LEN

An example of the attribute usage:

  SEC("socket")
  __description("return 42")
  __success __success_unpriv __retval(42)
  __naked void the_42_test(void)
  {
          asm volatile ("                                 \
          r0 = 42;                                        \
          exit;                                           \
  "       ::: __clobber_all);
  }

Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
---
 tools/testing/selftests/bpf/progs/bpf_misc.h |  15 ++
 tools/testing/selftests/bpf/test_loader.c    | 149 +++++++++++++++++--
 2 files changed, 150 insertions(+), 14 deletions(-)

diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index 9defc217a5bd..6e3b4903c541 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -30,6 +30,15 @@
  * __failure         Expect program load failure in privileged mode.
  * __failure_unpriv  Expect program load failure in unprivileged mode.
  *
+ * __retval          Execute the program using BPF_PROG_TEST_RUN command,
+ *                   expect return value to match passed parameter:
+ *                   - a decimal number
+ *                   - a hexadecimal number, when starts from 0x
+ *                   - literal INT_MIN
+ *                   - literal POINTER_VALUE (see definition below)
+ *                   - literal TEST_DATA_LEN (see definition below)
+ * __retval_unpriv   Same, but load program in unprivileged mode.
+ *
  * __description     Text to be used instead of a program name for display
  *                   and filtering purposes.
  *
@@ -54,6 +63,8 @@
 #define __success_unpriv	__attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
 #define __log_level(lvl)	__attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
 #define __flag(flag)		__attribute__((btf_decl_tag("comment:test_prog_flags="#flag)))
+#define __retval(val)		__attribute__((btf_decl_tag("comment:test_retval="#val)))
+#define __retval_unpriv(val)	__attribute__((btf_decl_tag("comment:test_retval_unpriv="#val)))
 
 /* Convenience macro for use with 'asm volatile' blocks */
 #define __naked __attribute__((naked))
@@ -65,6 +76,10 @@
 #define __imm_ptr(name) [name]"p"(&name)
 #define __imm_insn(name, expr) [name]"i"(*(long *)&(expr))
 
+/* Magic constants used with __retval() */
+#define POINTER_VALUE	0xcafe4all
+#define TEST_DATA_LEN	64
+
 #if defined(__TARGET_ARCH_x86)
 #define SYSCALL_WRAPPER 1
 #define SYS_PREFIX "__x64_"
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index 41cddb303885..47e9e076bc8f 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -23,6 +23,12 @@
 #define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
 #define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
 #define TEST_TAG_DESCRIPTION_PFX "comment:test_description="
+#define TEST_TAG_RETVAL_PFX "comment:test_retval="
+#define TEST_TAG_RETVAL_PFX_UNPRIV "comment:test_retval_unpriv="
+
+/* Warning: duplicated in bpf_misc.h */
+#define POINTER_VALUE	0xcafe4all
+#define TEST_DATA_LEN	64
 
 #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
 #define EFFICIENT_UNALIGNED_ACCESS 1
@@ -42,6 +48,8 @@ struct test_subspec {
 	bool expect_failure;
 	const char **expect_msgs;
 	size_t expect_msg_cnt;
+	int retval;
+	bool execute;
 };
 
 struct test_spec {
@@ -96,6 +104,46 @@ static int push_msg(const char *msg, struct test_subspec *subspec)
 	return 0;
 }
 
+static int parse_int(const char *str, int *val, const char *name)
+{
+	char *end;
+	long tmp;
+
+	errno = 0;
+	if (str_has_pfx(str, "0x"))
+		tmp = strtol(str + 2, &end, 16);
+	else
+		tmp = strtol(str, &end, 10);
+	if (errno || end[0] != '\0') {
+		PRINT_FAIL("failed to parse %s from '%s'\n", name, str);
+		return -EINVAL;
+	}
+	*val = tmp;
+	return 0;
+}
+
+static int parse_retval(const char *str, int *val, const char *name)
+{
+	struct {
+		char *name;
+		int val;
+	} named_values[] = {
+		{ "INT_MIN"      , INT_MIN },
+		{ "POINTER_VALUE", POINTER_VALUE },
+		{ "TEST_DATA_LEN", TEST_DATA_LEN },
+	};
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(named_values); ++i) {
+		if (strcmp(str, named_values[i].name) != 0)
+			continue;
+		*val = named_values[i].val;
+		return 0;
+	}
+
+	return parse_int(str, val, name);
+}
+
 /* Uses btf_decl_tag attributes to describe the expected test
  * behavior, see bpf_misc.h for detailed description of each attribute
  * and attribute combinations.
@@ -107,6 +155,7 @@ static int parse_test_spec(struct test_loader *tester,
 {
 	const char *description = NULL;
 	bool has_unpriv_result = false;
+	bool has_unpriv_retval = false;
 	int func_id, i, err = 0;
 	struct btf *btf;
 
@@ -129,7 +178,7 @@ static int parse_test_spec(struct test_loader *tester,
 	for (i = 1; i < btf__type_cnt(btf); i++) {
 		const char *s, *val, *msg;
 		const struct btf_type *t;
-		char *e;
+		int tmp;
 
 		t = btf__type_by_id(btf, i);
 		if (!btf_is_decl_tag(t))
@@ -167,15 +216,26 @@ static int parse_test_spec(struct test_loader *tester,
 			if (err)
 				goto cleanup;
 			spec->mode_mask |= UNPRIV;
+		} else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX)) {
+			val = s + sizeof(TEST_TAG_RETVAL_PFX) - 1;
+			err = parse_retval(val, &spec->priv.retval, "__retval");
+			if (err)
+				goto cleanup;
+			spec->priv.execute = true;
+			spec->mode_mask |= PRIV;
+		} else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV)) {
+			val = s + sizeof(TEST_TAG_RETVAL_PFX_UNPRIV) - 1;
+			err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv");
+			if (err)
+				goto cleanup;
+			spec->mode_mask |= UNPRIV;
+			spec->unpriv.execute = true;
+			has_unpriv_retval = true;
 		} else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) {
 			val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1;
-			errno = 0;
-			spec->log_level = strtol(val, &e, 0);
-			if (errno || e[0] != '\0') {
-				PRINT_FAIL("failed to parse test log level from '%s'\n", s);
-				err = -EINVAL;
+			err = parse_int(val, &spec->log_level, "test log level");
+			if (err)
 				goto cleanup;
-			}
 		} else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) {
 			val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1;
 			if (strcmp(val, "BPF_F_STRICT_ALIGNMENT") == 0) {
@@ -191,14 +251,10 @@ static int parse_test_spec(struct test_loader *tester,
 			} else if (strcmp(val, "BPF_F_XDP_HAS_FRAGS") == 0) {
 				spec->prog_flags |= BPF_F_XDP_HAS_FRAGS;
 			} else /* assume numeric value */ {
-				errno = 0;
-				spec->prog_flags |= strtol(val, &e, 0);
-				if (errno || e[0] != '\0') {
-					PRINT_FAIL("failed to parse test prog flags from '%s'\n",
-						   val);
-					err = -EINVAL;
+				err = parse_int(val, &tmp, "test prog flags");
+				if (err)
 					goto cleanup;
-				}
+				spec->prog_flags |= tmp;
 			}
 		}
 	}
@@ -239,6 +295,11 @@ static int parse_test_spec(struct test_loader *tester,
 		if (!has_unpriv_result)
 			spec->unpriv.expect_failure = spec->priv.expect_failure;
 
+		if (!has_unpriv_retval) {
+			spec->unpriv.retval = spec->priv.retval;
+			spec->unpriv.execute = spec->priv.execute;
+		}
+
 		if (!spec->unpriv.expect_msgs) {
 			size_t sz = spec->priv.expect_msg_cnt * sizeof(void *);
 
@@ -402,6 +463,51 @@ static bool is_unpriv_capable_map(struct bpf_map *map)
 	}
 }
 
+static int do_prog_test_run(int fd_prog, int *retval)
+{
+	__u8 tmp_out[TEST_DATA_LEN << 2] = {};
+	__u8 tmp_in[TEST_DATA_LEN] = {};
+	int err, saved_errno;
+	LIBBPF_OPTS(bpf_test_run_opts, topts,
+		.data_in = tmp_in,
+		.data_size_in = sizeof(tmp_in),
+		.data_out = tmp_out,
+		.data_size_out = sizeof(tmp_out),
+		.repeat = 1,
+	);
+
+	err = bpf_prog_test_run_opts(fd_prog, &topts);
+	saved_errno = errno;
+
+	if (err) {
+		PRINT_FAIL("FAIL: Unexpected bpf_prog_test_run error: %d (%s) ",
+			   saved_errno, strerror(saved_errno));
+		return err;
+	}
+
+	ASSERT_OK(0, "bpf_prog_test_run");
+	*retval = topts.retval;
+
+	return 0;
+}
+
+static bool should_do_test_run(struct test_spec *spec, struct test_subspec *subspec)
+{
+	if (!subspec->execute)
+		return false;
+
+	if (subspec->expect_failure)
+		return false;
+
+	if ((spec->prog_flags & BPF_F_ANY_ALIGNMENT) && !EFFICIENT_UNALIGNED_ACCESS) {
+		if (env.verbosity != VERBOSE_NONE)
+			printf("alignment prevents execution\n");
+		return false;
+	}
+
+	return true;
+}
+
 /* this function is forced noinline and has short generic name to look better
  * in test_progs output (in case of a failure)
  */
@@ -418,6 +524,7 @@ void run_subtest(struct test_loader *tester,
 	struct bpf_program *tprog;
 	struct bpf_object *tobj;
 	struct bpf_map *map;
+	int retval;
 	int err;
 
 	if (!test__start_subtest(subspec->name))
@@ -476,6 +583,20 @@ void run_subtest(struct test_loader *tester,
 	emit_verifier_log(tester->log_buf, false /*force*/);
 	validate_case(tester, subspec, tobj, tprog, err);
 
+	if (should_do_test_run(spec, subspec)) {
+		/* For some reason test_verifier executes programs
+		 * with all capabilities restored. Do the same here.
+		 */
+		if (!restore_capabilities(&caps))
+			goto tobj_cleanup;
+
+		do_prog_test_run(bpf_program__fd(tprog), &retval);
+		if (retval != subspec->retval && subspec->retval != POINTER_VALUE) {
+			PRINT_FAIL("Unexpected retval: %d != %d\n", retval, subspec->retval);
+			goto tobj_cleanup;
+		}
+	}
+
 tobj_cleanup:
 	bpf_object__close(tobj);
 subtest_cleanup:
-- 
2.40.0


  parent reply	other threads:[~2023-03-25  2:56 UTC|newest]

Thread overview: 60+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-25  2:54 [PATCH bpf-next 00/43] First set of verifier/*.c migrated to inline assembly Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 01/43] selftests/bpf: Report program name on parse_test_spec error Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 02/43] selftests/bpf: __imm_insn & __imm_const macro for bpf_misc.h Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 03/43] selftests/bpf: Unprivileged tests for test_loader.c Eduard Zingerman
2023-03-25  2:54 ` Eduard Zingerman [this message]
2023-03-25  2:54 ` [PATCH bpf-next 05/43] selftests/bpf: prog_tests entry point for migrated test_verifier tests Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 06/43] selftests/bpf: verifier/and.c converted to inline assembly Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 07/43] selftests/bpf: verifier/array_access.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 08/43] selftests/bpf: verifier/basic_stack.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 09/43] selftests/bpf: verifier/bounds_deduction.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 10/43] selftests/bpf: verifier/bounds_mix_sign_unsign.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 11/43] selftests/bpf: verifier/cfg.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 12/43] selftests/bpf: verifier/cgroup_inv_retcode.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 13/43] selftests/bpf: verifier/cgroup_skb.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 14/43] selftests/bpf: verifier/cgroup_storage.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 15/43] selftests/bpf: verifier/const_or.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 16/43] selftests/bpf: verifier/ctx_sk_msg.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 17/43] selftests/bpf: verifier/direct_stack_access_wraparound.c " Eduard Zingerman
2023-03-25  2:54 ` [PATCH bpf-next 18/43] selftests/bpf: verifier/div0.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 19/43] selftests/bpf: verifier/div_overflow.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 20/43] selftests/bpf: verifier/helper_access_var_len.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 21/43] selftests/bpf: verifier/helper_packet_access.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 22/43] selftests/bpf: verifier/helper_restricted.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 23/43] selftests/bpf: verifier/helper_value_access.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 24/43] selftests/bpf: verifier/int_ptr.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 25/43] selftests/bpf: verifier/ld_ind.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 26/43] selftests/bpf: verifier/leak_ptr.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 27/43] selftests/bpf: verifier/map_ptr.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 28/43] selftests/bpf: verifier/map_ret_val.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 29/43] selftests/bpf: verifier/masking.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 30/43] selftests/bpf: verifier/meta_access.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 31/43] selftests/bpf: verifier/raw_stack.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 32/43] selftests/bpf: verifier/raw_tp_writable.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 33/43] selftests/bpf: verifier/ringbuf.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 34/43] selftests/bpf: verifier/spill_fill.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 35/43] selftests/bpf: verifier/stack_ptr.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 36/43] selftests/bpf: verifier/uninit.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 37/43] selftests/bpf: verifier/value_adj_spill.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 38/43] selftests/bpf: verifier/value.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 39/43] selftests/bpf: verifier/value_or_null.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 40/43] selftests/bpf: verifier/var_off.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 41/43] selftests/bpf: verifier/xadd.c " Eduard Zingerman
2023-03-25  2:55 ` [PATCH bpf-next 42/43] selftests/bpf: verifier/xdp.c " Eduard Zingerman
2023-03-25  3:23 ` [PATCH bpf-next 00/43] First set of verifier/*.c migrated " Stanislav Fomichev
2023-03-25 12:20   ` Eduard Zingerman
2023-03-25 16:16     ` Stanislav Fomichev
2023-03-26  1:19       ` Alexei Starovoitov
2023-03-27  3:15         ` Andrii Nakryiko
2023-03-27  3:57           ` Alexei Starovoitov
2023-03-27 11:26             ` Eduard Zingerman
2023-03-27 16:35             ` Andrii Nakryiko
2023-03-27 16:37               ` Andrii Nakryiko
2023-03-26  1:32 ` patchwork-bot+netdevbpf
2023-03-28  3:48 ` Daniel Borkmann
2023-03-28 21:52   ` Eduard Zingerman
2023-03-28 22:24     ` Andrii Nakryiko
2023-03-28 22:38       ` Eduard Zingerman
2023-03-28 23:31         ` Alexei Starovoitov
2023-03-29  0:11           ` Andrii Nakryiko
2023-03-29  0:07         ` Andrii Nakryiko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230325025524.144043-5-eddyz87@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=kernel-team@fb.com \
    --cc=martin.lau@linux.dev \
    --cc=yhs@fb.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.