diff options
Diffstat (limited to 'tools/testing/selftests/bpf/prog_tests')
33 files changed, 2052 insertions, 203 deletions
| diff --git a/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c b/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c index 67557cda2208..42b49870e520 100644 --- a/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c +++ b/tools/testing/selftests/bpf/prog_tests/bloom_filter_map.c @@ -13,7 +13,7 @@  static void test_fail_cases(void)  {  	LIBBPF_OPTS(bpf_map_create_opts, opts); -	__u32 value; +	__u32 value = 0;  	int fd, err;  	/* Invalid key size */ diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c index 6befa870434b..4a0670c056ba 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_cookie.c @@ -489,10 +489,28 @@ cleanup:  	bpf_link__destroy(link);  } +static int verify_tracing_link_info(int fd, u64 cookie) +{ +	struct bpf_link_info info; +	int err; +	u32 len = sizeof(info); + +	err = bpf_link_get_info_by_fd(fd, &info, &len); +	if (!ASSERT_OK(err, "get_link_info")) +		return -1; + +	if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_TRACING, "link_type")) +		return -1; + +	ASSERT_EQ(info.tracing.cookie, cookie, "tracing_cookie"); + +	return 0; +} +  static void tracing_subtest(struct test_bpf_cookie *skel)  {  	__u64 cookie; -	int prog_fd; +	int prog_fd, err;  	int fentry_fd = -1, fexit_fd = -1, fmod_ret_fd = -1;  	LIBBPF_OPTS(bpf_test_run_opts, opts);  	LIBBPF_OPTS(bpf_link_create_opts, link_opts); @@ -507,6 +525,10 @@ static void tracing_subtest(struct test_bpf_cookie *skel)  	if (!ASSERT_GE(fentry_fd, 0, "fentry.link_create"))  		goto cleanup; +	err = verify_tracing_link_info(fentry_fd, cookie); +	if (!ASSERT_OK(err, "verify_tracing_link_info")) +		goto cleanup; +  	cookie = 0x20000000000000L;  	prog_fd = bpf_program__fd(skel->progs.fexit_test1);  	link_opts.tracing.cookie = cookie; @@ -635,10 +657,29 @@ cleanup:  	bpf_link__destroy(link);  } +static int verify_raw_tp_link_info(int fd, u64 cookie) +{ +	struct bpf_link_info info; +	int err; +	u32 len = sizeof(info); + +	memset(&info, 0, sizeof(info)); +	err = bpf_link_get_info_by_fd(fd, &info, &len); +	if (!ASSERT_OK(err, "get_link_info")) +		return -1; + +	if (!ASSERT_EQ(info.type, BPF_LINK_TYPE_RAW_TRACEPOINT, "link_type")) +		return -1; + +	ASSERT_EQ(info.raw_tracepoint.cookie, cookie, "raw_tp_cookie"); + +	return 0; +} +  static void raw_tp_subtest(struct test_bpf_cookie *skel)  {  	__u64 cookie; -	int prog_fd, link_fd = -1; +	int err, prog_fd, link_fd = -1;  	struct bpf_link *link = NULL;  	LIBBPF_OPTS(bpf_raw_tp_opts, raw_tp_opts);  	LIBBPF_OPTS(bpf_raw_tracepoint_opts, opts); @@ -656,6 +697,11 @@ static void raw_tp_subtest(struct test_bpf_cookie *skel)  		goto cleanup;  	usleep(1); /* trigger */ + +	err = verify_raw_tp_link_info(link_fd, cookie); +	if (!ASSERT_OK(err, "verify_raw_tp_link_info")) +		goto cleanup; +  	close(link_fd); /* detach */  	link_fd = -1; diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c index add4a18c33bd..5225d69bf79b 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_iter.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_iter.c @@ -323,7 +323,7 @@ static void test_task_pidfd(void)  static void test_task_sleepable(void)  {  	struct bpf_iter_tasks *skel; -	int pid, status, err, data_pipe[2], finish_pipe[2], c; +	int pid, status, err, data_pipe[2], finish_pipe[2], c = 0;  	char *test_data = NULL;  	char *test_data_long = NULL;  	char *data[2]; diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c index fe2c502e5089..ecc3d47919ad 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_mod_race.c @@ -78,7 +78,7 @@ static int test_setup_uffd(void *fault_addr)  	}  	uffd_register.range.start = (unsigned long)fault_addr; -	uffd_register.range.len = 4096; +	uffd_register.range.len = getpagesize();  	uffd_register.mode = UFFDIO_REGISTER_MODE_MISSING;  	if (ioctl(uffd, UFFDIO_REGISTER, &uffd_register)) {  		close(uffd); diff --git a/tools/testing/selftests/bpf/prog_tests/btf_dump.c b/tools/testing/selftests/bpf/prog_tests/btf_dump.c index c0a776feec23..82903585c870 100644 --- a/tools/testing/selftests/bpf/prog_tests/btf_dump.c +++ b/tools/testing/selftests/bpf/prog_tests/btf_dump.c @@ -879,6 +879,122 @@ static void test_btf_dump_var_data(struct btf *btf, struct btf_dump *d,  			  "static int bpf_cgrp_storage_busy = (int)2", 2);  } +struct btf_dump_string_ctx { +	struct btf *btf; +	struct btf_dump *d; +	char *str; +	struct btf_dump_type_data_opts *opts; +	int array_id; +}; + +static int btf_dump_one_string(struct btf_dump_string_ctx *ctx, +			       char *ptr, size_t ptr_sz, +			       const char *expected_val) +{ +	size_t type_sz; +	int ret; + +	ctx->str[0] = '\0'; +	type_sz = btf__resolve_size(ctx->btf, ctx->array_id); +	ret = btf_dump__dump_type_data(ctx->d, ctx->array_id, ptr, ptr_sz, ctx->opts); +	if (type_sz <= ptr_sz) { +		if (!ASSERT_EQ(ret, type_sz, "failed/unexpected type_sz")) +			return -EINVAL; +	} +	if (!ASSERT_STREQ(ctx->str, expected_val, "ensure expected/actual match")) +		return -EFAULT; +	return 0; +} + +static void btf_dump_strings(struct btf_dump_string_ctx *ctx) +{ +	struct btf_dump_type_data_opts *opts = ctx->opts; + +	opts->emit_strings = true; + +	opts->compact = true; +	opts->emit_zeroes = false; + +	opts->skip_names = false; +	btf_dump_one_string(ctx, "foo", 4, "(char[4])\"foo\""); + +	opts->skip_names = true; +	btf_dump_one_string(ctx, "foo", 4, "\"foo\""); + +	/* This should have no effect. */ +	opts->emit_zeroes = false; +	btf_dump_one_string(ctx, "foo", 4, "\"foo\""); + +	/* This should have no effect. */ +	opts->compact = false; +	btf_dump_one_string(ctx, "foo", 4, "\"foo\""); + +	/* Non-printable characters come out as hex. */ +	btf_dump_one_string(ctx, "fo\xff", 4, "\"fo\\xff\""); +	btf_dump_one_string(ctx, "fo\x7", 4, "\"fo\\x07\""); + +	/* +	 * Strings that are too long for the specified type ("char[4]") +	 * should fall back to the current behavior. +	 */ +	opts->compact = true; +	btf_dump_one_string(ctx, "abcde", 6, "['a','b','c','d',]"); + +	/* +	 * Strings that are too short for the specified type ("char[4]") +	 * should work normally. +	 */ +	btf_dump_one_string(ctx, "ab", 3, "\"ab\""); + +	/* Non-NUL-terminated arrays don't get printed as strings. */ +	char food[4] = { 'f', 'o', 'o', 'd' }; +	char bye[3] = { 'b', 'y', 'e' }; + +	btf_dump_one_string(ctx, food, 4, "['f','o','o','d',]"); +	btf_dump_one_string(ctx, bye, 3, "['b','y','e',]"); + +	/* The embedded NUL should terminate the string. */ +	char embed[4] = { 'f', 'o', '\0', 'd' }; + +	btf_dump_one_string(ctx, embed, 4, "\"fo\""); +} + +static void test_btf_dump_string_data(void) +{ +	struct test_ctx t = {}; +	char str[STRSIZE]; +	struct btf_dump *d; +	DECLARE_LIBBPF_OPTS(btf_dump_type_data_opts, opts); +	struct btf_dump_string_ctx ctx; +	int char_id, int_id, array_id; + +	if (test_ctx__init(&t)) +		return; + +	d = btf_dump__new(t.btf, btf_dump_snprintf, str, NULL); +	if (!ASSERT_OK_PTR(d, "could not create BTF dump")) +		return; + +	/* Generate BTF for a four-element char array. */ +	char_id = btf__add_int(t.btf, "char", 1, BTF_INT_CHAR); +	ASSERT_EQ(char_id, 1, "char_id"); +	int_id = btf__add_int(t.btf, "int", 4, BTF_INT_SIGNED); +	ASSERT_EQ(int_id, 2, "int_id"); +	array_id = btf__add_array(t.btf, int_id, char_id, 4); +	ASSERT_EQ(array_id, 3, "array_id"); + +	ctx.btf = t.btf; +	ctx.d = d; +	ctx.str = str; +	ctx.opts = &opts; +	ctx.array_id = array_id; + +	btf_dump_strings(&ctx); + +	btf_dump__free(d); +	test_ctx__free(&t); +} +  static void test_btf_datasec(struct btf *btf, struct btf_dump *d, char *str,  			     const char *name, const char *expected_val,  			     void *data, size_t data_sz) @@ -970,6 +1086,8 @@ void test_btf_dump() {  		test_btf_dump_struct_data(btf, d, str);  	if (test__start_subtest("btf_dump: var_data"))  		test_btf_dump_var_data(btf, d, str); +	if (test__start_subtest("btf_dump: string_data")) +		test_btf_dump_string_data();  	btf_dump__free(d);  	btf__free(btf); diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c new file mode 100644 index 000000000000..bb60704a3ef9 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_opts.c @@ -0,0 +1,617 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include <test_progs.h> +#include "cgroup_helpers.h" +#include "cgroup_mprog.skel.h" + +static void assert_mprog_count(int cg, int atype, int expected) +{ +	__u32 count = 0, attach_flags = 0; +	int err; + +	err = bpf_prog_query(cg, atype, 0, &attach_flags, +			     NULL, &count); +	ASSERT_EQ(count, expected, "count"); +	ASSERT_EQ(err, 0, "prog_query"); +} + +static void test_prog_attach_detach(int atype) +{ +	LIBBPF_OPTS(bpf_prog_attach_opts, opta); +	LIBBPF_OPTS(bpf_prog_detach_opts, optd); +	LIBBPF_OPTS(bpf_prog_query_opts, optq); +	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4; +	struct cgroup_mprog *skel; +	__u32 prog_ids[10]; +	int cg, err; + +	cg = test__join_cgroup("/prog_attach_detach"); +	if (!ASSERT_GE(cg, 0, "join_cgroup /prog_attach_detach")) +		return; + +	skel = cgroup_mprog__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_load")) +		goto cleanup; + +	fd1 = bpf_program__fd(skel->progs.getsockopt_1); +	fd2 = bpf_program__fd(skel->progs.getsockopt_2); +	fd3 = bpf_program__fd(skel->progs.getsockopt_3); +	fd4 = bpf_program__fd(skel->progs.getsockopt_4); + +	id1 = id_from_prog_fd(fd1); +	id2 = id_from_prog_fd(fd2); +	id3 = id_from_prog_fd(fd3); +	id4 = id_from_prog_fd(fd4); + +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, +		.expected_revision = 1, +	); + +	/* ordering: [fd1] */ +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup; + +	assert_mprog_count(cg, atype, 1); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE, +		.expected_revision = 2, +	); + +	/* ordering: [fd2, fd1] */ +	err = bpf_prog_attach_opts(fd2, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup1; + +	assert_mprog_count(cg, atype, 2); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, +		.relative_fd = fd2, +		.expected_revision = 3, +	); + +	/* ordering: [fd2, fd3, fd1] */ +	err = bpf_prog_attach_opts(fd3, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup2; + +	assert_mprog_count(cg, atype, 3); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI, +		.expected_revision = 4, +	); + +	/* ordering: [fd2, fd3, fd1, fd4] */ +	err = bpf_prog_attach_opts(fd4, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup3; + +	assert_mprog_count(cg, atype, 4); + +	/* retrieve optq.prog_cnt */ +	err = bpf_prog_query_opts(cg, atype, &optq); +	if (!ASSERT_OK(err, "prog_query")) +		goto cleanup4; + +	/* optq.prog_cnt will be used in below query */ +	memset(prog_ids, 0, sizeof(prog_ids)); +	optq.prog_ids = prog_ids; +	err = bpf_prog_query_opts(cg, atype, &optq); +	if (!ASSERT_OK(err, "prog_query")) +		goto cleanup4; + +	ASSERT_EQ(optq.count, 4, "count"); +	ASSERT_EQ(optq.revision, 5, "revision"); +	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); +	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]"); +	ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]"); +	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); +	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); +	ASSERT_EQ(optq.link_ids, NULL, "link_ids"); + +cleanup4: +	optd.expected_revision = 5; +	err = bpf_prog_detach_opts(fd4, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 3); + +cleanup3: +	LIBBPF_OPTS_RESET(optd); +	err = bpf_prog_detach_opts(fd3, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 2); + +	/* Check revision after two detach operations */ +	err = bpf_prog_query_opts(cg, atype, &optq); +	ASSERT_OK(err, "prog_query"); +	ASSERT_EQ(optq.revision, 7, "revision"); + +cleanup2: +	err = bpf_prog_detach_opts(fd2, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 1); + +cleanup1: +	err = bpf_prog_detach_opts(fd1, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 0); + +cleanup: +	cgroup_mprog__destroy(skel); +	close(cg); +} + +static void test_link_attach_detach(int atype) +{ +	LIBBPF_OPTS(bpf_cgroup_opts, opta); +	LIBBPF_OPTS(bpf_cgroup_opts, optd); +	LIBBPF_OPTS(bpf_prog_query_opts, optq); +	struct bpf_link *link1, *link2, *link3, *link4; +	__u32 fd1, fd2, fd3, fd4, id1, id2, id3, id4; +	struct cgroup_mprog *skel; +	__u32 prog_ids[10]; +	int cg, err; + +	cg = test__join_cgroup("/link_attach_detach"); +	if (!ASSERT_GE(cg, 0, "join_cgroup /link_attach_detach")) +		return; + +	skel = cgroup_mprog__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_load")) +		goto cleanup; + +	fd1 = bpf_program__fd(skel->progs.getsockopt_1); +	fd2 = bpf_program__fd(skel->progs.getsockopt_2); +	fd3 = bpf_program__fd(skel->progs.getsockopt_3); +	fd4 = bpf_program__fd(skel->progs.getsockopt_4); + +	id1 = id_from_prog_fd(fd1); +	id2 = id_from_prog_fd(fd2); +	id3 = id_from_prog_fd(fd3); +	id4 = id_from_prog_fd(fd4); + +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.expected_revision = 1, +	); + +	/* ordering: [fd1] */ +	link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta); +	if (!ASSERT_OK_PTR(link1, "link_attach")) +		goto cleanup; + +	assert_mprog_count(cg, atype, 1); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_BEFORE | BPF_F_LINK, +		.relative_id = id_from_link_fd(bpf_link__fd(link1)), +		.expected_revision = 2, +	); + +	/* ordering: [fd2, fd1] */ +	link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta); +	if (!ASSERT_OK_PTR(link2, "link_attach")) +		goto cleanup1; + +	assert_mprog_count(cg, atype, 2); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_AFTER | BPF_F_LINK, +		.relative_fd = bpf_link__fd(link2), +		.expected_revision = 3, +	); + +	/* ordering: [fd2, fd3, fd1] */ +	link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); +	if (!ASSERT_OK_PTR(link3, "link_attach")) +		goto cleanup2; + +	assert_mprog_count(cg, atype, 3); + +	LIBBPF_OPTS_RESET(opta, +		.expected_revision = 4, +	); + +	/* ordering: [fd2, fd3, fd1, fd4] */ +	link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta); +	if (!ASSERT_OK_PTR(link4, "link_attach")) +		goto cleanup3; + +	assert_mprog_count(cg, atype, 4); + +	/* retrieve optq.prog_cnt */ +	err = bpf_prog_query_opts(cg, atype, &optq); +	if (!ASSERT_OK(err, "prog_query")) +		goto cleanup4; + +	/* optq.prog_cnt will be used in below query */ +	memset(prog_ids, 0, sizeof(prog_ids)); +	optq.prog_ids = prog_ids; +	err = bpf_prog_query_opts(cg, atype, &optq); +	if (!ASSERT_OK(err, "prog_query")) +		goto cleanup4; + +	ASSERT_EQ(optq.count, 4, "count"); +	ASSERT_EQ(optq.revision, 5, "revision"); +	ASSERT_EQ(optq.prog_ids[0], id2, "prog_ids[0]"); +	ASSERT_EQ(optq.prog_ids[1], id3, "prog_ids[1]"); +	ASSERT_EQ(optq.prog_ids[2], id1, "prog_ids[2]"); +	ASSERT_EQ(optq.prog_ids[3], id4, "prog_ids[3]"); +	ASSERT_EQ(optq.prog_ids[4], 0, "prog_ids[4]"); +	ASSERT_EQ(optq.link_ids, NULL, "link_ids"); + +cleanup4: +	bpf_link__destroy(link4); +	assert_mprog_count(cg, atype, 3); + +cleanup3: +	bpf_link__destroy(link3); +	assert_mprog_count(cg, atype, 2); + +	/* Check revision after two detach operations */ +	err = bpf_prog_query_opts(cg, atype, &optq); +	ASSERT_OK(err, "prog_query"); +	ASSERT_EQ(optq.revision, 7, "revision"); + +cleanup2: +	bpf_link__destroy(link2); +	assert_mprog_count(cg, atype, 1); + +cleanup1: +	bpf_link__destroy(link1); +	assert_mprog_count(cg, atype, 0); + +cleanup: +	cgroup_mprog__destroy(skel); +	close(cg); +} + +static void test_preorder_prog_attach_detach(int atype) +{ +	LIBBPF_OPTS(bpf_prog_attach_opts, opta); +	LIBBPF_OPTS(bpf_prog_detach_opts, optd); +	__u32 fd1, fd2, fd3, fd4; +	struct cgroup_mprog *skel; +	int cg, err; + +	cg = test__join_cgroup("/preorder_prog_attach_detach"); +	if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_prog_attach_detach")) +		return; + +	skel = cgroup_mprog__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_load")) +		goto cleanup; + +	fd1 = bpf_program__fd(skel->progs.getsockopt_1); +	fd2 = bpf_program__fd(skel->progs.getsockopt_2); +	fd3 = bpf_program__fd(skel->progs.getsockopt_3); +	fd4 = bpf_program__fd(skel->progs.getsockopt_4); + +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI, +		.expected_revision = 1, +	); + +	/* ordering: [fd1] */ +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup; + +	assert_mprog_count(cg, atype, 1); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_PREORDER, +		.expected_revision = 2, +	); + +	/* ordering: [fd1, fd2] */ +	err = bpf_prog_attach_opts(fd2, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup1; + +	assert_mprog_count(cg, atype, 2); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, +		.relative_fd = fd2, +		.expected_revision = 3, +	); + +	err = bpf_prog_attach_opts(fd3, cg, atype, &opta); +	if (!ASSERT_EQ(err, -EINVAL, "prog_attach")) +		goto cleanup2; + +	assert_mprog_count(cg, atype, 2); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_PREORDER, +		.relative_fd = fd2, +		.expected_revision = 3, +	); + +	/* ordering: [fd1, fd2, fd3] */ +	err = bpf_prog_attach_opts(fd3, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup2; + +	assert_mprog_count(cg, atype, 3); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI, +		.expected_revision = 4, +	); + +	/* ordering: [fd2, fd3, fd1, fd4] */ +	err = bpf_prog_attach_opts(fd4, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup3; + +	assert_mprog_count(cg, atype, 4); + +	err = bpf_prog_detach_opts(fd4, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 3); + +cleanup3: +	err = bpf_prog_detach_opts(fd3, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 2); + +cleanup2: +	err = bpf_prog_detach_opts(fd2, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 1); + +cleanup1: +	err = bpf_prog_detach_opts(fd1, cg, atype, &optd); +	ASSERT_OK(err, "prog_detach"); +	assert_mprog_count(cg, atype, 0); + +cleanup: +	cgroup_mprog__destroy(skel); +	close(cg); +} + +static void test_preorder_link_attach_detach(int atype) +{ +	LIBBPF_OPTS(bpf_cgroup_opts, opta); +	struct bpf_link *link1, *link2, *link3, *link4; +	struct cgroup_mprog *skel; +	__u32 fd2; +	int cg; + +	cg = test__join_cgroup("/preorder_link_attach_detach"); +	if (!ASSERT_GE(cg, 0, "join_cgroup /preorder_link_attach_detach")) +		return; + +	skel = cgroup_mprog__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_load")) +		goto cleanup; + +	fd2 = bpf_program__fd(skel->progs.getsockopt_2); + +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.expected_revision = 1, +	); + +	/* ordering: [fd1] */ +	link1 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_1, cg, &opta); +	if (!ASSERT_OK_PTR(link1, "link_attach")) +		goto cleanup; + +	assert_mprog_count(cg, atype, 1); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_PREORDER, +		.expected_revision = 2, +	); + +	/* ordering: [fd1, fd2] */ +	link2 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_2, cg, &opta); +	if (!ASSERT_OK_PTR(link2, "link_attach")) +		goto cleanup1; + +	assert_mprog_count(cg, atype, 2); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_AFTER, +		.relative_fd = fd2, +		.expected_revision = 3, +	); + +	link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); +	if (!ASSERT_ERR_PTR(link3, "link_attach")) +		goto cleanup2; + +	assert_mprog_count(cg, atype, 2); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_AFTER | BPF_F_PREORDER | BPF_F_LINK, +		.relative_fd = bpf_link__fd(link2), +		.expected_revision = 3, +	); + +	/* ordering: [fd1, fd2, fd3] */ +	link3 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_3, cg, &opta); +	if (!ASSERT_OK_PTR(link3, "link_attach")) +		goto cleanup2; + +	assert_mprog_count(cg, atype, 3); + +	LIBBPF_OPTS_RESET(opta, +		.expected_revision = 4, +	); + +	/* ordering: [fd2, fd3, fd1, fd4] */ +	link4 = bpf_program__attach_cgroup_opts(skel->progs.getsockopt_4, cg, &opta); +	if (!ASSERT_OK_PTR(link4, "prog_attach")) +		goto cleanup3; + +	assert_mprog_count(cg, atype, 4); + +	bpf_link__destroy(link4); +	assert_mprog_count(cg, atype, 3); + +cleanup3: +	bpf_link__destroy(link3); +	assert_mprog_count(cg, atype, 2); + +cleanup2: +	bpf_link__destroy(link2); +	assert_mprog_count(cg, atype, 1); + +cleanup1: +	bpf_link__destroy(link1); +	assert_mprog_count(cg, atype, 0); + +cleanup: +	cgroup_mprog__destroy(skel); +	close(cg); +} + +static void test_invalid_attach_detach(int atype) +{ +	LIBBPF_OPTS(bpf_prog_attach_opts, opta); +	__u32 fd1, fd2, id2; +	struct cgroup_mprog *skel; +	int cg, err; + +	cg = test__join_cgroup("/invalid_attach_detach"); +	if (!ASSERT_GE(cg, 0, "join_cgroup /invalid_attach_detach")) +		return; + +	skel = cgroup_mprog__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_load")) +		goto cleanup; + +	fd1 = bpf_program__fd(skel->progs.getsockopt_1); +	fd2 = bpf_program__fd(skel->progs.getsockopt_2); + +	id2 = id_from_prog_fd(fd2); + +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, +		.relative_id = id2, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -EINVAL, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_ID, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -ENOENT, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER | BPF_F_ID, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -ENOENT, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE | BPF_F_AFTER, +		.relative_id = id2, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -EINVAL, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_LINK, +		.relative_id = id2, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -EINVAL, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI, +		.relative_id = id2, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -EINVAL, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE, +		.relative_fd = fd1, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -ENOENT, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, +		.relative_fd = fd1, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -ENOENT, "prog_attach"); +	assert_mprog_count(cg, atype, 0); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	if (!ASSERT_EQ(err, 0, "prog_attach")) +		goto cleanup; +	assert_mprog_count(cg, atype, 1); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_AFTER, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -EINVAL, "prog_attach"); +	assert_mprog_count(cg, atype, 1); + +	LIBBPF_OPTS_RESET(opta, +		.flags = BPF_F_ALLOW_MULTI | BPF_F_REPLACE | BPF_F_AFTER, +		.replace_prog_fd = fd1, +	); + +	err = bpf_prog_attach_opts(fd1, cg, atype, &opta); +	ASSERT_EQ(err, -EINVAL, "prog_attach"); +	assert_mprog_count(cg, atype, 1); +cleanup: +	cgroup_mprog__destroy(skel); +	close(cg); +} + +void test_cgroup_mprog_opts(void) +{ +	if (test__start_subtest("prog_attach_detach")) +		test_prog_attach_detach(BPF_CGROUP_GETSOCKOPT); +	if (test__start_subtest("link_attach_detach")) +		test_link_attach_detach(BPF_CGROUP_GETSOCKOPT); +	if (test__start_subtest("preorder_prog_attach_detach")) +		test_preorder_prog_attach_detach(BPF_CGROUP_GETSOCKOPT); +	if (test__start_subtest("preorder_link_attach_detach")) +		test_preorder_link_attach_detach(BPF_CGROUP_GETSOCKOPT); +	if (test__start_subtest("invalid_attach_detach")) +		test_invalid_attach_detach(BPF_CGROUP_GETSOCKOPT); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c new file mode 100644 index 000000000000..a36d2e968bc5 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_mprog_ordering.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include <test_progs.h> +#include "cgroup_helpers.h" +#include "cgroup_preorder.skel.h" + +static int run_getsockopt_test(int cg_parent, int sock_fd, bool has_relative_fd) +{ +	LIBBPF_OPTS(bpf_prog_attach_opts, opts); +	enum bpf_attach_type prog_p_atype, prog_p2_atype; +	int prog_p_fd, prog_p2_fd; +	struct cgroup_preorder *skel = NULL; +	struct bpf_program *prog; +	__u8 *result, buf; +	socklen_t optlen = 1; +	int err = 0; + +	skel = cgroup_preorder__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "cgroup_preorder__open_and_load")) +		return 0; + +	LIBBPF_OPTS_RESET(opts); +	opts.flags = BPF_F_ALLOW_MULTI; +	prog = skel->progs.parent; +	prog_p_fd = bpf_program__fd(prog); +	prog_p_atype = bpf_program__expected_attach_type(prog); +	err = bpf_prog_attach_opts(prog_p_fd, cg_parent, prog_p_atype, &opts); +	if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent")) +		goto close_skel; + +	opts.flags = BPF_F_ALLOW_MULTI | BPF_F_BEFORE; +	if (has_relative_fd) +		opts.relative_fd = prog_p_fd; +	prog = skel->progs.parent_2; +	prog_p2_fd = bpf_program__fd(prog); +	prog_p2_atype = bpf_program__expected_attach_type(prog); +	err = bpf_prog_attach_opts(prog_p2_fd, cg_parent, prog_p2_atype, &opts); +	if (!ASSERT_OK(err, "bpf_prog_attach_opts-parent_2")) +		goto detach_parent; + +	err = getsockopt(sock_fd, SOL_IP, IP_TOS, &buf, &optlen); +	if (!ASSERT_OK(err, "getsockopt")) +		goto detach_parent_2; + +	result = skel->bss->result; +	ASSERT_TRUE(result[0] == 4 && result[1] == 3, "result values"); + +detach_parent_2: +	ASSERT_OK(bpf_prog_detach2(prog_p2_fd, cg_parent, prog_p2_atype), +		  "bpf_prog_detach2-parent_2"); +detach_parent: +	ASSERT_OK(bpf_prog_detach2(prog_p_fd, cg_parent, prog_p_atype), +		  "bpf_prog_detach2-parent"); +close_skel: +	cgroup_preorder__destroy(skel); +	return err; +} + +void test_cgroup_mprog_ordering(void) +{ +	int cg_parent = -1, sock_fd = -1; + +	cg_parent = test__join_cgroup("/parent"); +	if (!ASSERT_GE(cg_parent, 0, "join_cgroup /parent")) +		goto out; + +	sock_fd = socket(AF_INET, SOCK_STREAM, 0); +	if (!ASSERT_GE(sock_fd, 0, "socket")) +		goto out; + +	ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, false), "getsockopt_test_1"); +	ASSERT_OK(run_getsockopt_test(cg_parent, sock_fd, true), "getsockopt_test_2"); + +out: +	close(sock_fd); +	close(cg_parent); +} diff --git a/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c b/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c index 87978a0f7eb7..e0dd966e4a3e 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c +++ b/tools/testing/selftests/bpf/prog_tests/cgroup_xattr.c @@ -7,133 +7,60 @@  #include <string.h>  #include <unistd.h>  #include <sys/socket.h> -#include <sys/xattr.h> -  #include <test_progs.h> +#include "cgroup_helpers.h"  #include "read_cgroupfs_xattr.skel.h"  #include "cgroup_read_xattr.skel.h" -#define CGROUP_FS_ROOT "/sys/fs/cgroup/" -#define CGROUP_FS_PARENT CGROUP_FS_ROOT "foo/" +#define CGROUP_FS_PARENT "foo/"  #define CGROUP_FS_CHILD CGROUP_FS_PARENT "bar/" - -static int move_pid_to_cgroup(const char *cgroup_folder, pid_t pid) -{ -	char filename[128]; -	char pid_str[64]; -	int procs_fd; -	int ret; - -	snprintf(filename, sizeof(filename), "%scgroup.procs", cgroup_folder); -	snprintf(pid_str, sizeof(pid_str), "%d", pid); - -	procs_fd = open(filename, O_WRONLY | O_APPEND); -	if (!ASSERT_OK_FD(procs_fd, "open")) -		return -1; - -	ret = write(procs_fd, pid_str, strlen(pid_str)); -	close(procs_fd); -	if (!ASSERT_GT(ret, 0, "write cgroup.procs")) -		return -1; -	return 0; -} - -static void reset_cgroups_and_lo(void) -{ -	rmdir(CGROUP_FS_CHILD); -	rmdir(CGROUP_FS_PARENT); -	system("ip addr del 1.1.1.1/32 dev lo"); -	system("ip link set dev lo down"); -} +#define TMP_FILE "/tmp/selftests_cgroup_xattr"  static const char xattr_value_a[] = "bpf_selftest_value_a";  static const char xattr_value_b[] = "bpf_selftest_value_b";  static const char xattr_name[] = "user.bpf_test"; -static int setup_cgroups_and_lo(void) -{ -	int err; - -	err = mkdir(CGROUP_FS_PARENT, 0755); -	if (!ASSERT_OK(err, "mkdir 1")) -		goto error; -	err = mkdir(CGROUP_FS_CHILD, 0755); -	if (!ASSERT_OK(err, "mkdir 2")) -		goto error; - -	err = setxattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a, -		       strlen(xattr_value_a) + 1, 0); -	if (!ASSERT_OK(err, "setxattr 1")) -		goto error; - -	err = setxattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b, -		       strlen(xattr_value_b) + 1, 0); -	if (!ASSERT_OK(err, "setxattr 2")) -		goto error; - -	err = system("ip link set dev lo up"); -	if (!ASSERT_OK(err, "lo up")) -		goto error; - -	err = system("ip addr add 1.1.1.1 dev lo"); -	if (!ASSERT_OK(err, "lo addr v4")) -		goto error; - -	err = write_sysctl("/proc/sys/net/ipv4/ping_group_range", "0 0"); -	if (!ASSERT_OK(err, "write_sysctl")) -		goto error; - -	return 0; -error: -	reset_cgroups_and_lo(); -	return err; -} -  static void test_read_cgroup_xattr(void)  { -	struct sockaddr_in sa4 = { -		.sin_family = AF_INET, -		.sin_addr.s_addr = htonl(INADDR_LOOPBACK), -	}; +	int tmp_fd, parent_cgroup_fd = -1, child_cgroup_fd = -1;  	struct read_cgroupfs_xattr *skel = NULL; -	pid_t pid = gettid(); -	int sock_fd = -1; -	int connect_fd = -1; -	if (!ASSERT_OK(setup_cgroups_and_lo(), "setup_cgroups_and_lo")) +	parent_cgroup_fd = test__join_cgroup(CGROUP_FS_PARENT); +	if (!ASSERT_OK_FD(parent_cgroup_fd, "create parent cgroup"))  		return; -	if (!ASSERT_OK(move_pid_to_cgroup(CGROUP_FS_CHILD, pid), -		       "move_pid_to_cgroup")) +	if (!ASSERT_OK(set_cgroup_xattr(CGROUP_FS_PARENT, xattr_name, xattr_value_a), +		       "set parent xattr")) +		goto out; + +	child_cgroup_fd = test__join_cgroup(CGROUP_FS_CHILD); +	if (!ASSERT_OK_FD(child_cgroup_fd, "create child cgroup")) +		goto out; +	if (!ASSERT_OK(set_cgroup_xattr(CGROUP_FS_CHILD, xattr_name, xattr_value_b), +		       "set child xattr"))  		goto out;  	skel = read_cgroupfs_xattr__open_and_load();  	if (!ASSERT_OK_PTR(skel, "read_cgroupfs_xattr__open_and_load"))  		goto out; -	skel->bss->target_pid = pid; +	skel->bss->target_pid = gettid();  	if (!ASSERT_OK(read_cgroupfs_xattr__attach(skel), "read_cgroupfs_xattr__attach"))  		goto out; -	sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); -	if (!ASSERT_OK_FD(sock_fd, "sock create")) -		goto out; - -	connect_fd = connect(sock_fd, &sa4, sizeof(sa4)); -	if (!ASSERT_OK_FD(connect_fd, "connect 1")) -		goto out; -	close(connect_fd); +	tmp_fd = open(TMP_FILE, O_RDONLY | O_CREAT); +	ASSERT_OK_FD(tmp_fd, "open tmp file"); +	close(tmp_fd);  	ASSERT_TRUE(skel->bss->found_value_a, "found_value_a");  	ASSERT_TRUE(skel->bss->found_value_b, "found_value_b");  out: -	close(connect_fd); -	close(sock_fd); +	close(child_cgroup_fd); +	close(parent_cgroup_fd);  	read_cgroupfs_xattr__destroy(skel); -	move_pid_to_cgroup(CGROUP_FS_ROOT, pid); -	reset_cgroups_and_lo(); +	unlink(TMP_FILE);  }  void test_cgroup_xattr(void) diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index 62e7ec775f24..9b2d9ceda210 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -21,6 +21,14 @@ static struct {  	{"test_dynptr_data", SETUP_SYSCALL_SLEEP},  	{"test_dynptr_copy", SETUP_SYSCALL_SLEEP},  	{"test_dynptr_copy_xdp", SETUP_XDP_PROG}, +	{"test_dynptr_memset_zero", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_memset_notzero", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_memset_zero_offset", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_memset_zero_adjusted", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_memset_overflow", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_memset_overflow_offset", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_memset_readonly", SETUP_SKB_PROG}, +	{"test_dynptr_memset_xdp_chunks", SETUP_XDP_PROG},  	{"test_ringbuf", SETUP_SYSCALL_SLEEP},  	{"test_skb_readonly", SETUP_SKB_PROG},  	{"test_dynptr_skb_data", SETUP_SKB_PROG}, @@ -43,6 +51,8 @@ static struct {  	{"test_copy_from_user_task_str_dynptr", SETUP_SYSCALL_SLEEP},  }; +#define PAGE_SIZE_64K 65536 +  static void verify_success(const char *prog_name, enum test_setup_type setup_type)  {  	char user_data[384] = {[0 ... 382] = 'a', '\0'}; @@ -138,14 +148,18 @@ static void verify_success(const char *prog_name, enum test_setup_type setup_typ  	}  	case SETUP_XDP_PROG:  	{ -		char data[5000]; +		char data[90000];  		int err, prog_fd;  		LIBBPF_OPTS(bpf_test_run_opts, opts,  			    .data_in = &data, -			    .data_size_in = sizeof(data),  			    .repeat = 1,  		); +		if (getpagesize() == PAGE_SIZE_64K) +			opts.data_size_in = sizeof(data); +		else +			opts.data_size_in = 5000; +  		prog_fd = bpf_program__fd(prog);  		err = bpf_prog_test_run_opts(prog_fd, &opts); diff --git a/tools/testing/selftests/bpf/prog_tests/fd_array.c b/tools/testing/selftests/bpf/prog_tests/fd_array.c index 9add890c2d37..241b2c8c6e0f 100644 --- a/tools/testing/selftests/bpf/prog_tests/fd_array.c +++ b/tools/testing/selftests/bpf/prog_tests/fd_array.c @@ -312,7 +312,7 @@ static void check_fd_array_cnt__referenced_btfs(void)  	/* btf should still exist when original file descriptor is closed */  	err = get_btf_id_by_fd(extra_fds[0], &btf_id); -	if (!ASSERT_GE(err, 0, "get_btf_id_by_fd")) +	if (!ASSERT_EQ(err, 0, "get_btf_id_by_fd"))  		goto cleanup;  	Close(extra_fds[0]); diff --git a/tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c b/tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c deleted file mode 100644 index 568d3aa48a78..000000000000 --- a/tools/testing/selftests/bpf/prog_tests/fexit_noreturns.c +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include <test_progs.h> -#include "fexit_noreturns.skel.h" - -void test_fexit_noreturns(void) -{ -	RUN_TESTS(fexit_noreturns); -} diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 5266c7022863..14c5a7ef0e87 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -72,7 +72,7 @@ static struct {  	{ "new_null_ret", "R0 invalid mem access 'ptr_or_null_'" },  	{ "obj_new_acq", "Unreleased reference id=" },  	{ "use_after_drop", "invalid mem access 'scalar'" }, -	{ "ptr_walk_scalar", "type=scalar expected=percpu_ptr_" }, +	{ "ptr_walk_scalar", "type=rdonly_untrusted_mem expected=percpu_ptr_" },  	{ "direct_read_lock", "direct access to bpf_spin_lock is disallowed" },  	{ "direct_write_lock", "direct access to bpf_spin_lock is disallowed" },  	{ "direct_read_head", "direct access to bpf_list_head is disallowed" }, diff --git a/tools/testing/selftests/bpf/prog_tests/log_buf.c b/tools/testing/selftests/bpf/prog_tests/log_buf.c index 169ce689b97c..d6f14a232002 100644 --- a/tools/testing/selftests/bpf/prog_tests/log_buf.c +++ b/tools/testing/selftests/bpf/prog_tests/log_buf.c @@ -7,6 +7,10 @@  #include "test_log_buf.skel.h"  #include "bpf_util.h" +#if !defined(__clang__) +#pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif +  static size_t libbpf_log_pos;  static char libbpf_log_buf[1024 * 1024];  static bool libbpf_log_error; diff --git a/tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c b/tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c new file mode 100644 index 000000000000..40d4f687bd9c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/mem_rdonly_untrusted.c @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <test_progs.h> +#include "mem_rdonly_untrusted.skel.h" + +void test_mem_rdonly_untrusted(void) +{ +	RUN_TESTS(mem_rdonly_untrusted); +} diff --git a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c index 39d42271cc46..e261b0e872db 100644 --- a/tools/testing/selftests/bpf/prog_tests/reg_bounds.c +++ b/tools/testing/selftests/bpf/prog_tests/reg_bounds.c @@ -465,6 +465,20 @@ static struct range range_refine(enum num_t x_t, struct range x, enum num_t y_t,  		return range_improve(x_t, x, x_swap);  	} +	if (!t_is_32(x_t) && !t_is_32(y_t) && x_t != y_t) { +		if (x_t == S64 && x.a > x.b) { +			if (x.b < y.a && x.a <= y.b) +				return range(x_t, x.a, y.b); +			if (x.a > y.b && x.b >= y.a) +				return range(x_t, y.a, x.b); +		} else if (x_t == U64 && y.a > y.b) { +			if (y.b < x.a && y.a <= x.b) +				return range(x_t, y.a, x.b); +			if (y.a > x.b && y.b >= x.a) +				return range(x_t, x.a, y.b); +		} +	} +  	/* otherwise, plain range cast and intersection works */  	return range_improve(x_t, x, y_cast);  } diff --git a/tools/testing/selftests/bpf/prog_tests/ringbuf.c b/tools/testing/selftests/bpf/prog_tests/ringbuf.c index da430df45aa4..d1e4cb28a72c 100644 --- a/tools/testing/selftests/bpf/prog_tests/ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/ringbuf.c @@ -97,7 +97,7 @@ static void ringbuf_write_subtest(void)  	if (!ASSERT_OK_PTR(skel, "skel_open"))  		return; -	skel->maps.ringbuf.max_entries = 0x4000; +	skel->maps.ringbuf.max_entries = 0x40000;  	err = test_ringbuf_write_lskel__load(skel);  	if (!ASSERT_OK(err, "skel_load")) @@ -108,7 +108,7 @@ static void ringbuf_write_subtest(void)  	mmap_ptr = mmap(NULL, page_size, PROT_READ | PROT_WRITE, MAP_SHARED, rb_fd, 0);  	if (!ASSERT_OK_PTR(mmap_ptr, "rw_cons_pos"))  		goto cleanup; -	*mmap_ptr = 0x3000; +	*mmap_ptr = 0x30000;  	ASSERT_OK(munmap(mmap_ptr, page_size), "unmap_rw");  	skel->bss->pid = getpid(); diff --git a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c index a4517bee34d5..27781df8f2fb 100644 --- a/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c +++ b/tools/testing/selftests/bpf/prog_tests/sock_iter_batch.c @@ -1,11 +1,13 @@  // SPDX-License-Identifier: GPL-2.0  // Copyright (c) 2024 Meta +#include <poll.h>  #include <test_progs.h>  #include "network_helpers.h"  #include "sock_iter_batch.skel.h"  #define TEST_NS "sock_iter_batch_netns" +#define TEST_CHILD_NS "sock_iter_batch_child_netns"  static const int init_batch_size = 16;  static const int nr_soreuse = 4; @@ -118,6 +120,45 @@ done:  	return nth_sock_idx;  } +static void destroy(int fd) +{ +	struct sock_iter_batch *skel = NULL; +	__u64 cookie = socket_cookie(fd); +	struct bpf_link *link = NULL; +	int iter_fd = -1; +	int nread; +	__u64 out; + +	skel = sock_iter_batch__open(); +	if (!ASSERT_OK_PTR(skel, "sock_iter_batch__open")) +		goto done; + +	skel->rodata->destroy_cookie = cookie; + +	if (!ASSERT_OK(sock_iter_batch__load(skel), "sock_iter_batch__load")) +		goto done; + +	link = bpf_program__attach_iter(skel->progs.iter_tcp_destroy, NULL); +	if (!ASSERT_OK_PTR(link, "bpf_program__attach_iter")) +		goto done; + +	iter_fd = bpf_iter_create(bpf_link__fd(link)); +	if (!ASSERT_OK_FD(iter_fd, "bpf_iter_create")) +		goto done; + +	/* Delete matching socket. */ +	nread = read(iter_fd, &out, sizeof(out)); +	ASSERT_GE(nread, 0, "nread"); +	if (nread) +		ASSERT_EQ(out, cookie, "cookie matches"); +done: +	if (iter_fd >= 0) +		close(iter_fd); +	bpf_link__destroy(link); +	sock_iter_batch__destroy(skel); +	close(fd); +} +  static int get_seen_count(int fd, struct sock_count counts[], int n)  {  	__u64 cookie = socket_cookie(fd); @@ -152,8 +193,71 @@ static void check_n_were_seen_once(int *fds, int fds_len, int n,  	ASSERT_EQ(seen_once, n, "seen_once");  } +static int accept_from_one(struct pollfd *server_poll_fds, +			   int server_poll_fds_len) +{ +	static const int poll_timeout_ms = 5000; /* 5s */ +	int ret; +	int i; + +	ret = poll(server_poll_fds, server_poll_fds_len, poll_timeout_ms); +	if (!ASSERT_EQ(ret, 1, "poll")) +		return -1; + +	for (i = 0; i < server_poll_fds_len; i++) +		if (server_poll_fds[i].revents & POLLIN) +			return accept(server_poll_fds[i].fd, NULL, NULL); + +	return -1; +} + +static int *connect_to_server(int family, int sock_type, const char *addr, +			      __u16 port, int nr_connects, int *server_fds, +			      int server_fds_len) +{ +	struct pollfd *server_poll_fds = NULL; +	int *established_socks = NULL; +	int i; + +	server_poll_fds = calloc(server_fds_len, sizeof(*server_poll_fds)); +	if (!ASSERT_OK_PTR(server_poll_fds, "server_poll_fds")) +		return NULL; + +	for (i = 0; i < server_fds_len; i++) { +		server_poll_fds[i].fd = server_fds[i]; +		server_poll_fds[i].events = POLLIN; +	} + +	i = 0; + +	established_socks = malloc(sizeof(*established_socks) * nr_connects*2); +	if (!ASSERT_OK_PTR(established_socks, "established_socks")) +		goto error; + +	while (nr_connects--) { +		established_socks[i] = connect_to_addr_str(family, sock_type, +							   addr, port, NULL); +		if (!ASSERT_OK_FD(established_socks[i], "connect_to_addr_str")) +			goto error; +		i++; +		established_socks[i] = accept_from_one(server_poll_fds, +						       server_fds_len); +		if (!ASSERT_OK_FD(established_socks[i], "accept_from_one")) +			goto error; +		i++; +	} + +	free(server_poll_fds); +	return established_socks; +error: +	free_fds(established_socks, i); +	free(server_poll_fds); +	return NULL; +} +  static void remove_seen(int family, int sock_type, const char *addr, __u16 port, -			int *socks, int socks_len, struct sock_count *counts, +			int *socks, int socks_len, int *established_socks, +			int established_socks_len, struct sock_count *counts,  			int counts_len, struct bpf_link *link, int iter_fd)  {  	int close_idx; @@ -182,8 +286,46 @@ static void remove_seen(int family, int sock_type, const char *addr, __u16 port,  			       counts_len);  } +static void remove_seen_established(int family, int sock_type, const char *addr, +				    __u16 port, int *listen_socks, +				    int listen_socks_len, int *established_socks, +				    int established_socks_len, +				    struct sock_count *counts, int counts_len, +				    struct bpf_link *link, int iter_fd) +{ +	int close_idx; + +	/* Iterate through all listening sockets. */ +	read_n(iter_fd, listen_socks_len, counts, counts_len); + +	/* Make sure we saw all listening sockets exactly once. */ +	check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, +			       counts, counts_len); + +	/* Leave one established socket. */ +	read_n(iter_fd, established_socks_len - 1, counts, counts_len); + +	/* Close a socket we've already seen to remove it from the bucket. */ +	close_idx = get_nth_socket(established_socks, established_socks_len, +				   link, listen_socks_len + 1); +	if (!ASSERT_GE(close_idx, 0, "close_idx")) +		return; +	destroy(established_socks[close_idx]); +	established_socks[close_idx] = -1; + +	/* Iterate through the rest of the sockets. */ +	read_n(iter_fd, -1, counts, counts_len); + +	/* Make sure the last socket wasn't skipped and that there were no +	 * repeats. +	 */ +	check_n_were_seen_once(established_socks, established_socks_len, +			       established_socks_len - 1, counts, counts_len); +} +  static void remove_unseen(int family, int sock_type, const char *addr,  			  __u16 port, int *socks, int socks_len, +			  int *established_socks, int established_socks_len,  			  struct sock_count *counts, int counts_len,  			  struct bpf_link *link, int iter_fd)  { @@ -214,8 +356,54 @@ static void remove_unseen(int family, int sock_type, const char *addr,  			       counts_len);  } +static void remove_unseen_established(int family, int sock_type, +				      const char *addr, __u16 port, +				      int *listen_socks, int listen_socks_len, +				      int *established_socks, +				      int established_socks_len, +				      struct sock_count *counts, int counts_len, +				      struct bpf_link *link, int iter_fd) +{ +	int close_idx; + +	/* Iterate through all listening sockets. */ +	read_n(iter_fd, listen_socks_len, counts, counts_len); + +	/* Make sure we saw all listening sockets exactly once. */ +	check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, +			       counts, counts_len); + +	/* Iterate through the first established socket. */ +	read_n(iter_fd, 1, counts, counts_len); + +	/* Make sure we saw one established socks. */ +	check_n_were_seen_once(established_socks, established_socks_len, 1, +			       counts, counts_len); + +	/* Close what would be the next socket in the bucket to exercise the +	 * condition where we need to skip past the first cookie we remembered. +	 */ +	close_idx = get_nth_socket(established_socks, established_socks_len, +				   link, listen_socks_len + 1); +	if (!ASSERT_GE(close_idx, 0, "close_idx")) +		return; + +	destroy(established_socks[close_idx]); +	established_socks[close_idx] = -1; + +	/* Iterate through the rest of the sockets. */ +	read_n(iter_fd, -1, counts, counts_len); + +	/* Make sure the remaining sockets were seen exactly once and that we +	 * didn't repeat the socket that was already seen. +	 */ +	check_n_were_seen_once(established_socks, established_socks_len, +			       established_socks_len - 1, counts, counts_len); +} +  static void remove_all(int family, int sock_type, const char *addr,  		       __u16 port, int *socks, int socks_len, +		       int *established_socks, int established_socks_len,  		       struct sock_count *counts, int counts_len,  		       struct bpf_link *link, int iter_fd)  { @@ -242,8 +430,57 @@ static void remove_all(int family, int sock_type, const char *addr,  	ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n");  } +static void remove_all_established(int family, int sock_type, const char *addr, +				   __u16 port, int *listen_socks, +				   int listen_socks_len, int *established_socks, +				   int established_socks_len, +				   struct sock_count *counts, int counts_len, +				   struct bpf_link *link, int iter_fd) +{ +	int *close_idx = NULL; +	int i; + +	/* Iterate through all listening sockets. */ +	read_n(iter_fd, listen_socks_len, counts, counts_len); + +	/* Make sure we saw all listening sockets exactly once. */ +	check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, +			       counts, counts_len); + +	/* Iterate through the first established socket. */ +	read_n(iter_fd, 1, counts, counts_len); + +	/* Make sure we saw one established socks. */ +	check_n_were_seen_once(established_socks, established_socks_len, 1, +			       counts, counts_len); + +	/* Close all remaining sockets to exhaust the list of saved cookies and +	 * exit without putting any sockets into the batch on the next read. +	 */ +	close_idx = malloc(sizeof(int) * (established_socks_len - 1)); +	if (!ASSERT_OK_PTR(close_idx, "close_idx malloc")) +		return; +	for (i = 0; i < established_socks_len - 1; i++) { +		close_idx[i] = get_nth_socket(established_socks, +					      established_socks_len, link, +					      listen_socks_len + i); +		if (!ASSERT_GE(close_idx[i], 0, "close_idx")) +			return; +	} + +	for (i = 0; i < established_socks_len - 1; i++) { +		destroy(established_socks[close_idx[i]]); +		established_socks[close_idx[i]] = -1; +	} + +	/* Make sure there are no more sockets returned */ +	ASSERT_EQ(read_n(iter_fd, -1, counts, counts_len), 0, "read_n"); +	free(close_idx); +} +  static void add_some(int family, int sock_type, const char *addr, __u16 port, -		     int *socks, int socks_len, struct sock_count *counts, +		     int *socks, int socks_len, int *established_socks, +		     int established_socks_len, struct sock_count *counts,  		     int counts_len, struct bpf_link *link, int iter_fd)  {  	int *new_socks = NULL; @@ -271,8 +508,52 @@ done:  	free_fds(new_socks, socks_len);  } +static void add_some_established(int family, int sock_type, const char *addr, +				 __u16 port, int *listen_socks, +				 int listen_socks_len, int *established_socks, +				 int established_socks_len, +				 struct sock_count *counts, +				 int counts_len, struct bpf_link *link, +				 int iter_fd) +{ +	int *new_socks = NULL; + +	/* Iterate through all listening sockets. */ +	read_n(iter_fd, listen_socks_len, counts, counts_len); + +	/* Make sure we saw all listening sockets exactly once. */ +	check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, +			       counts, counts_len); + +	/* Iterate through the first established_socks_len - 1 sockets. */ +	read_n(iter_fd, established_socks_len - 1, counts, counts_len); + +	/* Make sure we saw established_socks_len - 1 sockets exactly once. */ +	check_n_were_seen_once(established_socks, established_socks_len, +			       established_socks_len - 1, counts, counts_len); + +	/* Double the number of established sockets in the bucket. */ +	new_socks = connect_to_server(family, sock_type, addr, port, +				      established_socks_len / 2, listen_socks, +				      listen_socks_len); +	if (!ASSERT_OK_PTR(new_socks, "connect_to_server")) +		goto done; + +	/* Iterate through the rest of the sockets. */ +	read_n(iter_fd, -1, counts, counts_len); + +	/* Make sure each of the original sockets was seen exactly once. */ +	check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, +			       counts, counts_len); +	check_n_were_seen_once(established_socks, established_socks_len, +			       established_socks_len, counts, counts_len); +done: +	free_fds(new_socks, established_socks_len); +} +  static void force_realloc(int family, int sock_type, const char *addr,  			  __u16 port, int *socks, int socks_len, +			  int *established_socks, int established_socks_len,  			  struct sock_count *counts, int counts_len,  			  struct bpf_link *link, int iter_fd)  { @@ -299,11 +580,32 @@ done:  	free_fds(new_socks, socks_len);  } +static void force_realloc_established(int family, int sock_type, +				      const char *addr, __u16 port, +				      int *listen_socks, int listen_socks_len, +				      int *established_socks, +				      int established_socks_len, +				      struct sock_count *counts, int counts_len, +				      struct bpf_link *link, int iter_fd) +{ +	/* Iterate through all sockets to trigger a realloc. */ +	read_n(iter_fd, -1, counts, counts_len); + +	/* Make sure each socket was seen exactly once. */ +	check_n_were_seen_once(listen_socks, listen_socks_len, listen_socks_len, +			       counts, counts_len); +	check_n_were_seen_once(established_socks, established_socks_len, +			       established_socks_len, counts, counts_len); +} +  struct test_case {  	void (*test)(int family, int sock_type, const char *addr, __u16 port, -		     int *socks, int socks_len, struct sock_count *counts, +		     int *socks, int socks_len, int *established_socks, +		     int established_socks_len, struct sock_count *counts,  		     int counts_len, struct bpf_link *link, int iter_fd);  	const char *description; +	int ehash_buckets; +	int connections;  	int init_socks;  	int max_socks;  	int sock_type; @@ -358,18 +660,140 @@ static struct test_case resume_tests[] = {  		.family = AF_INET6,  		.test = force_realloc,  	}, +	{ +		.description = "tcp: resume after removing a seen socket (listening)", +		.init_socks = nr_soreuse, +		.max_socks = nr_soreuse, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = remove_seen, +	}, +	{ +		.description = "tcp: resume after removing one unseen socket (listening)", +		.init_socks = nr_soreuse, +		.max_socks = nr_soreuse, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = remove_unseen, +	}, +	{ +		.description = "tcp: resume after removing all unseen sockets (listening)", +		.init_socks = nr_soreuse, +		.max_socks = nr_soreuse, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = remove_all, +	}, +	{ +		.description = "tcp: resume after adding a few sockets (listening)", +		.init_socks = nr_soreuse, +		.max_socks = nr_soreuse, +		.sock_type = SOCK_STREAM, +		/* Use AF_INET so that new sockets are added to the head of the +		 * bucket's list. +		 */ +		.family = AF_INET, +		.test = add_some, +	}, +	{ +		.description = "tcp: force a realloc to occur (listening)", +		.init_socks = init_batch_size, +		.max_socks = init_batch_size * 2, +		.sock_type = SOCK_STREAM, +		/* Use AF_INET6 so that new sockets are added to the tail of the +		 * bucket's list, needing to be added to the next batch to force +		 * a realloc. +		 */ +		.family = AF_INET6, +		.test = force_realloc, +	}, +	{ +		.description = "tcp: resume after removing a seen socket (established)", +		/* Force all established sockets into one bucket */ +		.ehash_buckets = 1, +		.connections = nr_soreuse, +		.init_socks = nr_soreuse, +		/* Room for connect()ed and accept()ed sockets */ +		.max_socks = nr_soreuse * 3, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = remove_seen_established, +	}, +	{ +		.description = "tcp: resume after removing one unseen socket (established)", +		/* Force all established sockets into one bucket */ +		.ehash_buckets = 1, +		.connections = nr_soreuse, +		.init_socks = nr_soreuse, +		/* Room for connect()ed and accept()ed sockets */ +		.max_socks = nr_soreuse * 3, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = remove_unseen_established, +	}, +	{ +		.description = "tcp: resume after removing all unseen sockets (established)", +		/* Force all established sockets into one bucket */ +		.ehash_buckets = 1, +		.connections = nr_soreuse, +		.init_socks = nr_soreuse, +		/* Room for connect()ed and accept()ed sockets */ +		.max_socks = nr_soreuse * 3, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = remove_all_established, +	}, +	{ +		.description = "tcp: resume after adding a few sockets (established)", +		/* Force all established sockets into one bucket */ +		.ehash_buckets = 1, +		.connections = nr_soreuse, +		.init_socks = nr_soreuse, +		/* Room for connect()ed and accept()ed sockets */ +		.max_socks = nr_soreuse * 3, +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = add_some_established, +	}, +	{ +		.description = "tcp: force a realloc to occur (established)", +		/* Force all established sockets into one bucket */ +		.ehash_buckets = 1, +		/* Bucket size will need to double when going from listening to +		 * established sockets. +		 */ +		.connections = init_batch_size, +		.init_socks = nr_soreuse, +		/* Room for connect()ed and accept()ed sockets */ +		.max_socks = nr_soreuse + (init_batch_size * 2), +		.sock_type = SOCK_STREAM, +		.family = AF_INET6, +		.test = force_realloc_established, +	},  };  static void do_resume_test(struct test_case *tc)  {  	struct sock_iter_batch *skel = NULL; +	struct sock_count *counts = NULL;  	static const __u16 port = 10001; +	struct nstoken *nstoken = NULL;  	struct bpf_link *link = NULL; -	struct sock_count *counts; +	int *established_fds = NULL;  	int err, iter_fd = -1;  	const char *addr;  	int *fds = NULL; -	int local_port; + +	if (tc->ehash_buckets) { +		SYS_NOFAIL("ip netns del " TEST_CHILD_NS); +		SYS(done, "sysctl -wq net.ipv4.tcp_child_ehash_entries=%d", +		    tc->ehash_buckets); +		SYS(done, "ip netns add %s", TEST_CHILD_NS); +		SYS(done, "ip -net %s link set dev lo up", TEST_CHILD_NS); +		nstoken = open_netns(TEST_CHILD_NS); +		if (!ASSERT_OK_PTR(nstoken, "open_child_netns")) +			goto done; +	}  	counts = calloc(tc->max_socks, sizeof(*counts));  	if (!ASSERT_OK_PTR(counts, "counts")) @@ -384,11 +808,18 @@ static void do_resume_test(struct test_case *tc)  				     tc->init_socks);  	if (!ASSERT_OK_PTR(fds, "start_reuseport_server"))  		goto done; -	local_port = get_socket_local_port(*fds); -	if (!ASSERT_GE(local_port, 0, "get_socket_local_port")) -		goto done; -	skel->rodata->ports[0] = ntohs(local_port); +	if (tc->connections) { +		established_fds = connect_to_server(tc->family, tc->sock_type, +						    addr, port, +						    tc->connections, fds, +						    tc->init_socks); +		if (!ASSERT_OK_PTR(established_fds, "connect_to_server")) +			goto done; +	} +	skel->rodata->ports[0] = 0; +	skel->rodata->ports[1] = 0;  	skel->rodata->sf = tc->family; +	skel->rodata->ss = 0;  	err = sock_iter_batch__load(skel);  	if (!ASSERT_OK(err, "sock_iter_batch__load")) @@ -406,10 +837,15 @@ static void do_resume_test(struct test_case *tc)  		goto done;  	tc->test(tc->family, tc->sock_type, addr, port, fds, tc->init_socks, -		 counts, tc->max_socks, link, iter_fd); +		 established_fds, tc->connections*2, counts, tc->max_socks, +		 link, iter_fd);  done: +	close_netns(nstoken); +	SYS_NOFAIL("ip netns del " TEST_CHILD_NS); +	SYS_NOFAIL("sysctl -w net.ipv4.tcp_child_ehash_entries=0");  	free(counts);  	free_fds(fds, tc->init_socks); +	free_fds(established_fds, tc->connections*2);  	if (iter_fd >= 0)  		close(iter_fd);  	bpf_link__destroy(link); @@ -454,6 +890,8 @@ static void do_test(int sock_type, bool onebyone)  		skel->rodata->ports[i] = ntohs(local_port);  	}  	skel->rodata->sf = AF_INET6; +	if (sock_type == SOCK_STREAM) +		skel->rodata->ss = TCP_LISTEN;  	err = sock_iter_batch__load(skel);  	if (!ASSERT_OK(err, "sock_iter_batch__load")) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c index b6c471da5c28..b87e7f39e15a 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_ktls.c @@ -314,6 +314,95 @@ out:  	test_sockmap_ktls__destroy(skel);  } +static void test_sockmap_ktls_tx_pop(int family, int sotype) +{ +	char msg[37] = "0123456789abcdefghijklmnopqrstuvwxyz\0"; +	int c = 0, p = 0, one = 1, sent, recvd; +	struct test_sockmap_ktls *skel; +	int prog_fd, map_fd; +	char rcv[50] = {0}; +	int err; +	int i, m, r; + +	skel = test_sockmap_ktls__open_and_load(); +	if (!ASSERT_TRUE(skel, "open ktls skel")) +		return; + +	err = create_pair(family, sotype, &c, &p); +	if (!ASSERT_OK(err, "create_pair()")) +		goto out; + +	prog_fd = bpf_program__fd(skel->progs.prog_sk_policy); +	map_fd = bpf_map__fd(skel->maps.sock_map); + +	err = bpf_prog_attach(prog_fd, map_fd, BPF_SK_MSG_VERDICT, 0); +	if (!ASSERT_OK(err, "bpf_prog_attach sk msg")) +		goto out; + +	err = bpf_map_update_elem(map_fd, &one, &c, BPF_NOEXIST); +	if (!ASSERT_OK(err, "bpf_map_update_elem(c)")) +		goto out; + +	err = init_ktls_pairs(c, p); +	if (!ASSERT_OK(err, "init_ktls_pairs(c, p)")) +		goto out; + +	struct { +		int	pop_start; +		int	pop_len; +	} pop_policy[] = { +		/* trim the start */ +		{0, 2}, +		{0, 10}, +		{1, 2}, +		{1, 10}, +		/* trim the end */ +		{35, 2}, +		/* New entries should be added before this line */ +		{-1, -1}, +	}; + +	i = 0; +	while (pop_policy[i].pop_start >= 0) { +		skel->bss->pop_start = pop_policy[i].pop_start; +		skel->bss->pop_end =  pop_policy[i].pop_len; + +		sent = send(c, msg, sizeof(msg), 0); +		if (!ASSERT_EQ(sent, sizeof(msg), "send(msg)")) +			goto out; + +		recvd = recv_timeout(p, rcv, sizeof(rcv), MSG_DONTWAIT, 1); +		if (!ASSERT_EQ(recvd, sizeof(msg) - pop_policy[i].pop_len, "pop len mismatch")) +			goto out; + +		/* verify the data +		 * msg: 0123456789a bcdefghij klmnopqrstuvwxyz +		 *                  |       | +		 *                  popped data +		 */ +		for (m = 0, r = 0; m < sizeof(msg);) { +			/* skip checking the data that has been popped */ +			if (m >= pop_policy[i].pop_start && +			    m <= pop_policy[i].pop_start + pop_policy[i].pop_len - 1) { +				m++; +				continue; +			} + +			if (!ASSERT_EQ(msg[m], rcv[r], "data mismatch")) +				goto out; +			m++; +			r++; +		} +		i++; +	} +out: +	if (c) +		close(c); +	if (p) +		close(p); +	test_sockmap_ktls__destroy(skel); +} +  static void run_tests(int family, enum bpf_map_type map_type)  {  	int map; @@ -338,6 +427,8 @@ static void run_ktls_test(int family, int sotype)  		test_sockmap_ktls_tx_cork(family, sotype, true);  	if (test__start_subtest("tls tx egress with no buf"))  		test_sockmap_ktls_tx_no_buf(family, sotype, true); +	if (test__start_subtest("tls tx with pop")) +		test_sockmap_ktls_tx_pop(family, sotype);  }  void test_sockmap_ktls(void) diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index 1d98eee7a2c3..f1bdccc7e4e7 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -924,6 +924,8 @@ static void redir_partial(int family, int sotype, int sock_map, int parser_map)  		goto close;  	n = xsend(c1, buf, sizeof(buf), 0); +	if (n == -1) +		goto close;  	if (n < sizeof(buf))  		FAIL("incomplete write"); diff --git a/tools/testing/selftests/bpf/prog_tests/stream.c b/tools/testing/selftests/bpf/prog_tests/stream.c new file mode 100644 index 000000000000..d9f0185dca61 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/stream.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ +#include <test_progs.h> +#include <sys/mman.h> +#include <regex.h> + +#include "stream.skel.h" +#include "stream_fail.skel.h" + +void test_stream_failure(void) +{ +	RUN_TESTS(stream_fail); +} + +void test_stream_success(void) +{ +	RUN_TESTS(stream); +	return; +} + +struct { +	int prog_off; +	const char *errstr; +} stream_error_arr[] = { +	{ +		offsetof(struct stream, progs.stream_cond_break), +		"ERROR: Timeout detected for may_goto instruction\n" +		"CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n" +		"Call trace:\n" +		"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" +		"|[ \t]+[^\n]+\n)*", +	}, +	{ +		offsetof(struct stream, progs.stream_deadlock), +		"ERROR: AA or ABBA deadlock detected for bpf_res_spin_lock\n" +		"Attempted lock   = (0x[0-9a-fA-F]+)\n" +		"Total held locks = 1\n" +		"Held lock\\[ 0\\] = \\1\n"  // Lock address must match +		"CPU: [0-9]+ UID: 0 PID: [0-9]+ Comm: .*\n" +		"Call trace:\n" +		"([a-zA-Z_][a-zA-Z0-9_]*\\+0x[0-9a-fA-F]+/0x[0-9a-fA-F]+\n" +		"|[ \t]+[^\n]+\n)*", +	}, +}; + +static int match_regex(const char *pattern, const char *string) +{ +	int err, rc; +	regex_t re; + +	err = regcomp(&re, pattern, REG_EXTENDED | REG_NEWLINE); +	if (err) +		return -1; +	rc = regexec(&re, string, 0, NULL, 0); +	regfree(&re); +	return rc == 0 ? 1 : 0; +} + +void test_stream_errors(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts); +	LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts); +	struct stream *skel; +	int ret, prog_fd; +	char buf[1024]; + +	skel = stream__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "stream__open_and_load")) +		return; + +	for (int i = 0; i < ARRAY_SIZE(stream_error_arr); i++) { +		struct bpf_program **prog; + +		prog = (struct bpf_program **)(((char *)skel) + stream_error_arr[i].prog_off); +		prog_fd = bpf_program__fd(*prog); +		ret = bpf_prog_test_run_opts(prog_fd, &opts); +		ASSERT_OK(ret, "ret"); +		ASSERT_OK(opts.retval, "retval"); + +#if !defined(__x86_64__) +		ASSERT_TRUE(1, "Timed may_goto unsupported, skip."); +		if (i == 0) { +			ret = bpf_prog_stream_read(prog_fd, 2, buf, sizeof(buf), &ropts); +			ASSERT_EQ(ret, 0, "stream read"); +			continue; +		} +#endif + +		ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, sizeof(buf), &ropts); +		ASSERT_GT(ret, 0, "stream read"); +		ASSERT_LE(ret, 1023, "len for buf"); +		buf[ret] = '\0'; + +		ret = match_regex(stream_error_arr[i].errstr, buf); +		if (!ASSERT_TRUE(ret == 1, "regex match")) +			fprintf(stderr, "Output from stream:\n%s\n", buf); +	} + +	stream__destroy(skel); +} + +void test_stream_syscall(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts); +	LIBBPF_OPTS(bpf_prog_stream_read_opts, ropts); +	struct stream *skel; +	int ret, prog_fd; +	char buf[64]; + +	skel = stream__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "stream__open_and_load")) +		return; + +	prog_fd = bpf_program__fd(skel->progs.stream_syscall); +	ret = bpf_prog_test_run_opts(prog_fd, &opts); +	ASSERT_OK(ret, "ret"); +	ASSERT_OK(opts.retval, "retval"); + +	ASSERT_LT(bpf_prog_stream_read(0, BPF_STREAM_STDOUT, buf, sizeof(buf), &ropts), 0, "error"); +	ret = -errno; +	ASSERT_EQ(ret, -EINVAL, "bad prog_fd"); + +	ASSERT_LT(bpf_prog_stream_read(prog_fd, 0, buf, sizeof(buf), &ropts), 0, "error"); +	ret = -errno; +	ASSERT_EQ(ret, -ENOENT, "bad stream id"); + +	ASSERT_LT(bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, NULL, sizeof(buf), NULL), 0, "error"); +	ret = -errno; +	ASSERT_EQ(ret, -EFAULT, "bad stream buf"); + +	ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 2, NULL); +	ASSERT_EQ(ret, 2, "bytes"); +	ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 2, NULL); +	ASSERT_EQ(ret, 1, "bytes"); +	ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDOUT, buf, 1, &ropts); +	ASSERT_EQ(ret, 0, "no bytes stdout"); +	ret = bpf_prog_stream_read(prog_fd, BPF_STREAM_STDERR, buf, 1, &ropts); +	ASSERT_EQ(ret, 0, "no bytes stderr"); + +	stream__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c new file mode 100644 index 000000000000..35af8044d059 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/string_kfuncs.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2025 Red Hat, Inc.*/ +#include <test_progs.h> +#include "string_kfuncs_success.skel.h" +#include "string_kfuncs_failure1.skel.h" +#include "string_kfuncs_failure2.skel.h" +#include <sys/mman.h> + +static const char * const test_cases[] = { +	"strcmp", +	"strchr", +	"strchrnul", +	"strnchr", +	"strrchr", +	"strlen", +	"strnlen", +	"strspn_str", +	"strspn_accept", +	"strcspn_str", +	"strcspn_reject", +	"strstr", +	"strnstr", +}; + +void run_too_long_tests(void) +{ +	struct string_kfuncs_failure2 *skel; +	struct bpf_program *prog; +	char test_name[256]; +	int err, i; + +	skel = string_kfuncs_failure2__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "string_kfuncs_failure2__open_and_load")) +		return; + +	memset(skel->bss->long_str, 'a', sizeof(skel->bss->long_str)); + +	for (i = 0; i < ARRAY_SIZE(test_cases); i++) { +		sprintf(test_name, "test_%s_too_long", test_cases[i]); +		if (!test__start_subtest(test_name)) +			continue; + +		prog = bpf_object__find_program_by_name(skel->obj, test_name); +		if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) +			goto cleanup; + +		LIBBPF_OPTS(bpf_test_run_opts, topts); +		err = bpf_prog_test_run_opts(bpf_program__fd(prog), &topts); +		if (!ASSERT_OK(err, "bpf_prog_test_run")) +			goto cleanup; + +		ASSERT_EQ(topts.retval, -E2BIG, "reading too long string fails with -E2BIG"); +	} + +cleanup: +	string_kfuncs_failure2__destroy(skel); +} + +void test_string_kfuncs(void) +{ +	RUN_TESTS(string_kfuncs_success); +	RUN_TESTS(string_kfuncs_failure1); + +	run_too_long_tests(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/tailcalls.c b/tools/testing/selftests/bpf/prog_tests/tailcalls.c index 66a900327f91..0ab36503c3b2 100644 --- a/tools/testing/selftests/bpf/prog_tests/tailcalls.c +++ b/tools/testing/selftests/bpf/prog_tests/tailcalls.c @@ -1195,7 +1195,7 @@ static void test_tailcall_hierarchy_count(const char *which, bool test_fentry,  					  bool test_fexit,  					  bool test_fentry_entry)  { -	int err, map_fd, prog_fd, main_data_fd, fentry_data_fd, fexit_data_fd, i, val; +	int err, map_fd, prog_fd, main_data_fd, fentry_data_fd = 0, fexit_data_fd = 0, i, val;  	struct bpf_object *obj = NULL, *fentry_obj = NULL, *fexit_obj = NULL;  	struct bpf_link *fentry_link = NULL, *fexit_link = NULL;  	struct bpf_program *prog, *fentry_prog; diff --git a/tools/testing/selftests/bpf/prog_tests/tc_helpers.h b/tools/testing/selftests/bpf/prog_tests/tc_helpers.h index 924d0e25320c..d52a62af77bf 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_helpers.h +++ b/tools/testing/selftests/bpf/prog_tests/tc_helpers.h @@ -8,34 +8,6 @@  # define loopback 1  #endif -static inline __u32 id_from_prog_fd(int fd) -{ -	struct bpf_prog_info prog_info = {}; -	__u32 prog_info_len = sizeof(prog_info); -	int err; - -	err = bpf_obj_get_info_by_fd(fd, &prog_info, &prog_info_len); -	if (!ASSERT_OK(err, "id_from_prog_fd")) -		return 0; - -	ASSERT_NEQ(prog_info.id, 0, "prog_info.id"); -	return prog_info.id; -} - -static inline __u32 id_from_link_fd(int fd) -{ -	struct bpf_link_info link_info = {}; -	__u32 link_info_len = sizeof(link_info); -	int err; - -	err = bpf_link_get_info_by_fd(fd, &link_info, &link_info_len); -	if (!ASSERT_OK(err, "id_from_link_fd")) -		return 0; - -	ASSERT_NEQ(link_info.id, 0, "link_info.id"); -	return link_info.id; -} -  static inline __u32 ifindex_from_link_fd(int fd)  {  	struct bpf_link_info link_info = {}; diff --git a/tools/testing/selftests/bpf/prog_tests/test_veristat.c b/tools/testing/selftests/bpf/prog_tests/test_veristat.c index 47b56c258f3f..367f47e4a936 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_veristat.c +++ b/tools/testing/selftests/bpf/prog_tests/test_veristat.c @@ -60,13 +60,19 @@ static void test_set_global_vars_succeeds(void)  	    " -G \"var_s8 = -128\" "\  	    " -G \"var_u8 = 255\" "\  	    " -G \"var_ea = EA2\" "\ -	    " -G \"var_eb = EB2\" "\ -	    " -G \"var_ec = EC2\" "\ +	    " -G \"var_eb  =  EB2\" "\ +	    " -G \"var_ec=EC2\" "\  	    " -G \"var_b = 1\" "\ -	    " -G \"struct1.struct2.u.var_u8 = 170\" "\ +	    " -G \"struct1[2].struct2[1][2].u.var_u8[2]=170\" "\  	    " -G \"union1.struct3.var_u8_l = 0xaa\" "\  	    " -G \"union1.struct3.var_u8_h = 0xaa\" "\ -	    "-vl2 > %s", fix->veristat, fix->tmpfile); +	    " -G \"arr[3]= 171\" "	\ +	    " -G \"arr[EA2] =172\" "	\ +	    " -G \"enum_arr[EC2]=EA3\" " \ +	    " -G \"three_d[31][7][EA2]=173\"" \ +	    " -G \"struct1[2].struct2[1][2].u.mat[5][3]=174\" " \ +	    " -G \"struct11 [ 7 ] [ 5 ] .struct2[0][1].u.mat[3][0] = 175\" " \ +	    " -vl2 > %s", fix->veristat, fix->tmpfile);  	read(fix->fd, fix->output, fix->sz);  	__CHECK_STR("_w=0xf000000000000001 ", "var_s64 = 0xf000000000000001"); @@ -81,8 +87,14 @@ static void test_set_global_vars_succeeds(void)  	__CHECK_STR("_w=12 ", "var_eb = EB2");  	__CHECK_STR("_w=13 ", "var_ec = EC2");  	__CHECK_STR("_w=1 ", "var_b = 1"); -	__CHECK_STR("_w=170 ", "struct1.struct2.u.var_u8 = 170"); +	__CHECK_STR("_w=170 ", "struct1[2].struct2[1][2].u.var_u8[2]=170");  	__CHECK_STR("_w=0xaaaa ", "union1.var_u16 = 0xaaaa"); +	__CHECK_STR("_w=171 ", "arr[3]= 171"); +	__CHECK_STR("_w=172 ", "arr[EA2] =172"); +	__CHECK_STR("_w=10 ", "enum_arr[EC2]=EA3"); +	__CHECK_STR("_w=173 ", "matrix[31][7][11]=173"); +	__CHECK_STR("_w=174 ", "struct1[2].struct2[1][2].u.mat[5][3]=174"); +	__CHECK_STR("_w=175 ", "struct11[7][5].struct2[0][1].u.mat[3][0]=175");  out:  	teardown_fixture(fix); @@ -129,6 +141,95 @@ out:  	teardown_fixture(fix);  } +static void test_unsupported_ptr_array_type(void) +{ +	struct fixture *fix = init_fixture(); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"ptr_arr[0] = 0\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	read(fix->fd, fix->output, fix->sz); +	__CHECK_STR("Can't set ptr_arr[0]. Only ints and enums are supported", "ptr_arr"); + +out: +	teardown_fixture(fix); +} + +static void test_array_out_of_bounds(void) +{ +	struct fixture *fix = init_fixture(); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"arr[99] = 0\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	read(fix->fd, fix->output, fix->sz); +	__CHECK_STR("Array index 99 is out of bounds", "arr[99]"); + +out: +	teardown_fixture(fix); +} + +static void test_array_index_not_found(void) +{ +	struct fixture *fix = init_fixture(); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"arr[EG2] = 0\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	read(fix->fd, fix->output, fix->sz); +	__CHECK_STR("Can't resolve enum value EG2", "arr[EG2]"); + +out: +	teardown_fixture(fix); +} + +static void test_array_index_for_non_array(void) +{ +	struct fixture *fix = init_fixture(); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"var_b[0] = 1\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	pread(fix->fd, fix->output, fix->sz, 0); +	__CHECK_STR("Array index is not expected for var_b", "var_b[0] = 1"); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"union1.struct3[0].var_u8_l=1\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	pread(fix->fd, fix->output, fix->sz, 0); +	__CHECK_STR("Array index is not expected for struct3", "union1.struct3[0].var_u8_l=1"); + +out: +	teardown_fixture(fix); +} + +static void test_no_array_index_for_array(void) +{ +	struct fixture *fix = init_fixture(); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"arr = 1\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	pread(fix->fd, fix->output, fix->sz, 0); +	__CHECK_STR("Can't set arr. Only ints and enums are supported", "arr = 1"); + +	SYS_FAIL(out, +		 "%s set_global_vars.bpf.o -G \"struct1[0].struct2.u.var_u8[2]=1\" -vl2 2> %s", +		 fix->veristat, fix->tmpfile); + +	pread(fix->fd, fix->output, fix->sz, 0); +	__CHECK_STR("Can't resolve field u for non-composite type", "struct1[0].struct2.u.var_u8[2]=1"); + +out: +	teardown_fixture(fix); +} +  void test_veristat(void)  {  	if (test__start_subtest("set_global_vars_succeeds")) @@ -139,6 +240,22 @@ void test_veristat(void)  	if (test__start_subtest("set_global_vars_from_file_succeeds"))  		test_set_global_vars_from_file_succeeds(); + +	if (test__start_subtest("test_unsupported_ptr_array_type")) +		test_unsupported_ptr_array_type(); + +	if (test__start_subtest("test_array_out_of_bounds")) +		test_array_out_of_bounds(); + +	if (test__start_subtest("test_array_index_not_found")) +		test_array_index_not_found(); + +	if (test__start_subtest("test_array_index_for_non_array")) +		test_array_index_for_non_array(); + +	if (test__start_subtest("test_no_array_index_for_array")) +		test_no_array_index_for_array(); +  }  #undef __CHECK_STR diff --git a/tools/testing/selftests/bpf/prog_tests/token.c b/tools/testing/selftests/bpf/prog_tests/token.c index f9392df23f8a..b81dde283052 100644 --- a/tools/testing/selftests/bpf/prog_tests/token.c +++ b/tools/testing/selftests/bpf/prog_tests/token.c @@ -115,7 +115,7 @@ static int create_bpffs_fd(void)  static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts)  { -	int mnt_fd, err; +	int err;  	/* set up token delegation mount options */  	err = set_delegate_mask(fs_fd, "delegate_cmds", opts->cmds, opts->cmds_str); @@ -136,12 +136,7 @@ static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts)  	if (err < 0)  		return -errno; -	/* create O_PATH fd for detached mount */ -	mnt_fd = sys_fsmount(fs_fd, 0, 0); -	if (err < 0) -		return -errno; - -	return mnt_fd; +	return 0;  }  /* send FD over Unix domain (AF_UNIX) socket */ @@ -287,6 +282,7 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba  {  	int mnt_fd = -1, fs_fd = -1, err = 0, bpffs_fd = -1, token_fd = -1;  	struct token_lsm *lsm_skel = NULL; +	char one;  	/* load and attach LSM "policy" before we go into unpriv userns */  	lsm_skel = token_lsm__open_and_load(); @@ -333,13 +329,19 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba  	err = sendfd(sock_fd, fs_fd);  	if (!ASSERT_OK(err, "send_fs_fd"))  		goto cleanup; -	zclose(fs_fd); + +	/* wait that the parent reads the fd, does the fsconfig() calls +	 * and send us a signal that it is done +	 */ +	err = read(sock_fd, &one, sizeof(one)); +	if (!ASSERT_GE(err, 0, "read_one")) +		goto cleanup;  	/* avoid mucking around with mount namespaces and mounting at -	 * well-known path, just get detach-mounted BPF FS fd back from parent +	 * well-known path, just create O_PATH fd for detached mount  	 */ -	err = recvfd(sock_fd, &mnt_fd); -	if (!ASSERT_OK(err, "recv_mnt_fd")) +	mnt_fd = sys_fsmount(fs_fd, 0, 0); +	if (!ASSERT_OK_FD(mnt_fd, "mnt_fd"))  		goto cleanup;  	/* try to fspick() BPF FS and try to add some delegation options */ @@ -429,24 +431,24 @@ again:  static void parent(int child_pid, struct bpffs_opts *bpffs_opts, int sock_fd)  { -	int fs_fd = -1, mnt_fd = -1, token_fd = -1, err; +	int fs_fd = -1, token_fd = -1, err; +	char one = 1;  	err = recvfd(sock_fd, &fs_fd);  	if (!ASSERT_OK(err, "recv_bpffs_fd"))  		goto cleanup; -	mnt_fd = materialize_bpffs_fd(fs_fd, bpffs_opts); -	if (!ASSERT_GE(mnt_fd, 0, "materialize_bpffs_fd")) { +	err = materialize_bpffs_fd(fs_fd, bpffs_opts); +	if (!ASSERT_GE(err, 0, "materialize_bpffs_fd")) {  		err = -EINVAL;  		goto cleanup;  	} -	zclose(fs_fd); -	/* pass BPF FS context object to parent */ -	err = sendfd(sock_fd, mnt_fd); -	if (!ASSERT_OK(err, "send_mnt_fd")) +	/* notify the child that we did the fsconfig() calls and it can proceed. */ +	err = write(sock_fd, &one, sizeof(one)); +	if (!ASSERT_EQ(err, sizeof(one), "send_one"))  		goto cleanup; -	zclose(mnt_fd); +	zclose(fs_fd);  	/* receive BPF token FD back from child for some extra tests */  	err = recvfd(sock_fd, &token_fd); @@ -459,7 +461,6 @@ static void parent(int child_pid, struct bpffs_opts *bpffs_opts, int sock_fd)  cleanup:  	zclose(sock_fd);  	zclose(fs_fd); -	zclose(mnt_fd);  	zclose(token_fd);  	if (child_pid > 0) @@ -1046,6 +1047,41 @@ err_out:  #define bit(n) (1ULL << (n)) +static int userns_bpf_token_info(int mnt_fd, struct token_lsm *lsm_skel) +{ +	int err, token_fd = -1; +	struct bpf_token_info info; +	u32 len = sizeof(struct bpf_token_info); + +	/* create BPF token from BPF FS mount */ +	token_fd = bpf_token_create(mnt_fd, NULL); +	if (!ASSERT_GT(token_fd, 0, "token_create")) { +		err = -EINVAL; +		goto cleanup; +	} + +	memset(&info, 0, len); +	err = bpf_obj_get_info_by_fd(token_fd, &info, &len); +	if (!ASSERT_ERR(err, "bpf_obj_get_token_info")) +		goto cleanup; +	if (!ASSERT_EQ(info.allowed_cmds, bit(BPF_MAP_CREATE), "token_info_cmds_map_create")) { +		err = -EINVAL; +		goto cleanup; +	} +	if (!ASSERT_EQ(info.allowed_progs, bit(BPF_PROG_TYPE_XDP), "token_info_progs_xdp")) { +		err = -EINVAL; +		goto cleanup; +	} + +	/* The BPF_PROG_TYPE_EXT is not set in token */ +	if (ASSERT_EQ(info.allowed_progs, bit(BPF_PROG_TYPE_EXT), "token_info_progs_ext")) +		err = -EINVAL; + +cleanup: +	zclose(token_fd); +	return err; +} +  void test_token(void)  {  	if (test__start_subtest("map_token")) { @@ -1149,4 +1185,13 @@ void test_token(void)  		subtest_userns(&opts, userns_obj_priv_implicit_token_envvar);  	} +	if (test__start_subtest("bpf_token_info")) { +		struct bpffs_opts opts = { +			.cmds = bit(BPF_MAP_CREATE), +			.progs = bit(BPF_PROG_TYPE_XDP), +			.attachs = ~0ULL, +		}; + +		subtest_userns(&opts, userns_bpf_token_info); +	}  } diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c index a222df765bc3..10e231965589 100644 --- a/tools/testing/selftests/bpf/prog_tests/tracing_failure.c +++ b/tools/testing/selftests/bpf/prog_tests/tracing_failure.c @@ -28,10 +28,62 @@ out:  	tracing_failure__destroy(skel);  } +static void test_tracing_fail_prog(const char *prog_name, const char *exp_msg) +{ +	struct tracing_failure *skel; +	struct bpf_program *prog; +	char log_buf[256]; +	int err; + +	skel = tracing_failure__open(); +	if (!ASSERT_OK_PTR(skel, "tracing_failure__open")) +		return; + +	prog = bpf_object__find_program_by_name(skel->obj, prog_name); +	if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name")) +		goto out; + +	bpf_program__set_autoload(prog, true); +	bpf_program__set_log_buf(prog, log_buf, sizeof(log_buf)); + +	err = tracing_failure__load(skel); +	if (!ASSERT_ERR(err, "tracing_failure__load")) +		goto out; + +	ASSERT_HAS_SUBSTR(log_buf, exp_msg, "log_buf"); +out: +	tracing_failure__destroy(skel); +} + +static void test_tracing_deny(void) +{ +	int btf_id; + +	/* __rcu_read_lock depends on CONFIG_PREEMPT_RCU */ +	btf_id = libbpf_find_vmlinux_btf_id("__rcu_read_lock", BPF_TRACE_FENTRY); +	if (btf_id <= 0) { +		test__skip(); +		return; +	} + +	test_tracing_fail_prog("tracing_deny", +			       "Attaching tracing programs to function '__rcu_read_lock' is rejected."); +} + +static void test_fexit_noreturns(void) +{ +	test_tracing_fail_prog("fexit_noreturns", +			       "Attaching fexit/fmod_ret to __noreturn function 'do_exit' is rejected."); +} +  void test_tracing_failure(void)  {  	if (test__start_subtest("bpf_spin_lock"))  		test_bpf_spin_lock(true);  	if (test__start_subtest("bpf_spin_unlock"))  		test_bpf_spin_lock(false); +	if (test__start_subtest("tracing_deny")) +		test_tracing_deny(); +	if (test__start_subtest("fexit_noreturns")) +		test_fexit_noreturns();  } diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c index c397336fe1ed..b17dc39a23db 100644 --- a/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_syscall.c @@ -251,7 +251,7 @@ static void test_uretprobe_syscall_call(void)  		.retprobe = true,  	);  	struct uprobe_syscall_executed *skel; -	int pid, status, err, go[2], c; +	int pid, status, err, go[2], c = 0;  	if (!ASSERT_OK(pipe(go), "pipe"))  		return; diff --git a/tools/testing/selftests/bpf/prog_tests/usdt.c b/tools/testing/selftests/bpf/prog_tests/usdt.c index 495d66414b57..9057e983cc54 100644 --- a/tools/testing/selftests/bpf/prog_tests/usdt.c +++ b/tools/testing/selftests/bpf/prog_tests/usdt.c @@ -270,8 +270,16 @@ static void subtest_multispec_usdt(void)  	 */  	trigger_300_usdts(); -	/* we'll reuse usdt_100 BPF program for usdt_300 test */  	bpf_link__destroy(skel->links.usdt_100); + +	bss->usdt_100_called = 0; +	bss->usdt_100_sum = 0; + +	/* If built with arm64/clang, there will be much less number of specs +	 * for usdt_300 call sites. +	 */ +#if !defined(__aarch64__) || !defined(__clang__) +	/* we'll reuse usdt_100 BPF program for usdt_300 test */  	skel->links.usdt_100 = bpf_program__attach_usdt(skel->progs.usdt_100, -1, "/proc/self/exe",  							"test", "usdt_300", NULL);  	err = -errno; @@ -282,13 +290,11 @@ static void subtest_multispec_usdt(void)  	/* let's check that there are no "dangling" BPF programs attached due  	 * to partial success of the above test:usdt_300 attachment  	 */ -	bss->usdt_100_called = 0; -	bss->usdt_100_sum = 0; -  	f300(777); /* this is 301st instance of usdt_300 */  	ASSERT_EQ(bss->usdt_100_called, 0, "usdt_301_called");  	ASSERT_EQ(bss->usdt_100_sum, 0, "usdt_301_sum"); +#endif  	/* This time we have USDT with 400 inlined invocations, but arg specs  	 * should be the same across all sites, so libbpf will only need to diff --git a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c index d424e7ecbd12..9fd3ae987321 100644 --- a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c @@ -21,8 +21,7 @@  #include "../progs/test_user_ringbuf.h"  static const long c_sample_size = sizeof(struct sample) + BPF_RINGBUF_HDR_SZ; -static const long c_ringbuf_size = 1 << 12; /* 1 small page */ -static const long c_max_entries = c_ringbuf_size / c_sample_size; +static long c_ringbuf_size, c_max_entries;  static void drain_current_samples(void)  { @@ -424,7 +423,9 @@ static void test_user_ringbuf_loop(void)  	uint32_t remaining_samples = total_samples;  	int err; -	BUILD_BUG_ON(total_samples <= c_max_entries); +	if (!ASSERT_LT(c_max_entries, total_samples, "compare_c_max_entries")) +		return; +  	err = load_skel_create_user_ringbuf(&skel, &ringbuf);  	if (err)  		return; @@ -686,6 +687,9 @@ void test_user_ringbuf(void)  {  	int i; +	c_ringbuf_size = getpagesize(); /* 1 page */ +	c_max_entries = c_ringbuf_size / c_sample_size; +  	for (i = 0; i < ARRAY_SIZE(success_tests); i++) {  		if (!test__start_subtest(success_tests[i].test_name))  			continue; diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c index c9da06741104..77ec95d4ffaa 100644 --- a/tools/testing/selftests/bpf/prog_tests/verifier.c +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -85,6 +85,7 @@  #include "verifier_store_release.skel.h"  #include "verifier_subprog_precision.skel.h"  #include "verifier_subreg.skel.h" +#include "verifier_tailcall.skel.h"  #include "verifier_tailcall_jit.skel.h"  #include "verifier_typedef.skel.h"  #include "verifier_uninit.skel.h" @@ -219,6 +220,7 @@ void test_verifier_stack_ptr(void)            { RUN(verifier_stack_ptr); }  void test_verifier_store_release(void)        { RUN(verifier_store_release); }  void test_verifier_subprog_precision(void)    { RUN(verifier_subprog_precision); }  void test_verifier_subreg(void)               { RUN(verifier_subreg); } +void test_verifier_tailcall(void)             { RUN(verifier_tailcall); }  void test_verifier_tailcall_jit(void)         { RUN(verifier_tailcall_jit); }  void test_verifier_typedef(void)              { RUN(verifier_typedef); }  void test_verifier_uninit(void)               { RUN(verifier_uninit); } diff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c index ab0f02faa80c..4d69d9d55e17 100644 --- a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c +++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c @@ -268,7 +268,7 @@ static void test_verify_pkcs7_sig_from_map(void)  	char *tmp_dir;  	struct test_verify_pkcs7_sig *skel = NULL;  	struct bpf_map *map; -	struct data data; +	struct data data = {};  	int ret, zero = 0;  	/* Trigger creation of session keyring. */ diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c index b2b2d85dbb1b..43264347e7d7 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_adjust_tail.c @@ -37,21 +37,26 @@ static void test_xdp_adjust_tail_shrink(void)  	bpf_object__close(obj);  } -static void test_xdp_adjust_tail_grow(void) +static void test_xdp_adjust_tail_grow(bool is_64k_pagesize)  {  	const char *file = "./test_xdp_adjust_tail_grow.bpf.o";  	struct bpf_object *obj; -	char buf[4096]; /* avoid segfault: large buf to hold grow results */ +	char buf[8192]; /* avoid segfault: large buf to hold grow results */  	__u32 expect_sz;  	int err, prog_fd;  	LIBBPF_OPTS(bpf_test_run_opts, topts,  		.data_in = &pkt_v4, -		.data_size_in = sizeof(pkt_v4),  		.data_out = buf,  		.data_size_out = sizeof(buf),  		.repeat = 1,  	); +	/* topts.data_size_in as a special signal to bpf prog */ +	if (is_64k_pagesize) +		topts.data_size_in = sizeof(pkt_v4) - 1; +	else +		topts.data_size_in = sizeof(pkt_v4); +  	err = bpf_prog_test_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd);  	if (!ASSERT_OK(err, "test_xdp_adjust_tail_grow"))  		return; @@ -208,7 +213,7 @@ out:  	bpf_object__close(obj);  } -static void test_xdp_adjust_frags_tail_grow(void) +static void test_xdp_adjust_frags_tail_grow_4k(void)  {  	const char *file = "./test_xdp_adjust_tail_grow.bpf.o";  	__u32 exp_size; @@ -246,14 +251,20 @@ static void test_xdp_adjust_frags_tail_grow(void)  	ASSERT_EQ(topts.retval, XDP_TX, "9Kb+10b retval");  	ASSERT_EQ(topts.data_size_out, exp_size, "9Kb+10b size"); -	for (i = 0; i < 9000; i++) -		ASSERT_EQ(buf[i], 1, "9Kb+10b-old"); +	for (i = 0; i < 9000; i++) { +		if (buf[i] != 1) +			ASSERT_EQ(buf[i], 1, "9Kb+10b-old"); +	} -	for (i = 9000; i < 9010; i++) -		ASSERT_EQ(buf[i], 0, "9Kb+10b-new"); +	for (i = 9000; i < 9010; i++) { +		if (buf[i] != 0) +			ASSERT_EQ(buf[i], 0, "9Kb+10b-new"); +	} -	for (i = 9010; i < 16384; i++) -		ASSERT_EQ(buf[i], 1, "9Kb+10b-untouched"); +	for (i = 9010; i < 16384; i++) { +		if (buf[i] != 1) +			ASSERT_EQ(buf[i], 1, "9Kb+10b-untouched"); +	}  	/* Test a too large grow */  	memset(buf, 1, 16384); @@ -273,16 +284,93 @@ out:  	bpf_object__close(obj);  } +static void test_xdp_adjust_frags_tail_grow_64k(void) +{ +	const char *file = "./test_xdp_adjust_tail_grow.bpf.o"; +	__u32 exp_size; +	struct bpf_program *prog; +	struct bpf_object *obj; +	int err, i, prog_fd; +	__u8 *buf; +	LIBBPF_OPTS(bpf_test_run_opts, topts); + +	obj = bpf_object__open(file); +	if (libbpf_get_error(obj)) +		return; + +	prog = bpf_object__next_program(obj, NULL); +	if (bpf_object__load(obj)) +		goto out; + +	prog_fd = bpf_program__fd(prog); + +	buf = malloc(262144); +	if (!ASSERT_OK_PTR(buf, "alloc buf 256Kb")) +		goto out; + +	/* Test case add 10 bytes to last frag */ +	memset(buf, 1, 262144); +	exp_size = 90000 + 10; + +	topts.data_in = buf; +	topts.data_out = buf; +	topts.data_size_in = 90000; +	topts.data_size_out = 262144; +	err = bpf_prog_test_run_opts(prog_fd, &topts); + +	ASSERT_OK(err, "90Kb+10b"); +	ASSERT_EQ(topts.retval, XDP_TX, "90Kb+10b retval"); +	ASSERT_EQ(topts.data_size_out, exp_size, "90Kb+10b size"); + +	for (i = 0; i < 90000; i++) { +		if (buf[i] != 1) +			ASSERT_EQ(buf[i], 1, "90Kb+10b-old"); +	} + +	for (i = 90000; i < 90010; i++) { +		if (buf[i] != 0) +			ASSERT_EQ(buf[i], 0, "90Kb+10b-new"); +	} + +	for (i = 90010; i < 262144; i++) { +		if (buf[i] != 1) +			ASSERT_EQ(buf[i], 1, "90Kb+10b-untouched"); +	} + +	/* Test a too large grow */ +	memset(buf, 1, 262144); +	exp_size = 90001; + +	topts.data_in = topts.data_out = buf; +	topts.data_size_in = 90001; +	topts.data_size_out = 262144; +	err = bpf_prog_test_run_opts(prog_fd, &topts); + +	ASSERT_OK(err, "90Kb+10b"); +	ASSERT_EQ(topts.retval, XDP_DROP, "90Kb+10b retval"); +	ASSERT_EQ(topts.data_size_out, exp_size, "90Kb+10b size"); + +	free(buf); +out: +	bpf_object__close(obj); +} +  void test_xdp_adjust_tail(void)  { +	int page_size = getpagesize(); +  	if (test__start_subtest("xdp_adjust_tail_shrink"))  		test_xdp_adjust_tail_shrink();  	if (test__start_subtest("xdp_adjust_tail_grow")) -		test_xdp_adjust_tail_grow(); +		test_xdp_adjust_tail_grow(page_size == 65536);  	if (test__start_subtest("xdp_adjust_tail_grow2"))  		test_xdp_adjust_tail_grow2();  	if (test__start_subtest("xdp_adjust_frags_tail_shrink"))  		test_xdp_adjust_frags_tail_shrink(); -	if (test__start_subtest("xdp_adjust_frags_tail_grow")) -		test_xdp_adjust_frags_tail_grow(); +	if (test__start_subtest("xdp_adjust_frags_tail_grow")) { +		if (page_size == 65536) +			test_xdp_adjust_frags_tail_grow_64k(); +		else +			test_xdp_adjust_frags_tail_grow_4k(); +	}  } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c index 7dac044664ac..dd34b0cc4b4e 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c @@ -66,16 +66,25 @@ static int attach_tc_prog(struct bpf_tc_hook *hook, int fd)  #else  #define MAX_PKT_SIZE 3408  #endif + +#define PAGE_SIZE_4K  4096 +#define PAGE_SIZE_64K 65536 +  static void test_max_pkt_size(int fd)  { -	char data[MAX_PKT_SIZE + 1] = {}; +	char data[PAGE_SIZE_64K + 1] = {};  	int err;  	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,  			    .data_in = &data, -			    .data_size_in = MAX_PKT_SIZE,  			    .flags = BPF_F_TEST_XDP_LIVE_FRAMES,  			    .repeat = 1,  		); + +	if (getpagesize() == PAGE_SIZE_64K) +		opts.data_size_in = MAX_PKT_SIZE + PAGE_SIZE_64K - PAGE_SIZE_4K; +	else +		opts.data_size_in = MAX_PKT_SIZE; +  	err = bpf_prog_test_run_opts(fd, &opts);  	ASSERT_OK(err, "prog_run_max_size"); | 
