Commit f26ebdd3 authored by Jiri Olsa's avatar Jiri Olsa Committed by Alexei Starovoitov
Browse files

selftests/bpf: Allow to use kfunc from testmod.ko in test_verifier



Currently the test_verifier allows test to specify kfunc symbol
and search for it in the kernel BTF.

Adding the possibility to search for kfunc also in bpf_testmod
module when it's not found in kernel BTF.

To find bpf_testmod btf we need to get back SYS_ADMIN cap.

Acked-by: default avatarDavid Vernet <void@manifault.com>
Signed-off-by: default avatarJiri Olsa <jolsa@kernel.org>
Link: https://lore.kernel.org/r/20230515133756.1658301-9-jolsa@kernel.org


Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent b23b385f
Loading
Loading
Loading
Loading
+139 −22
Original line number Diff line number Diff line
@@ -874,8 +874,140 @@ static int create_map_kptr(void)
	return fd;
}

static void set_root(bool set)
{
	__u64 caps;

	if (set) {
		if (cap_enable_effective(1ULL << CAP_SYS_ADMIN, &caps))
			perror("cap_disable_effective(CAP_SYS_ADMIN)");
	} else {
		if (cap_disable_effective(1ULL << CAP_SYS_ADMIN, &caps))
			perror("cap_disable_effective(CAP_SYS_ADMIN)");
	}
}

static __u64 ptr_to_u64(const void *ptr)
{
	return (uintptr_t) ptr;
}

static struct btf *btf__load_testmod_btf(struct btf *vmlinux)
{
	struct bpf_btf_info info;
	__u32 len = sizeof(info);
	struct btf *btf = NULL;
	char name[64];
	__u32 id = 0;
	int err, fd;

	/* Iterate all loaded BTF objects and find bpf_testmod,
	 * we need SYS_ADMIN cap for that.
	 */
	set_root(true);

	while (true) {
		err = bpf_btf_get_next_id(id, &id);
		if (err) {
			if (errno == ENOENT)
				break;
			perror("bpf_btf_get_next_id failed");
			break;
		}

		fd = bpf_btf_get_fd_by_id(id);
		if (fd < 0) {
			if (errno == ENOENT)
				continue;
			perror("bpf_btf_get_fd_by_id failed");
			break;
		}

		memset(&info, 0, sizeof(info));
		info.name_len = sizeof(name);
		info.name = ptr_to_u64(name);
		len = sizeof(info);

		err = bpf_obj_get_info_by_fd(fd, &info, &len);
		if (err) {
			close(fd);
			perror("bpf_obj_get_info_by_fd failed");
			break;
		}

		if (strcmp("bpf_testmod", name)) {
			close(fd);
			continue;
		}

		btf = btf__load_from_kernel_by_id_split(id, vmlinux);
		if (!btf) {
			close(fd);
			break;
		}

		/* We need the fd to stay open so it can be used in fd_array.
		 * The final cleanup call to btf__free will free btf object
		 * and close the file descriptor.
		 */
		btf__set_fd(btf, fd);
		break;
	}

	set_root(false);
	return btf;
}

static struct btf *testmod_btf;
static struct btf *vmlinux_btf;

static void kfuncs_cleanup(void)
{
	btf__free(testmod_btf);
	btf__free(vmlinux_btf);
}

