162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */
362306a36Sopenharmony_ci#include <linux/capability.h>
462306a36Sopenharmony_ci#include <stdlib.h>
562306a36Sopenharmony_ci#include <test_progs.h>
662306a36Sopenharmony_ci#include <bpf/btf.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "autoconf_helper.h"
962306a36Sopenharmony_ci#include "unpriv_helpers.h"
1062306a36Sopenharmony_ci#include "cap_helpers.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define str_has_pfx(str, pfx) \
1362306a36Sopenharmony_ci	(strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define TEST_LOADER_LOG_BUF_SZ 1048576
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure"
1862306a36Sopenharmony_ci#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
1962306a36Sopenharmony_ci#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
2062306a36Sopenharmony_ci#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv"
2162306a36Sopenharmony_ci#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv"
2262306a36Sopenharmony_ci#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv="
2362306a36Sopenharmony_ci#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
2462306a36Sopenharmony_ci#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
2562306a36Sopenharmony_ci#define TEST_TAG_DESCRIPTION_PFX "comment:test_description="
2662306a36Sopenharmony_ci#define TEST_TAG_RETVAL_PFX "comment:test_retval="
2762306a36Sopenharmony_ci#define TEST_TAG_RETVAL_PFX_UNPRIV "comment:test_retval_unpriv="
2862306a36Sopenharmony_ci#define TEST_TAG_AUXILIARY "comment:test_auxiliary"
2962306a36Sopenharmony_ci#define TEST_TAG_AUXILIARY_UNPRIV "comment:test_auxiliary_unpriv"
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* Warning: duplicated in bpf_misc.h */
3262306a36Sopenharmony_ci#define POINTER_VALUE	0xcafe4all
3362306a36Sopenharmony_ci#define TEST_DATA_LEN	64
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
3662306a36Sopenharmony_ci#define EFFICIENT_UNALIGNED_ACCESS 1
3762306a36Sopenharmony_ci#else
3862306a36Sopenharmony_ci#define EFFICIENT_UNALIGNED_ACCESS 0
3962306a36Sopenharmony_ci#endif
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int sysctl_unpriv_disabled = -1;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cienum mode {
4462306a36Sopenharmony_ci	PRIV = 1,
4562306a36Sopenharmony_ci	UNPRIV = 2
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistruct test_subspec {
4962306a36Sopenharmony_ci	char *name;
5062306a36Sopenharmony_ci	bool expect_failure;
5162306a36Sopenharmony_ci	const char **expect_msgs;
5262306a36Sopenharmony_ci	size_t expect_msg_cnt;
5362306a36Sopenharmony_ci	int retval;
5462306a36Sopenharmony_ci	bool execute;
5562306a36Sopenharmony_ci};
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistruct test_spec {
5862306a36Sopenharmony_ci	const char *prog_name;
5962306a36Sopenharmony_ci	struct test_subspec priv;
6062306a36Sopenharmony_ci	struct test_subspec unpriv;
6162306a36Sopenharmony_ci	int log_level;
6262306a36Sopenharmony_ci	int prog_flags;
6362306a36Sopenharmony_ci	int mode_mask;
6462306a36Sopenharmony_ci	bool auxiliary;
6562306a36Sopenharmony_ci	bool valid;
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int tester_init(struct test_loader *tester)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	if (!tester->log_buf) {
7162306a36Sopenharmony_ci		tester->log_buf_sz = TEST_LOADER_LOG_BUF_SZ;
7262306a36Sopenharmony_ci		tester->log_buf = malloc(tester->log_buf_sz);
7362306a36Sopenharmony_ci		if (!ASSERT_OK_PTR(tester->log_buf, "tester_log_buf"))
7462306a36Sopenharmony_ci			return -ENOMEM;
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	return 0;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_civoid test_loader_fini(struct test_loader *tester)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	if (!tester)
8362306a36Sopenharmony_ci		return;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	free(tester->log_buf);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void free_test_spec(struct test_spec *spec)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	free(spec->priv.name);
9162306a36Sopenharmony_ci	free(spec->unpriv.name);
9262306a36Sopenharmony_ci	free(spec->priv.expect_msgs);
9362306a36Sopenharmony_ci	free(spec->unpriv.expect_msgs);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	spec->priv.name = NULL;
9662306a36Sopenharmony_ci	spec->unpriv.name = NULL;
9762306a36Sopenharmony_ci	spec->priv.expect_msgs = NULL;
9862306a36Sopenharmony_ci	spec->unpriv.expect_msgs = NULL;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic int push_msg(const char *msg, struct test_subspec *subspec)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	void *tmp;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	tmp = realloc(subspec->expect_msgs, (1 + subspec->expect_msg_cnt) * sizeof(void *));
10662306a36Sopenharmony_ci	if (!tmp) {
10762306a36Sopenharmony_ci		ASSERT_FAIL("failed to realloc memory for messages\n");
10862306a36Sopenharmony_ci		return -ENOMEM;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	subspec->expect_msgs = tmp;
11162306a36Sopenharmony_ci	subspec->expect_msgs[subspec->expect_msg_cnt++] = msg;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int parse_int(const char *str, int *val, const char *name)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	char *end;
11962306a36Sopenharmony_ci	long tmp;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	errno = 0;
12262306a36Sopenharmony_ci	if (str_has_pfx(str, "0x"))
12362306a36Sopenharmony_ci		tmp = strtol(str + 2, &end, 16);
12462306a36Sopenharmony_ci	else
12562306a36Sopenharmony_ci		tmp = strtol(str, &end, 10);
12662306a36Sopenharmony_ci	if (errno || end[0] != '\0') {
12762306a36Sopenharmony_ci		PRINT_FAIL("failed to parse %s from '%s'\n", name, str);
12862306a36Sopenharmony_ci		return -EINVAL;
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci	*val = tmp;
13162306a36Sopenharmony_ci	return 0;
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic int parse_retval(const char *str, int *val, const char *name)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	struct {
13762306a36Sopenharmony_ci		char *name;
13862306a36Sopenharmony_ci		int val;
13962306a36Sopenharmony_ci	} named_values[] = {
14062306a36Sopenharmony_ci		{ "INT_MIN"      , INT_MIN },
14162306a36Sopenharmony_ci		{ "POINTER_VALUE", POINTER_VALUE },
14262306a36Sopenharmony_ci		{ "TEST_DATA_LEN", TEST_DATA_LEN },
14362306a36Sopenharmony_ci	};
14462306a36Sopenharmony_ci	int i;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(named_values); ++i) {
14762306a36Sopenharmony_ci		if (strcmp(str, named_values[i].name) != 0)
14862306a36Sopenharmony_ci			continue;
14962306a36Sopenharmony_ci		*val = named_values[i].val;
15062306a36Sopenharmony_ci		return 0;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return parse_int(str, val, name);
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* Uses btf_decl_tag attributes to describe the expected test
15762306a36Sopenharmony_ci * behavior, see bpf_misc.h for detailed description of each attribute
15862306a36Sopenharmony_ci * and attribute combinations.
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_cistatic int parse_test_spec(struct test_loader *tester,
16162306a36Sopenharmony_ci			   struct bpf_object *obj,
16262306a36Sopenharmony_ci			   struct bpf_program *prog,
16362306a36Sopenharmony_ci			   struct test_spec *spec)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	const char *description = NULL;
16662306a36Sopenharmony_ci	bool has_unpriv_result = false;
16762306a36Sopenharmony_ci	bool has_unpriv_retval = false;
16862306a36Sopenharmony_ci	int func_id, i, err = 0;
16962306a36Sopenharmony_ci	struct btf *btf;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	memset(spec, 0, sizeof(*spec));
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	spec->prog_name = bpf_program__name(prog);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	btf = bpf_object__btf(obj);
17662306a36Sopenharmony_ci	if (!btf) {
17762306a36Sopenharmony_ci		ASSERT_FAIL("BPF object has no BTF");
17862306a36Sopenharmony_ci		return -EINVAL;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	func_id = btf__find_by_name_kind(btf, spec->prog_name, BTF_KIND_FUNC);
18262306a36Sopenharmony_ci	if (func_id < 0) {
18362306a36Sopenharmony_ci		ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->prog_name);
18462306a36Sopenharmony_ci		return -EINVAL;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	for (i = 1; i < btf__type_cnt(btf); i++) {
18862306a36Sopenharmony_ci		const char *s, *val, *msg;
18962306a36Sopenharmony_ci		const struct btf_type *t;
19062306a36Sopenharmony_ci		int tmp;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		t = btf__type_by_id(btf, i);
19362306a36Sopenharmony_ci		if (!btf_is_decl_tag(t))
19462306a36Sopenharmony_ci			continue;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci		if (t->type != func_id || btf_decl_tag(t)->component_idx != -1)
19762306a36Sopenharmony_ci			continue;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		s = btf__str_by_offset(btf, t->name_off);
20062306a36Sopenharmony_ci		if (str_has_pfx(s, TEST_TAG_DESCRIPTION_PFX)) {
20162306a36Sopenharmony_ci			description = s + sizeof(TEST_TAG_DESCRIPTION_PFX) - 1;
20262306a36Sopenharmony_ci		} else if (strcmp(s, TEST_TAG_EXPECT_FAILURE) == 0) {
20362306a36Sopenharmony_ci			spec->priv.expect_failure = true;
20462306a36Sopenharmony_ci			spec->mode_mask |= PRIV;
20562306a36Sopenharmony_ci		} else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS) == 0) {
20662306a36Sopenharmony_ci			spec->priv.expect_failure = false;
20762306a36Sopenharmony_ci			spec->mode_mask |= PRIV;
20862306a36Sopenharmony_ci		} else if (strcmp(s, TEST_TAG_EXPECT_FAILURE_UNPRIV) == 0) {
20962306a36Sopenharmony_ci			spec->unpriv.expect_failure = true;
21062306a36Sopenharmony_ci			spec->mode_mask |= UNPRIV;
21162306a36Sopenharmony_ci			has_unpriv_result = true;
21262306a36Sopenharmony_ci		} else if (strcmp(s, TEST_TAG_EXPECT_SUCCESS_UNPRIV) == 0) {
21362306a36Sopenharmony_ci			spec->unpriv.expect_failure = false;
21462306a36Sopenharmony_ci			spec->mode_mask |= UNPRIV;
21562306a36Sopenharmony_ci			has_unpriv_result = true;
21662306a36Sopenharmony_ci		} else if (strcmp(s, TEST_TAG_AUXILIARY) == 0) {
21762306a36Sopenharmony_ci			spec->auxiliary = true;
21862306a36Sopenharmony_ci			spec->mode_mask |= PRIV;
21962306a36Sopenharmony_ci		} else if (strcmp(s, TEST_TAG_AUXILIARY_UNPRIV) == 0) {
22062306a36Sopenharmony_ci			spec->auxiliary = true;
22162306a36Sopenharmony_ci			spec->mode_mask |= UNPRIV;
22262306a36Sopenharmony_ci		} else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX)) {
22362306a36Sopenharmony_ci			msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX) - 1;
22462306a36Sopenharmony_ci			err = push_msg(msg, &spec->priv);
22562306a36Sopenharmony_ci			if (err)
22662306a36Sopenharmony_ci				goto cleanup;
22762306a36Sopenharmony_ci			spec->mode_mask |= PRIV;
22862306a36Sopenharmony_ci		} else if (str_has_pfx(s, TEST_TAG_EXPECT_MSG_PFX_UNPRIV)) {
22962306a36Sopenharmony_ci			msg = s + sizeof(TEST_TAG_EXPECT_MSG_PFX_UNPRIV) - 1;
23062306a36Sopenharmony_ci			err = push_msg(msg, &spec->unpriv);
23162306a36Sopenharmony_ci			if (err)
23262306a36Sopenharmony_ci				goto cleanup;
23362306a36Sopenharmony_ci			spec->mode_mask |= UNPRIV;
23462306a36Sopenharmony_ci		} else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX)) {
23562306a36Sopenharmony_ci			val = s + sizeof(TEST_TAG_RETVAL_PFX) - 1;
23662306a36Sopenharmony_ci			err = parse_retval(val, &spec->priv.retval, "__retval");
23762306a36Sopenharmony_ci			if (err)
23862306a36Sopenharmony_ci				goto cleanup;
23962306a36Sopenharmony_ci			spec->priv.execute = true;
24062306a36Sopenharmony_ci			spec->mode_mask |= PRIV;
24162306a36Sopenharmony_ci		} else if (str_has_pfx(s, TEST_TAG_RETVAL_PFX_UNPRIV)) {
24262306a36Sopenharmony_ci			val = s + sizeof(TEST_TAG_RETVAL_PFX_UNPRIV) - 1;
24362306a36Sopenharmony_ci			err = parse_retval(val, &spec->unpriv.retval, "__retval_unpriv");
24462306a36Sopenharmony_ci			if (err)
24562306a36Sopenharmony_ci				goto cleanup;
24662306a36Sopenharmony_ci			spec->mode_mask |= UNPRIV;
24762306a36Sopenharmony_ci			spec->unpriv.execute = true;
24862306a36Sopenharmony_ci			has_unpriv_retval = true;
24962306a36Sopenharmony_ci		} else if (str_has_pfx(s, TEST_TAG_LOG_LEVEL_PFX)) {
25062306a36Sopenharmony_ci			val = s + sizeof(TEST_TAG_LOG_LEVEL_PFX) - 1;
25162306a36Sopenharmony_ci			err = parse_int(val, &spec->log_level, "test log level");
25262306a36Sopenharmony_ci			if (err)
25362306a36Sopenharmony_ci				goto cleanup;
25462306a36Sopenharmony_ci		} else if (str_has_pfx(s, TEST_TAG_PROG_FLAGS_PFX)) {
25562306a36Sopenharmony_ci			val = s + sizeof(TEST_TAG_PROG_FLAGS_PFX) - 1;
25662306a36Sopenharmony_ci			if (strcmp(val, "BPF_F_STRICT_ALIGNMENT") == 0) {
25762306a36Sopenharmony_ci				spec->prog_flags |= BPF_F_STRICT_ALIGNMENT;
25862306a36Sopenharmony_ci			} else if (strcmp(val, "BPF_F_ANY_ALIGNMENT") == 0) {
25962306a36Sopenharmony_ci				spec->prog_flags |= BPF_F_ANY_ALIGNMENT;
26062306a36Sopenharmony_ci			} else if (strcmp(val, "BPF_F_TEST_RND_HI32") == 0) {
26162306a36Sopenharmony_ci				spec->prog_flags |= BPF_F_TEST_RND_HI32;
26262306a36Sopenharmony_ci			} else if (strcmp(val, "BPF_F_TEST_STATE_FREQ") == 0) {
26362306a36Sopenharmony_ci				spec->prog_flags |= BPF_F_TEST_STATE_FREQ;
26462306a36Sopenharmony_ci			} else if (strcmp(val, "BPF_F_SLEEPABLE") == 0) {
26562306a36Sopenharmony_ci				spec->prog_flags |= BPF_F_SLEEPABLE;
26662306a36Sopenharmony_ci			} else if (strcmp(val, "BPF_F_XDP_HAS_FRAGS") == 0) {
26762306a36Sopenharmony_ci				spec->prog_flags |= BPF_F_XDP_HAS_FRAGS;
26862306a36Sopenharmony_ci			} else /* assume numeric value */ {
26962306a36Sopenharmony_ci				err = parse_int(val, &tmp, "test prog flags");
27062306a36Sopenharmony_ci				if (err)
27162306a36Sopenharmony_ci					goto cleanup;
27262306a36Sopenharmony_ci				spec->prog_flags |= tmp;
27362306a36Sopenharmony_ci			}
27462306a36Sopenharmony_ci		}
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	if (spec->mode_mask == 0)
27862306a36Sopenharmony_ci		spec->mode_mask = PRIV;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (!description)
28162306a36Sopenharmony_ci		description = spec->prog_name;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (spec->mode_mask & PRIV) {
28462306a36Sopenharmony_ci		spec->priv.name = strdup(description);
28562306a36Sopenharmony_ci		if (!spec->priv.name) {
28662306a36Sopenharmony_ci			PRINT_FAIL("failed to allocate memory for priv.name\n");
28762306a36Sopenharmony_ci			err = -ENOMEM;
28862306a36Sopenharmony_ci			goto cleanup;
28962306a36Sopenharmony_ci		}
29062306a36Sopenharmony_ci	}
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (spec->mode_mask & UNPRIV) {
29362306a36Sopenharmony_ci		int descr_len = strlen(description);
29462306a36Sopenharmony_ci		const char *suffix = " @unpriv";
29562306a36Sopenharmony_ci		char *name;
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci		name = malloc(descr_len + strlen(suffix) + 1);
29862306a36Sopenharmony_ci		if (!name) {
29962306a36Sopenharmony_ci			PRINT_FAIL("failed to allocate memory for unpriv.name\n");
30062306a36Sopenharmony_ci			err = -ENOMEM;
30162306a36Sopenharmony_ci			goto cleanup;
30262306a36Sopenharmony_ci		}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci		strcpy(name, description);
30562306a36Sopenharmony_ci		strcpy(&name[descr_len], suffix);
30662306a36Sopenharmony_ci		spec->unpriv.name = name;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (spec->mode_mask & (PRIV | UNPRIV)) {
31062306a36Sopenharmony_ci		if (!has_unpriv_result)
31162306a36Sopenharmony_ci			spec->unpriv.expect_failure = spec->priv.expect_failure;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci		if (!has_unpriv_retval) {
31462306a36Sopenharmony_ci			spec->unpriv.retval = spec->priv.retval;
31562306a36Sopenharmony_ci			spec->unpriv.execute = spec->priv.execute;
31662306a36Sopenharmony_ci		}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		if (!spec->unpriv.expect_msgs) {
31962306a36Sopenharmony_ci			size_t sz = spec->priv.expect_msg_cnt * sizeof(void *);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci			spec->unpriv.expect_msgs = malloc(sz);
32262306a36Sopenharmony_ci			if (!spec->unpriv.expect_msgs) {
32362306a36Sopenharmony_ci				PRINT_FAIL("failed to allocate memory for unpriv.expect_msgs\n");
32462306a36Sopenharmony_ci				err = -ENOMEM;
32562306a36Sopenharmony_ci				goto cleanup;
32662306a36Sopenharmony_ci			}
32762306a36Sopenharmony_ci			memcpy(spec->unpriv.expect_msgs, spec->priv.expect_msgs, sz);
32862306a36Sopenharmony_ci			spec->unpriv.expect_msg_cnt = spec->priv.expect_msg_cnt;
32962306a36Sopenharmony_ci		}
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	spec->valid = true;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return 0;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cicleanup:
33762306a36Sopenharmony_ci	free_test_spec(spec);
33862306a36Sopenharmony_ci	return err;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void prepare_case(struct test_loader *tester,
34262306a36Sopenharmony_ci			 struct test_spec *spec,
34362306a36Sopenharmony_ci			 struct bpf_object *obj,
34462306a36Sopenharmony_ci			 struct bpf_program *prog)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	int min_log_level = 0, prog_flags;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (env.verbosity > VERBOSE_NONE)
34962306a36Sopenharmony_ci		min_log_level = 1;
35062306a36Sopenharmony_ci	if (env.verbosity > VERBOSE_VERY)
35162306a36Sopenharmony_ci		min_log_level = 2;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	bpf_program__set_log_buf(prog, tester->log_buf, tester->log_buf_sz);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/* Make sure we set at least minimal log level, unless test requires
35662306a36Sopenharmony_ci	 * even higher level already. Make sure to preserve independent log
35762306a36Sopenharmony_ci	 * level 4 (verifier stats), though.
35862306a36Sopenharmony_ci	 */
35962306a36Sopenharmony_ci	if ((spec->log_level & 3) < min_log_level)
36062306a36Sopenharmony_ci		bpf_program__set_log_level(prog, (spec->log_level & 4) | min_log_level);
36162306a36Sopenharmony_ci	else
36262306a36Sopenharmony_ci		bpf_program__set_log_level(prog, spec->log_level);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	prog_flags = bpf_program__flags(prog);
36562306a36Sopenharmony_ci	bpf_program__set_flags(prog, prog_flags | spec->prog_flags);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	tester->log_buf[0] = '\0';
36862306a36Sopenharmony_ci	tester->next_match_pos = 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_cistatic void emit_verifier_log(const char *log_buf, bool force)
37262306a36Sopenharmony_ci{
37362306a36Sopenharmony_ci	if (!force && env.verbosity == VERBOSE_NONE)
37462306a36Sopenharmony_ci		return;
37562306a36Sopenharmony_ci	fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log_buf);
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic void validate_case(struct test_loader *tester,
37962306a36Sopenharmony_ci			  struct test_subspec *subspec,
38062306a36Sopenharmony_ci			  struct bpf_object *obj,
38162306a36Sopenharmony_ci			  struct bpf_program *prog,
38262306a36Sopenharmony_ci			  int load_err)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	int i, j;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	for (i = 0; i < subspec->expect_msg_cnt; i++) {
38762306a36Sopenharmony_ci		char *match;
38862306a36Sopenharmony_ci		const char *expect_msg;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		expect_msg = subspec->expect_msgs[i];
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci		match = strstr(tester->log_buf + tester->next_match_pos, expect_msg);
39362306a36Sopenharmony_ci		if (!ASSERT_OK_PTR(match, "expect_msg")) {
39462306a36Sopenharmony_ci			/* if we are in verbose mode, we've already emitted log */
39562306a36Sopenharmony_ci			if (env.verbosity == VERBOSE_NONE)
39662306a36Sopenharmony_ci				emit_verifier_log(tester->log_buf, true /*force*/);
39762306a36Sopenharmony_ci			for (j = 0; j < i; j++)
39862306a36Sopenharmony_ci				fprintf(stderr,
39962306a36Sopenharmony_ci					"MATCHED  MSG: '%s'\n", subspec->expect_msgs[j]);
40062306a36Sopenharmony_ci			fprintf(stderr, "EXPECTED MSG: '%s'\n", expect_msg);
40162306a36Sopenharmony_ci			return;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		tester->next_match_pos = match - tester->log_buf + strlen(expect_msg);
40562306a36Sopenharmony_ci	}
40662306a36Sopenharmony_ci}
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_cistruct cap_state {
40962306a36Sopenharmony_ci	__u64 old_caps;
41062306a36Sopenharmony_ci	bool initialized;
41162306a36Sopenharmony_ci};
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_cistatic int drop_capabilities(struct cap_state *caps)
41462306a36Sopenharmony_ci{
41562306a36Sopenharmony_ci	const __u64 caps_to_drop = (1ULL << CAP_SYS_ADMIN | 1ULL << CAP_NET_ADMIN |
41662306a36Sopenharmony_ci				    1ULL << CAP_PERFMON   | 1ULL << CAP_BPF);
41762306a36Sopenharmony_ci	int err;
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	err = cap_disable_effective(caps_to_drop, &caps->old_caps);
42062306a36Sopenharmony_ci	if (err) {
42162306a36Sopenharmony_ci		PRINT_FAIL("failed to drop capabilities: %i, %s\n", err, strerror(err));
42262306a36Sopenharmony_ci		return err;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	caps->initialized = true;
42662306a36Sopenharmony_ci	return 0;
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int restore_capabilities(struct cap_state *caps)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	int err;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (!caps->initialized)
43462306a36Sopenharmony_ci		return 0;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	err = cap_enable_effective(caps->old_caps, NULL);
43762306a36Sopenharmony_ci	if (err)
43862306a36Sopenharmony_ci		PRINT_FAIL("failed to restore capabilities: %i, %s\n", err, strerror(err));
43962306a36Sopenharmony_ci	caps->initialized = false;
44062306a36Sopenharmony_ci	return err;
44162306a36Sopenharmony_ci}
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_cistatic bool can_execute_unpriv(struct test_loader *tester, struct test_spec *spec)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	if (sysctl_unpriv_disabled < 0)
44662306a36Sopenharmony_ci		sysctl_unpriv_disabled = get_unpriv_disabled() ? 1 : 0;
44762306a36Sopenharmony_ci	if (sysctl_unpriv_disabled)
44862306a36Sopenharmony_ci		return false;
44962306a36Sopenharmony_ci	if ((spec->prog_flags & BPF_F_ANY_ALIGNMENT) && !EFFICIENT_UNALIGNED_ACCESS)
45062306a36Sopenharmony_ci		return false;
45162306a36Sopenharmony_ci	return true;
45262306a36Sopenharmony_ci}
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_cistatic bool is_unpriv_capable_map(struct bpf_map *map)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	enum bpf_map_type type;
45762306a36Sopenharmony_ci	__u32 flags;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	type = bpf_map__type(map);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	switch (type) {
46262306a36Sopenharmony_ci	case BPF_MAP_TYPE_HASH:
46362306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERCPU_HASH:
46462306a36Sopenharmony_ci	case BPF_MAP_TYPE_HASH_OF_MAPS:
46562306a36Sopenharmony_ci		flags = bpf_map__map_flags(map);
46662306a36Sopenharmony_ci		return !(flags & BPF_F_ZERO_SEED);
46762306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
46862306a36Sopenharmony_ci	case BPF_MAP_TYPE_ARRAY:
46962306a36Sopenharmony_ci	case BPF_MAP_TYPE_RINGBUF:
47062306a36Sopenharmony_ci	case BPF_MAP_TYPE_PROG_ARRAY:
47162306a36Sopenharmony_ci	case BPF_MAP_TYPE_CGROUP_ARRAY:
47262306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERCPU_ARRAY:
47362306a36Sopenharmony_ci	case BPF_MAP_TYPE_USER_RINGBUF:
47462306a36Sopenharmony_ci	case BPF_MAP_TYPE_ARRAY_OF_MAPS:
47562306a36Sopenharmony_ci	case BPF_MAP_TYPE_CGROUP_STORAGE:
47662306a36Sopenharmony_ci	case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
47762306a36Sopenharmony_ci		return true;
47862306a36Sopenharmony_ci	default:
47962306a36Sopenharmony_ci		return false;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistatic int do_prog_test_run(int fd_prog, int *retval)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	__u8 tmp_out[TEST_DATA_LEN << 2] = {};
48662306a36Sopenharmony_ci	__u8 tmp_in[TEST_DATA_LEN] = {};
48762306a36Sopenharmony_ci	int err, saved_errno;
48862306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_test_run_opts, topts,
48962306a36Sopenharmony_ci		.data_in = tmp_in,
49062306a36Sopenharmony_ci		.data_size_in = sizeof(tmp_in),
49162306a36Sopenharmony_ci		.data_out = tmp_out,
49262306a36Sopenharmony_ci		.data_size_out = sizeof(tmp_out),
49362306a36Sopenharmony_ci		.repeat = 1,
49462306a36Sopenharmony_ci	);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	err = bpf_prog_test_run_opts(fd_prog, &topts);
49762306a36Sopenharmony_ci	saved_errno = errno;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (err) {
50062306a36Sopenharmony_ci		PRINT_FAIL("FAIL: Unexpected bpf_prog_test_run error: %d (%s) ",
50162306a36Sopenharmony_ci			   saved_errno, strerror(saved_errno));
50262306a36Sopenharmony_ci		return err;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	ASSERT_OK(0, "bpf_prog_test_run");
50662306a36Sopenharmony_ci	*retval = topts.retval;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	return 0;
50962306a36Sopenharmony_ci}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_cistatic bool should_do_test_run(struct test_spec *spec, struct test_subspec *subspec)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	if (!subspec->execute)
51462306a36Sopenharmony_ci		return false;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	if (subspec->expect_failure)
51762306a36Sopenharmony_ci		return false;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	if ((spec->prog_flags & BPF_F_ANY_ALIGNMENT) && !EFFICIENT_UNALIGNED_ACCESS) {
52062306a36Sopenharmony_ci		if (env.verbosity != VERBOSE_NONE)
52162306a36Sopenharmony_ci			printf("alignment prevents execution\n");
52262306a36Sopenharmony_ci		return false;
52362306a36Sopenharmony_ci	}
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	return true;
52662306a36Sopenharmony_ci}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci/* this function is forced noinline and has short generic name to look better
52962306a36Sopenharmony_ci * in test_progs output (in case of a failure)
53062306a36Sopenharmony_ci */
53162306a36Sopenharmony_cistatic noinline
53262306a36Sopenharmony_civoid run_subtest(struct test_loader *tester,
53362306a36Sopenharmony_ci		 struct bpf_object_open_opts *open_opts,
53462306a36Sopenharmony_ci		 const void *obj_bytes,
53562306a36Sopenharmony_ci		 size_t obj_byte_cnt,
53662306a36Sopenharmony_ci		 struct test_spec *specs,
53762306a36Sopenharmony_ci		 struct test_spec *spec,
53862306a36Sopenharmony_ci		 bool unpriv)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct test_subspec *subspec = unpriv ? &spec->unpriv : &spec->priv;
54162306a36Sopenharmony_ci	struct bpf_program *tprog, *tprog_iter;
54262306a36Sopenharmony_ci	struct test_spec *spec_iter;
54362306a36Sopenharmony_ci	struct cap_state caps = {};
54462306a36Sopenharmony_ci	struct bpf_object *tobj;
54562306a36Sopenharmony_ci	struct bpf_map *map;
54662306a36Sopenharmony_ci	int retval, err, i;
54762306a36Sopenharmony_ci	bool should_load;
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	if (!test__start_subtest(subspec->name))
55062306a36Sopenharmony_ci		return;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	if (unpriv) {
55362306a36Sopenharmony_ci		if (!can_execute_unpriv(tester, spec)) {
55462306a36Sopenharmony_ci			test__skip();
55562306a36Sopenharmony_ci			test__end_subtest();
55662306a36Sopenharmony_ci			return;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci		if (drop_capabilities(&caps)) {
55962306a36Sopenharmony_ci			test__end_subtest();
56062306a36Sopenharmony_ci			return;
56162306a36Sopenharmony_ci		}
56262306a36Sopenharmony_ci	}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	tobj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, open_opts);
56562306a36Sopenharmony_ci	if (!ASSERT_OK_PTR(tobj, "obj_open_mem")) /* shouldn't happen */
56662306a36Sopenharmony_ci		goto subtest_cleanup;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	i = 0;
56962306a36Sopenharmony_ci	bpf_object__for_each_program(tprog_iter, tobj) {
57062306a36Sopenharmony_ci		spec_iter = &specs[i++];
57162306a36Sopenharmony_ci		should_load = false;
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_ci		if (spec_iter->valid) {
57462306a36Sopenharmony_ci			if (strcmp(bpf_program__name(tprog_iter), spec->prog_name) == 0) {
57562306a36Sopenharmony_ci				tprog = tprog_iter;
57662306a36Sopenharmony_ci				should_load = true;
57762306a36Sopenharmony_ci			}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci			if (spec_iter->auxiliary &&
58062306a36Sopenharmony_ci			    spec_iter->mode_mask & (unpriv ? UNPRIV : PRIV))
58162306a36Sopenharmony_ci				should_load = true;
58262306a36Sopenharmony_ci		}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		bpf_program__set_autoload(tprog_iter, should_load);
58562306a36Sopenharmony_ci	}
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	prepare_case(tester, spec, tobj, tprog);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* By default bpf_object__load() automatically creates all
59062306a36Sopenharmony_ci	 * maps declared in the skeleton. Some map types are only
59162306a36Sopenharmony_ci	 * allowed in priv mode. Disable autoload for such maps in
59262306a36Sopenharmony_ci	 * unpriv mode.
59362306a36Sopenharmony_ci	 */
59462306a36Sopenharmony_ci	bpf_object__for_each_map(map, tobj)
59562306a36Sopenharmony_ci		bpf_map__set_autocreate(map, !unpriv || is_unpriv_capable_map(map));
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	err = bpf_object__load(tobj);
59862306a36Sopenharmony_ci	if (subspec->expect_failure) {
59962306a36Sopenharmony_ci		if (!ASSERT_ERR(err, "unexpected_load_success")) {
60062306a36Sopenharmony_ci			emit_verifier_log(tester->log_buf, false /*force*/);
60162306a36Sopenharmony_ci			goto tobj_cleanup;
60262306a36Sopenharmony_ci		}
60362306a36Sopenharmony_ci	} else {
60462306a36Sopenharmony_ci		if (!ASSERT_OK(err, "unexpected_load_failure")) {
60562306a36Sopenharmony_ci			emit_verifier_log(tester->log_buf, true /*force*/);
60662306a36Sopenharmony_ci			goto tobj_cleanup;
60762306a36Sopenharmony_ci		}
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci	emit_verifier_log(tester->log_buf, false /*force*/);
61162306a36Sopenharmony_ci	validate_case(tester, subspec, tobj, tprog, err);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	if (should_do_test_run(spec, subspec)) {
61462306a36Sopenharmony_ci		/* For some reason test_verifier executes programs
61562306a36Sopenharmony_ci		 * with all capabilities restored. Do the same here.
61662306a36Sopenharmony_ci		 */
61762306a36Sopenharmony_ci		if (restore_capabilities(&caps))
61862306a36Sopenharmony_ci			goto tobj_cleanup;
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci		if (tester->pre_execution_cb) {
62162306a36Sopenharmony_ci			err = tester->pre_execution_cb(tobj);
62262306a36Sopenharmony_ci			if (err) {
62362306a36Sopenharmony_ci				PRINT_FAIL("pre_execution_cb failed: %d\n", err);
62462306a36Sopenharmony_ci				goto tobj_cleanup;
62562306a36Sopenharmony_ci			}
62662306a36Sopenharmony_ci		}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci		do_prog_test_run(bpf_program__fd(tprog), &retval);
62962306a36Sopenharmony_ci		if (retval != subspec->retval && subspec->retval != POINTER_VALUE) {
63062306a36Sopenharmony_ci			PRINT_FAIL("Unexpected retval: %d != %d\n", retval, subspec->retval);
63162306a36Sopenharmony_ci			goto tobj_cleanup;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_citobj_cleanup:
63662306a36Sopenharmony_ci	bpf_object__close(tobj);
63762306a36Sopenharmony_cisubtest_cleanup:
63862306a36Sopenharmony_ci	test__end_subtest();
63962306a36Sopenharmony_ci	restore_capabilities(&caps);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic void process_subtest(struct test_loader *tester,
64362306a36Sopenharmony_ci			    const char *skel_name,
64462306a36Sopenharmony_ci			    skel_elf_bytes_fn elf_bytes_factory)
64562306a36Sopenharmony_ci{
64662306a36Sopenharmony_ci	LIBBPF_OPTS(bpf_object_open_opts, open_opts, .object_name = skel_name);
64762306a36Sopenharmony_ci	struct test_spec *specs = NULL;
64862306a36Sopenharmony_ci	struct bpf_object *obj = NULL;
64962306a36Sopenharmony_ci	struct bpf_program *prog;
65062306a36Sopenharmony_ci	const void *obj_bytes;
65162306a36Sopenharmony_ci	int err, i, nr_progs;
65262306a36Sopenharmony_ci	size_t obj_byte_cnt;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (tester_init(tester) < 0)
65562306a36Sopenharmony_ci		return; /* failed to initialize tester */
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	obj_bytes = elf_bytes_factory(&obj_byte_cnt);
65862306a36Sopenharmony_ci	obj = bpf_object__open_mem(obj_bytes, obj_byte_cnt, &open_opts);
65962306a36Sopenharmony_ci	if (!ASSERT_OK_PTR(obj, "obj_open_mem"))
66062306a36Sopenharmony_ci		return;
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	nr_progs = 0;
66362306a36Sopenharmony_ci	bpf_object__for_each_program(prog, obj)
66462306a36Sopenharmony_ci		++nr_progs;
66562306a36Sopenharmony_ci
66662306a36Sopenharmony_ci	specs = calloc(nr_progs, sizeof(struct test_spec));
66762306a36Sopenharmony_ci	if (!ASSERT_OK_PTR(specs, "Can't alloc specs array"))
66862306a36Sopenharmony_ci		return;
66962306a36Sopenharmony_ci
67062306a36Sopenharmony_ci	i = 0;
67162306a36Sopenharmony_ci	bpf_object__for_each_program(prog, obj) {
67262306a36Sopenharmony_ci		/* ignore tests for which  we can't derive test specification */
67362306a36Sopenharmony_ci		err = parse_test_spec(tester, obj, prog, &specs[i++]);
67462306a36Sopenharmony_ci		if (err)
67562306a36Sopenharmony_ci			PRINT_FAIL("Can't parse test spec for program '%s'\n",
67662306a36Sopenharmony_ci				   bpf_program__name(prog));
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	i = 0;
68062306a36Sopenharmony_ci	bpf_object__for_each_program(prog, obj) {
68162306a36Sopenharmony_ci		struct test_spec *spec = &specs[i++];
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci		if (!spec->valid || spec->auxiliary)
68462306a36Sopenharmony_ci			continue;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci		if (spec->mode_mask & PRIV)
68762306a36Sopenharmony_ci			run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt,
68862306a36Sopenharmony_ci				    specs, spec, false);
68962306a36Sopenharmony_ci		if (spec->mode_mask & UNPRIV)
69062306a36Sopenharmony_ci			run_subtest(tester, &open_opts, obj_bytes, obj_byte_cnt,
69162306a36Sopenharmony_ci				    specs, spec, true);
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	}
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	for (i = 0; i < nr_progs; ++i)
69662306a36Sopenharmony_ci		free_test_spec(&specs[i]);
69762306a36Sopenharmony_ci	free(specs);
69862306a36Sopenharmony_ci	bpf_object__close(obj);
69962306a36Sopenharmony_ci}
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_civoid test_loader__run_subtests(struct test_loader *tester,
70262306a36Sopenharmony_ci			       const char *skel_name,
70362306a36Sopenharmony_ci			       skel_elf_bytes_fn elf_bytes_factory)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	/* see comment in run_subtest() for why we do this function nesting */
70662306a36Sopenharmony_ci	process_subtest(tester, skel_name, elf_bytes_factory);
70762306a36Sopenharmony_ci}
708