Commit d69b5a90 authored by Andrii Nakryiko's avatar Andrii Nakryiko
Browse files

Merge branch 'selftests/bpf: support custom per-test flags and multiple expected messages'

Eduard Zingerman says:

====================

This patch allows to specify program flags and multiple verifier log
messages for the test_loader kind of tests. For example:

  tools/testing/selftets/bpf/progs/foobar.c:

    SEC("tc")
    __success __log_level(7)
    __msg("first message")
    __msg("next message")
    __flag(BPF_F_ANY_ALIGNMENT)
    int buz(struct __sk_buff *skb)
    { ... }

It was developed by Andrii Nakryiko ([1]), I reused it in a
"test_verifier tests migration to inline assembly" patch series ([2]),
but the series is currently stuck on my side.
Andrii asked to spin this particular patch separately ([3]).

[1] https://lore.kernel.org/bpf/CAEf4BzZH0ZxorCi7nPDbRqSK9f+410RooNwNJGwfw8=0a5i1nw@mail.gmail.com/
[2] https://lore.kernel.org/bpf/20230123145148.2791939-1-eddyz87@gmail.com/
[3] https://lore.kernel.org/bpf/20230123145148.2791939-1-eddyz87@gmail.com/T/#m52e806c5a679a2aa8f484d011be7ec105939127a


====================

Signed-off-by: default avatarAndrii Nakryiko <andrii@kernel.org>
parents 07e2193f 35cbf7f9
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -2,10 +2,33 @@
#ifndef __BPF_MISC_H__
#define __BPF_MISC_H__

/* This set of attributes controls behavior of the
 * test_loader.c:test_loader__run_subtests().
 *
 * __msg             Message expected to be found in the verifier log.
 *                   Multiple __msg attributes could be specified.
 *
 * __success         Expect program load success in privileged mode.
 *
 * __failure         Expect program load failure in privileged mode.
 *
 * __log_level       Log level to use for the program, numeric value expected.
 *
 * __flag            Adds one flag use for the program, the following values are valid:
 *                   - BPF_F_STRICT_ALIGNMENT;
 *                   - BPF_F_TEST_RND_HI32;
 *                   - BPF_F_TEST_STATE_FREQ;
 *                   - BPF_F_SLEEPABLE;
 *                   - BPF_F_XDP_HAS_FRAGS;
 *                   - A numeric value.
 *                   Multiple __flag attributes could be specified, the final flags
 *                   value is derived by applying binary "or" to all specified values.
 */
#define __msg(msg)		__attribute__((btf_decl_tag("comment:test_expect_msg=" msg)))
#define __failure		__attribute__((btf_decl_tag("comment:test_expect_failure")))
#define __success		__attribute__((btf_decl_tag("comment:test_expect_success")))
#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)))

/* Convenience macro for use with 'asm volatile' blocks */
#define __naked __attribute__((naked))
+60 −9
Original line number Diff line number Diff line
@@ -13,12 +13,15 @@
#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="

struct test_spec {
	const char *name;
	bool expect_failure;
	const char *expect_msg;
	const char **expect_msgs;
	size_t expect_msg_cnt;
	int log_level;
	int prog_flags;
};

static int tester_init(struct test_loader *tester)
@@ -67,7 +70,8 @@ static int parse_test_spec(struct test_loader *tester,

	for (i = 1; i < btf__type_cnt(btf); i++) {
		const struct btf_type *t;
		const char *s;
		const char *s, *val;
		char *e;

		t = btf__type_by_id(btf, i);
		if (!btf_is_decl_tag(t))
@@ -82,14 +86,48 @@ static int parse_test_spec(struct test_loader *tester,
		} else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) {
			spec->expect_failure = false;
		} else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) {
			spec->expect_msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1;
			void *tmp;
			const char **msg;

			tmp = realloc(spec->expect_msgs,
				      (1 + spec->expect_msg_cnt) * sizeof(void *));
			if (!tmp) {
				ASSERT_FAIL("failed to realloc memory for messages\n");
				return -ENOMEM;
			}
			spec->expect_msgs = tmp;
			msg = &spec->expect_msgs[spec->expect_msg_cnt++];
			*msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1;
		} 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(s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1, NULL, 0);
			if (errno) {
			spec->log_level = strtol(val, &e, 0);
			if (errno || e[0] != '\0') {
				ASSERT_FAIL("failed to parse test log level from '%s'", s);
				return -EINVAL;
			}
		} 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) {
				spec->prog_flags |= BPF_F_STRICT_ALIGNMENT;
			} else if (strcmp(val, "BPF_F_ANY_ALIGNMENT") == 0) {
				spec->prog_flags |= BPF_F_ANY_ALIGNMENT;
			} else if (strcmp(val, "BPF_F_TEST_RND_HI32") == 0) {
				spec->prog_flags |= BPF_F_TEST_RND_HI32;
			} else if (strcmp(val, "BPF_F_TEST_STATE_FREQ") == 0) {
				spec->prog_flags |= BPF_F_TEST_STATE_FREQ;
			} else if (strcmp(val, "BPF_F_SLEEPABLE") == 0) {
				spec->prog_flags |= BPF_F_SLEEPABLE;
			} 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') {
					ASSERT_FAIL("failed to parse test prog flags from '%s'", s);
					return -EINVAL;
				}
			}
		}
	}

@@ -101,7 +139,7 @@ static void prepare_case(struct test_loader *tester,
			 struct bpf_object *obj,
			 struct bpf_program *prog)
{
	int min_log_level = 0;
	int min_log_level = 0, prog_flags;

	if (env.verbosity > VERBOSE_NONE)
		min_log_level = 1;
@@ -119,7 +157,11 @@ static void prepare_case(struct test_loader *tester,
	else
		bpf_program__set_log_level(prog, spec->log_level);

	prog_flags = bpf_program__flags(prog);
	bpf_program__set_flags(prog, prog_flags | spec->prog_flags);

	tester->log_buf[0] = '\0';
	tester->next_match_pos = 0;
}

static void emit_verifier_log(const char *log_buf, bool force)
@@ -135,17 +177,26 @@ static void validate_case(struct test_loader *tester,
			  struct bpf_program *prog,
			  int load_err)
{
	if (spec->expect_msg) {
	int i, j;

	for (i = 0; i < spec->expect_msg_cnt; i++) {
		char *match;
		const char *expect_msg;

		expect_msg = spec->expect_msgs[i];

		match = strstr(tester->log_buf, spec->expect_msg);
		match = strstr(tester->log_buf + tester->next_match_pos, expect_msg);
		if (!ASSERT_OK_PTR(match, "expect_msg")) {
			/* if we are in verbose mode, we've already emitted log */
			if (env.verbosity == VERBOSE_NONE)
				emit_verifier_log(tester->log_buf, true /*force*/);
			fprintf(stderr, "EXPECTED MSG: '%s'\n", spec->expect_msg);
			for (j = 0; j < i; j++)
				fprintf(stderr, "MATCHED  MSG: '%s'\n", spec->expect_msgs[j]);
			fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg);
			return;
		}

		tester->next_match_pos = match - tester->log_buf + strlen(expect_msg);
	}
}

+1 −0
Original line number Diff line number Diff line
@@ -427,6 +427,7 @@ int get_bpf_max_tramp_links(void);
struct test_loader {
	char *log_buf;
	size_t log_buf_sz;
	size_t next_match_pos;

	struct bpf_object *obj;
};