static void fixup_prog_kfuncs(struct bpf_insn *prog, int *fd_array,
			      struct kfunc_btf_id_pair *fixup_kfunc_btf_id)
{
	/* Patch in kfunc BTF IDs */
	while (fixup_kfunc_btf_id->kfunc) {
		int btf_id = 0;

		/* try to find kfunc in kernel BTF */
		vmlinux_btf = vmlinux_btf ?: btf__load_vmlinux_btf();
		if (vmlinux_btf) {
			btf_id = btf__find_by_name_kind(vmlinux_btf,
							fixup_kfunc_btf_id->kfunc,
							BTF_KIND_FUNC);
			btf_id = btf_id < 0 ? 0 : btf_id;
		}

		/* kfunc not found in kernel BTF, try bpf_testmod BTF */
		if (!btf_id) {
			testmod_btf = testmod_btf ?: btf__load_testmod_btf(vmlinux_btf);
			if (testmod_btf) {
				btf_id = btf__find_by_name_kind(testmod_btf,
								fixup_kfunc_btf_id->kfunc,
								BTF_KIND_FUNC);
				btf_id = btf_id < 0 ? 0 : btf_id;
				if (btf_id) {
					/* We put bpf_testmod module fd into fd_array
					 * and its index 1 into instruction 'off'.
					 */
					*fd_array = btf__fd(testmod_btf);
					prog[fixup_kfunc_btf_id->insn_idx].off = 1;
				}
			}
		}

		prog[fixup_kfunc_btf_id->insn_idx].imm = btf_id;
		fixup_kfunc_btf_id++;
	}
}

static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
			  struct bpf_insn *prog, int *map_fds)
			  struct bpf_insn *prog, int *map_fds, int *fd_array)
{
	int *fixup_map_hash_8b = test->fixup_map_hash_8b;
	int *fixup_map_hash_48b = test->fixup_map_hash_48b;
@@ -900,7 +1032,6 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
	int *fixup_map_ringbuf = test->fixup_map_ringbuf;
	int *fixup_map_timer = test->fixup_map_timer;
	int *fixup_map_kptr = test->fixup_map_kptr;
	struct kfunc_btf_id_pair *fixup_kfunc_btf_id = test->fixup_kfunc_btf_id;

	if (test->fill_helper) {
		test->fill_insns = calloc(MAX_TEST_INSNS, sizeof(struct bpf_insn));
@@ -1101,25 +1232,7 @@ static void do_test_fixup(struct bpf_test *test, enum bpf_prog_type prog_type,
		} while (*fixup_map_kptr);
	}

	/* Patch in kfunc BTF IDs */
	if (fixup_kfunc_btf_id->kfunc) {
		struct btf *btf;
		int btf_id;

		do {
			btf_id = 0;
			btf = btf__load_vmlinux_btf();
			if (btf) {
				btf_id = btf__find_by_name_kind(btf,
								fixup_kfunc_btf_id->kfunc,
								BTF_KIND_FUNC);
				btf_id = btf_id < 0 ? 0 : btf_id;
			}
			btf__free(btf);
			prog[fixup_kfunc_btf_id->insn_idx].imm = btf_id;
			fixup_kfunc_btf_id++;
		} while (fixup_kfunc_btf_id->kfunc);
	}
	fixup_prog_kfuncs(prog, fd_array, test->fixup_kfunc_btf_id);
}

struct libcap {
@@ -1446,6 +1559,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
	int run_errs, run_successes;
	int map_fds[MAX_NR_MAPS];
	const char *expected_err;
	int fd_array[2] = { -1, -1 };
	int saved_errno;
	int fixup_skips;
	__u32 pflags;
@@ -1459,7 +1573,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
	if (!prog_type)
		prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
	fixup_skips = skips;
	do_test_fixup(test, prog_type, prog, map_fds);
	do_test_fixup(test, prog_type, prog, map_fds, &fd_array[1]);
	if (test->fill_insns) {
		prog = test->fill_insns;
		prog_len = test->prog_len;
@@ -1493,6 +1607,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
	else
		opts.log_level = DEFAULT_LIBBPF_LOG_LEVEL;
	opts.prog_flags = pflags;
	if (fd_array[1] != -1)
		opts.fd_array = &fd_array[0];

	if ((prog_type == BPF_PROG_TYPE_TRACING ||
	     prog_type == BPF_PROG_TYPE_LSM) && test->kfunc) {
@@ -1719,6 +1835,7 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to)
	}

	unload_bpf_testmod(verbose);
	kfuncs_cleanup();

	printf("Summary: %d PASSED, %d SKIPPED, %d FAILED\n", passes,
	       skips, errors);