diff options
Diffstat (limited to 'tools/testing/selftests/bpf/prog_tests')
60 files changed, 3603 insertions, 578 deletions
| diff --git a/tools/testing/selftests/bpf/prog_tests/access_variable_array.c b/tools/testing/selftests/bpf/prog_tests/access_variable_array.c new file mode 100644 index 000000000000..08131782437c --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/access_variable_array.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Bytedance */ + +#include <test_progs.h> +#include "test_access_variable_array.skel.h" + +void test_access_variable_array(void) +{ +	struct test_access_variable_array *skel; + +	skel = test_access_variable_array__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "test_access_variable_array__open_and_load")) +		return; + +	test_access_variable_array__destroy(skel); +} diff --git a/tools/testing/selftests/bpf/prog_tests/align.c b/tools/testing/selftests/bpf/prog_tests/align.c index 4666f88f2bb4..b92770592563 100644 --- a/tools/testing/selftests/bpf/prog_tests/align.c +++ b/tools/testing/selftests/bpf/prog_tests/align.c @@ -575,14 +575,14 @@ static struct bpf_align_test tests[] = {  			/* New unknown value in R7 is (4n), >= 76 */  			{14, "R7_w=scalar(umin=76,umax=1096,var_off=(0x0; 0x7fc))"},  			/* Adding it to packet pointer gives nice bounds again */ -			{16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"}, +			{16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,var_off=(0x2; 0x7fc)"},  			/* At the time the word size load is performed from R5,  			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)  			 * which is 2.  Then the variable offset is (4n+2), so  			 * the total offset is 4-byte aligned and meets the  			 * load's requirements.  			 */ -			{20, "R5=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0xfffffffc)"}, +			{20, "R5=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0x7fc)"},  		},  	},  }; @@ -660,16 +660,22 @@ static int do_test_single(struct bpf_align_test *test)  			 * func#0 @0  			 * 0: R1=ctx(off=0,imm=0) R10=fp0  			 * 0: (b7) r3 = 2                 ; R3_w=2 +			 * +			 * Sometimes it's actually two lines below, e.g. when +			 * searching for "6: R3_w=scalar(umax=255,var_off=(0x0; 0xff))": +			 *   from 4 to 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0 +			 *   6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0 +			 *   6: (71) r3 = *(u8 *)(r2 +0)           ; R2_w=pkt(off=0,r=8,imm=0) R3_w=scalar(umax=255,var_off=(0x0; 0xff))  			 */ -			if (!strstr(line_ptr, m.match)) { +			while (!strstr(line_ptr, m.match)) {  				cur_line = -1;  				line_ptr = strtok(NULL, "\n"); -				sscanf(line_ptr, "%u: ", &cur_line); +				sscanf(line_ptr ?: "", "%u: ", &cur_line); +				if (!line_ptr || cur_line != m.line) +					break;  			} -			if (cur_line != m.line || !line_ptr || -			    !strstr(line_ptr, m.match)) { -				printf("Failed to find match %u: %s\n", -				       m.line, m.match); +			if (cur_line != m.line || !line_ptr || !strstr(line_ptr, m.match)) { +				printf("Failed to find match %u: %s\n", m.line, m.match);  				ret = 1;  				printf("%s", bpf_vlog);  				break; diff --git a/tools/testing/selftests/bpf/prog_tests/attach_probe.c b/tools/testing/selftests/bpf/prog_tests/attach_probe.c index 56374c8b5436..7175af39134f 100644 --- a/tools/testing/selftests/bpf/prog_tests/attach_probe.c +++ b/tools/testing/selftests/bpf/prog_tests/attach_probe.c @@ -1,5 +1,7 @@  // SPDX-License-Identifier: GPL-2.0  #include <test_progs.h> +#include "test_attach_kprobe_sleepable.skel.h" +#include "test_attach_probe_manual.skel.h"  #include "test_attach_probe.skel.h"  /* this is how USDT semaphore is actually defined, except volatile modifier */ @@ -23,81 +25,54 @@ static noinline void trigger_func3(void)  	asm volatile ("");  } +/* attach point for ref_ctr */ +static noinline void trigger_func4(void) +{ +	asm volatile (""); +} +  static char test_data[] = "test_data"; -void test_attach_probe(void) +/* manual attach kprobe/kretprobe/uprobe/uretprobe testings */ +static void test_attach_probe_manual(enum probe_attach_mode attach_mode)  {  	DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); +	DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, kprobe_opts);  	struct bpf_link *kprobe_link, *kretprobe_link;  	struct bpf_link *uprobe_link, *uretprobe_link; -	struct test_attach_probe* skel; -	ssize_t uprobe_offset, ref_ctr_offset; -	struct bpf_link *uprobe_err_link; -	FILE *devnull; -	bool legacy; - -	/* Check if new-style kprobe/uprobe API is supported. -	 * Kernels that support new FD-based kprobe and uprobe BPF attachment -	 * through perf_event_open() syscall expose -	 * /sys/bus/event_source/devices/kprobe/type and -	 * /sys/bus/event_source/devices/uprobe/type files, respectively. They -	 * contain magic numbers that are passed as "type" field of -	 * perf_event_attr. Lack of such file in the system indicates legacy -	 * kernel with old-style kprobe/uprobe attach interface through -	 * creating per-probe event through tracefs. For such cases -	 * ref_ctr_offset feature is not supported, so we don't test it. -	 */ -	legacy = access("/sys/bus/event_source/devices/kprobe/type", F_OK) != 0; +	struct test_attach_probe_manual *skel; +	ssize_t uprobe_offset; -	uprobe_offset = get_uprobe_offset(&trigger_func); -	if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset")) +	skel = test_attach_probe_manual__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_kprobe_manual_open_and_load"))  		return; -	ref_ctr_offset = get_rel_offset((uintptr_t)&uprobe_ref_ctr); -	if (!ASSERT_GE(ref_ctr_offset, 0, "ref_ctr_offset")) -		return; - -	skel = test_attach_probe__open(); -	if (!ASSERT_OK_PTR(skel, "skel_open")) -		return; - -	/* sleepable kprobe test case needs flags set before loading */ -	if (!ASSERT_OK(bpf_program__set_flags(skel->progs.handle_kprobe_sleepable, -		BPF_F_SLEEPABLE), "kprobe_sleepable_flags")) -		goto cleanup; - -	if (!ASSERT_OK(test_attach_probe__load(skel), "skel_load")) -		goto cleanup; -	if (!ASSERT_OK_PTR(skel->bss, "check_bss")) +	uprobe_offset = get_uprobe_offset(&trigger_func); +	if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset"))  		goto cleanup;  	/* manual-attach kprobe/kretprobe */ -	kprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kprobe, -						 false /* retprobe */, -						 SYS_NANOSLEEP_KPROBE_NAME); +	kprobe_opts.attach_mode = attach_mode; +	kprobe_opts.retprobe = false; +	kprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kprobe, +						      SYS_NANOSLEEP_KPROBE_NAME, +						      &kprobe_opts);  	if (!ASSERT_OK_PTR(kprobe_link, "attach_kprobe"))  		goto cleanup;  	skel->links.handle_kprobe = kprobe_link; -	kretprobe_link = bpf_program__attach_kprobe(skel->progs.handle_kretprobe, -						    true /* retprobe */, -						    SYS_NANOSLEEP_KPROBE_NAME); +	kprobe_opts.retprobe = true; +	kretprobe_link = bpf_program__attach_kprobe_opts(skel->progs.handle_kretprobe, +							 SYS_NANOSLEEP_KPROBE_NAME, +							 &kprobe_opts);  	if (!ASSERT_OK_PTR(kretprobe_link, "attach_kretprobe"))  		goto cleanup;  	skel->links.handle_kretprobe = kretprobe_link; -	/* auto-attachable kprobe and kretprobe */ -	skel->links.handle_kprobe_auto = bpf_program__attach(skel->progs.handle_kprobe_auto); -	ASSERT_OK_PTR(skel->links.handle_kprobe_auto, "attach_kprobe_auto"); - -	skel->links.handle_kretprobe_auto = bpf_program__attach(skel->progs.handle_kretprobe_auto); -	ASSERT_OK_PTR(skel->links.handle_kretprobe_auto, "attach_kretprobe_auto"); - -	if (!legacy) -		ASSERT_EQ(uprobe_ref_ctr, 0, "uprobe_ref_ctr_before"); - +	/* manual-attach uprobe/uretprobe */ +	uprobe_opts.attach_mode = attach_mode; +	uprobe_opts.ref_ctr_offset = 0;  	uprobe_opts.retprobe = false; -	uprobe_opts.ref_ctr_offset = legacy ? 0 : ref_ctr_offset;  	uprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe,  						      0 /* self pid */,  						      "/proc/self/exe", @@ -107,12 +82,7 @@ void test_attach_probe(void)  		goto cleanup;  	skel->links.handle_uprobe = uprobe_link; -	if (!legacy) -		ASSERT_GT(uprobe_ref_ctr, 0, "uprobe_ref_ctr_after"); - -	/* if uprobe uses ref_ctr, uretprobe has to use ref_ctr as well */  	uprobe_opts.retprobe = true; -	uprobe_opts.ref_ctr_offset = legacy ? 0 : ref_ctr_offset;  	uretprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe,  							 -1 /* any pid */,  							 "/proc/self/exe", @@ -121,12 +91,7 @@ void test_attach_probe(void)  		goto cleanup;  	skel->links.handle_uretprobe = uretprobe_link; -	/* verify auto-attach fails for old-style uprobe definition */ -	uprobe_err_link = bpf_program__attach(skel->progs.handle_uprobe_byname); -	if (!ASSERT_EQ(libbpf_get_error(uprobe_err_link), -EOPNOTSUPP, -		       "auto-attach should fail for old-style name")) -		goto cleanup; - +	/* attach uprobe by function name manually */  	uprobe_opts.func_name = "trigger_func2";  	uprobe_opts.retprobe = false;  	uprobe_opts.ref_ctr_offset = 0; @@ -138,11 +103,63 @@ void test_attach_probe(void)  	if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname, "attach_uprobe_byname"))  		goto cleanup; +	/* trigger & validate kprobe && kretprobe */ +	usleep(1); + +	/* trigger & validate uprobe & uretprobe */ +	trigger_func(); + +	/* trigger & validate uprobe attached by name */ +	trigger_func2(); + +	ASSERT_EQ(skel->bss->kprobe_res, 1, "check_kprobe_res"); +	ASSERT_EQ(skel->bss->kretprobe_res, 2, "check_kretprobe_res"); +	ASSERT_EQ(skel->bss->uprobe_res, 3, "check_uprobe_res"); +	ASSERT_EQ(skel->bss->uretprobe_res, 4, "check_uretprobe_res"); +	ASSERT_EQ(skel->bss->uprobe_byname_res, 5, "check_uprobe_byname_res"); + +cleanup: +	test_attach_probe_manual__destroy(skel); +} + +static void test_attach_probe_auto(struct test_attach_probe *skel) +{ +	struct bpf_link *uprobe_err_link; + +	/* auto-attachable kprobe and kretprobe */ +	skel->links.handle_kprobe_auto = bpf_program__attach(skel->progs.handle_kprobe_auto); +	ASSERT_OK_PTR(skel->links.handle_kprobe_auto, "attach_kprobe_auto"); + +	skel->links.handle_kretprobe_auto = bpf_program__attach(skel->progs.handle_kretprobe_auto); +	ASSERT_OK_PTR(skel->links.handle_kretprobe_auto, "attach_kretprobe_auto"); + +	/* verify auto-attach fails for old-style uprobe definition */ +	uprobe_err_link = bpf_program__attach(skel->progs.handle_uprobe_byname); +	if (!ASSERT_EQ(libbpf_get_error(uprobe_err_link), -EOPNOTSUPP, +		       "auto-attach should fail for old-style name")) +		return; +  	/* verify auto-attach works */  	skel->links.handle_uretprobe_byname =  			bpf_program__attach(skel->progs.handle_uretprobe_byname);  	if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname, "attach_uretprobe_byname")) -		goto cleanup; +		return; + +	/* trigger & validate kprobe && kretprobe */ +	usleep(1); + +	/* trigger & validate uprobe attached by name */ +	trigger_func2(); + +	ASSERT_EQ(skel->bss->kprobe2_res, 11, "check_kprobe_auto_res"); +	ASSERT_EQ(skel->bss->kretprobe2_res, 22, "check_kretprobe_auto_res"); +	ASSERT_EQ(skel->bss->uretprobe_byname_res, 6, "check_uretprobe_byname_res"); +} + +static void test_uprobe_lib(struct test_attach_probe *skel) +{ +	DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); +	FILE *devnull;  	/* test attach by name for a library function, using the library  	 * as the binary argument. libc.so.6 will be resolved via dlopen()/dlinfo(). @@ -155,7 +172,7 @@ void test_attach_probe(void)  							"libc.so.6",  							0, &uprobe_opts);  	if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname2, "attach_uprobe_byname2")) -		goto cleanup; +		return;  	uprobe_opts.func_name = "fclose";  	uprobe_opts.retprobe = true; @@ -165,62 +182,144 @@ void test_attach_probe(void)  							"libc.so.6",  							0, &uprobe_opts);  	if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname2, "attach_uretprobe_byname2")) +		return; + +	/* trigger & validate shared library u[ret]probes attached by name */ +	devnull = fopen("/dev/null", "r"); +	fclose(devnull); + +	ASSERT_EQ(skel->bss->uprobe_byname2_res, 7, "check_uprobe_byname2_res"); +	ASSERT_EQ(skel->bss->uretprobe_byname2_res, 8, "check_uretprobe_byname2_res"); +} + +static void test_uprobe_ref_ctr(struct test_attach_probe *skel) +{ +	DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, uprobe_opts); +	struct bpf_link *uprobe_link, *uretprobe_link; +	ssize_t uprobe_offset, ref_ctr_offset; + +	uprobe_offset = get_uprobe_offset(&trigger_func4); +	if (!ASSERT_GE(uprobe_offset, 0, "uprobe_offset_ref_ctr")) +		return; + +	ref_ctr_offset = get_rel_offset((uintptr_t)&uprobe_ref_ctr); +	if (!ASSERT_GE(ref_ctr_offset, 0, "ref_ctr_offset")) +		return; + +	ASSERT_EQ(uprobe_ref_ctr, 0, "uprobe_ref_ctr_before"); + +	uprobe_opts.retprobe = false; +	uprobe_opts.ref_ctr_offset = ref_ctr_offset; +	uprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uprobe_ref_ctr, +						      0 /* self pid */, +						      "/proc/self/exe", +						      uprobe_offset, +						      &uprobe_opts); +	if (!ASSERT_OK_PTR(uprobe_link, "attach_uprobe_ref_ctr")) +		return; +	skel->links.handle_uprobe_ref_ctr = uprobe_link; + +	ASSERT_GT(uprobe_ref_ctr, 0, "uprobe_ref_ctr_after"); + +	/* if uprobe uses ref_ctr, uretprobe has to use ref_ctr as well */ +	uprobe_opts.retprobe = true; +	uprobe_opts.ref_ctr_offset = ref_ctr_offset; +	uretprobe_link = bpf_program__attach_uprobe_opts(skel->progs.handle_uretprobe_ref_ctr, +							 -1 /* any pid */, +							 "/proc/self/exe", +							 uprobe_offset, &uprobe_opts); +	if (!ASSERT_OK_PTR(uretprobe_link, "attach_uretprobe_ref_ctr")) +		return; +	skel->links.handle_uretprobe_ref_ctr = uretprobe_link; +} + +static void test_kprobe_sleepable(void) +{ +	struct test_attach_kprobe_sleepable *skel; + +	skel = test_attach_kprobe_sleepable__open(); +	if (!ASSERT_OK_PTR(skel, "skel_kprobe_sleepable_open")) +		return; + +	/* sleepable kprobe test case needs flags set before loading */ +	if (!ASSERT_OK(bpf_program__set_flags(skel->progs.handle_kprobe_sleepable, +		BPF_F_SLEEPABLE), "kprobe_sleepable_flags")) +		goto cleanup; + +	if (!ASSERT_OK(test_attach_kprobe_sleepable__load(skel), +		       "skel_kprobe_sleepable_load"))  		goto cleanup;  	/* sleepable kprobes should not attach successfully */  	skel->links.handle_kprobe_sleepable = bpf_program__attach(skel->progs.handle_kprobe_sleepable); -	if (!ASSERT_ERR_PTR(skel->links.handle_kprobe_sleepable, "attach_kprobe_sleepable")) -		goto cleanup; +	ASSERT_ERR_PTR(skel->links.handle_kprobe_sleepable, "attach_kprobe_sleepable"); +cleanup: +	test_attach_kprobe_sleepable__destroy(skel); +} + +static void test_uprobe_sleepable(struct test_attach_probe *skel) +{  	/* test sleepable uprobe and uretprobe variants */  	skel->links.handle_uprobe_byname3_sleepable = bpf_program__attach(skel->progs.handle_uprobe_byname3_sleepable);  	if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname3_sleepable, "attach_uprobe_byname3_sleepable")) -		goto cleanup; +		return;  	skel->links.handle_uprobe_byname3 = bpf_program__attach(skel->progs.handle_uprobe_byname3);  	if (!ASSERT_OK_PTR(skel->links.handle_uprobe_byname3, "attach_uprobe_byname3")) -		goto cleanup; +		return;  	skel->links.handle_uretprobe_byname3_sleepable = bpf_program__attach(skel->progs.handle_uretprobe_byname3_sleepable);  	if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname3_sleepable, "attach_uretprobe_byname3_sleepable")) -		goto cleanup; +		return;  	skel->links.handle_uretprobe_byname3 = bpf_program__attach(skel->progs.handle_uretprobe_byname3);  	if (!ASSERT_OK_PTR(skel->links.handle_uretprobe_byname3, "attach_uretprobe_byname3")) -		goto cleanup; +		return;  	skel->bss->user_ptr = test_data; -	/* trigger & validate kprobe && kretprobe */ -	usleep(1); - -	/* trigger & validate shared library u[ret]probes attached by name */ -	devnull = fopen("/dev/null", "r"); -	fclose(devnull); - -	/* trigger & validate uprobe & uretprobe */ -	trigger_func(); - -	/* trigger & validate uprobe attached by name */ -	trigger_func2(); -  	/* trigger & validate sleepable uprobe attached by name */  	trigger_func3(); -	ASSERT_EQ(skel->bss->kprobe_res, 1, "check_kprobe_res"); -	ASSERT_EQ(skel->bss->kprobe2_res, 11, "check_kprobe_auto_res"); -	ASSERT_EQ(skel->bss->kretprobe_res, 2, "check_kretprobe_res"); -	ASSERT_EQ(skel->bss->kretprobe2_res, 22, "check_kretprobe_auto_res"); -	ASSERT_EQ(skel->bss->uprobe_res, 3, "check_uprobe_res"); -	ASSERT_EQ(skel->bss->uretprobe_res, 4, "check_uretprobe_res"); -	ASSERT_EQ(skel->bss->uprobe_byname_res, 5, "check_uprobe_byname_res"); -	ASSERT_EQ(skel->bss->uretprobe_byname_res, 6, "check_uretprobe_byname_res"); -	ASSERT_EQ(skel->bss->uprobe_byname2_res, 7, "check_uprobe_byname2_res"); -	ASSERT_EQ(skel->bss->uretprobe_byname2_res, 8, "check_uretprobe_byname2_res");  	ASSERT_EQ(skel->bss->uprobe_byname3_sleepable_res, 9, "check_uprobe_byname3_sleepable_res");  	ASSERT_EQ(skel->bss->uprobe_byname3_res, 10, "check_uprobe_byname3_res");  	ASSERT_EQ(skel->bss->uretprobe_byname3_sleepable_res, 11, "check_uretprobe_byname3_sleepable_res");  	ASSERT_EQ(skel->bss->uretprobe_byname3_res, 12, "check_uretprobe_byname3_res"); +} + +void test_attach_probe(void) +{ +	struct test_attach_probe *skel; + +	skel = test_attach_probe__open(); +	if (!ASSERT_OK_PTR(skel, "skel_open")) +		return; + +	if (!ASSERT_OK(test_attach_probe__load(skel), "skel_load")) +		goto cleanup; +	if (!ASSERT_OK_PTR(skel->bss, "check_bss")) +		goto cleanup; + +	if (test__start_subtest("manual-default")) +		test_attach_probe_manual(PROBE_ATTACH_MODE_DEFAULT); +	if (test__start_subtest("manual-legacy")) +		test_attach_probe_manual(PROBE_ATTACH_MODE_LEGACY); +	if (test__start_subtest("manual-perf")) +		test_attach_probe_manual(PROBE_ATTACH_MODE_PERF); +	if (test__start_subtest("manual-link")) +		test_attach_probe_manual(PROBE_ATTACH_MODE_LINK); + +	if (test__start_subtest("auto")) +		test_attach_probe_auto(skel); +	if (test__start_subtest("kprobe-sleepable")) +		test_kprobe_sleepable(); +	if (test__start_subtest("uprobe-lib")) +		test_uprobe_lib(skel); +	if (test__start_subtest("uprobe-sleepable")) +		test_uprobe_sleepable(skel); +	if (test__start_subtest("uprobe-ref_ctr")) +		test_uprobe_ref_ctr(skel);  cleanup:  	test_attach_probe__destroy(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c index e980188d4124..a53c254c6058 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_tcp_ca.c @@ -8,6 +8,7 @@  #include "bpf_dctcp.skel.h"  #include "bpf_cubic.skel.h"  #include "bpf_tcp_nogpl.skel.h" +#include "tcp_ca_update.skel.h"  #include "bpf_dctcp_release.skel.h"  #include "tcp_ca_write_sk_pacing.skel.h"  #include "tcp_ca_incompl_cong_ops.skel.h" @@ -381,6 +382,155 @@ static void test_unsupp_cong_op(void)  	libbpf_set_print(old_print_fn);  } +static void test_update_ca(void) +{ +	struct tcp_ca_update *skel; +	struct bpf_link *link; +	int saved_ca1_cnt; +	int err; + +	skel = tcp_ca_update__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "open")) +		return; + +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); +	ASSERT_OK_PTR(link, "attach_struct_ops"); + +	do_test("tcp_ca_update", NULL); +	saved_ca1_cnt = skel->bss->ca1_cnt; +	ASSERT_GT(saved_ca1_cnt, 0, "ca1_ca1_cnt"); + +	err = bpf_link__update_map(link, skel->maps.ca_update_2); +	ASSERT_OK(err, "update_map"); + +	do_test("tcp_ca_update", NULL); +	ASSERT_EQ(skel->bss->ca1_cnt, saved_ca1_cnt, "ca2_ca1_cnt"); +	ASSERT_GT(skel->bss->ca2_cnt, 0, "ca2_ca2_cnt"); + +	bpf_link__destroy(link); +	tcp_ca_update__destroy(skel); +} + +static void test_update_wrong(void) +{ +	struct tcp_ca_update *skel; +	struct bpf_link *link; +	int saved_ca1_cnt; +	int err; + +	skel = tcp_ca_update__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "open")) +		return; + +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); +	ASSERT_OK_PTR(link, "attach_struct_ops"); + +	do_test("tcp_ca_update", NULL); +	saved_ca1_cnt = skel->bss->ca1_cnt; +	ASSERT_GT(saved_ca1_cnt, 0, "ca1_ca1_cnt"); + +	err = bpf_link__update_map(link, skel->maps.ca_wrong); +	ASSERT_ERR(err, "update_map"); + +	do_test("tcp_ca_update", NULL); +	ASSERT_GT(skel->bss->ca1_cnt, saved_ca1_cnt, "ca2_ca1_cnt"); + +	bpf_link__destroy(link); +	tcp_ca_update__destroy(skel); +} + +static void test_mixed_links(void) +{ +	struct tcp_ca_update *skel; +	struct bpf_link *link, *link_nl; +	int err; + +	skel = tcp_ca_update__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "open")) +		return; + +	link_nl = bpf_map__attach_struct_ops(skel->maps.ca_no_link); +	ASSERT_OK_PTR(link_nl, "attach_struct_ops_nl"); + +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); +	ASSERT_OK_PTR(link, "attach_struct_ops"); + +	do_test("tcp_ca_update", NULL); +	ASSERT_GT(skel->bss->ca1_cnt, 0, "ca1_ca1_cnt"); + +	err = bpf_link__update_map(link, skel->maps.ca_no_link); +	ASSERT_ERR(err, "update_map"); + +	bpf_link__destroy(link); +	bpf_link__destroy(link_nl); +	tcp_ca_update__destroy(skel); +} + +static void test_multi_links(void) +{ +	struct tcp_ca_update *skel; +	struct bpf_link *link; + +	skel = tcp_ca_update__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "open")) +		return; + +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); +	ASSERT_OK_PTR(link, "attach_struct_ops_1st"); +	bpf_link__destroy(link); + +	/* A map should be able to be used to create links multiple +	 * times. +	 */ +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); +	ASSERT_OK_PTR(link, "attach_struct_ops_2nd"); +	bpf_link__destroy(link); + +	tcp_ca_update__destroy(skel); +} + +static void test_link_replace(void) +{ +	DECLARE_LIBBPF_OPTS(bpf_link_update_opts, opts); +	struct tcp_ca_update *skel; +	struct bpf_link *link; +	int err; + +	skel = tcp_ca_update__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "open")) +		return; + +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_1); +	ASSERT_OK_PTR(link, "attach_struct_ops_1st"); +	bpf_link__destroy(link); + +	link = bpf_map__attach_struct_ops(skel->maps.ca_update_2); +	ASSERT_OK_PTR(link, "attach_struct_ops_2nd"); + +	/* BPF_F_REPLACE with a wrong old map Fd. It should fail! +	 * +	 * With BPF_F_REPLACE, the link should be updated only if the +	 * old map fd given here matches the map backing the link. +	 */ +	opts.old_map_fd = bpf_map__fd(skel->maps.ca_update_1); +	opts.flags = BPF_F_REPLACE; +	err = bpf_link_update(bpf_link__fd(link), +			      bpf_map__fd(skel->maps.ca_update_1), +			      &opts); +	ASSERT_ERR(err, "bpf_link_update_fail"); + +	/* BPF_F_REPLACE with a correct old map Fd. It should success! */ +	opts.old_map_fd = bpf_map__fd(skel->maps.ca_update_2); +	err = bpf_link_update(bpf_link__fd(link), +			      bpf_map__fd(skel->maps.ca_update_1), +			      &opts); +	ASSERT_OK(err, "bpf_link_update_success"); + +	bpf_link__destroy(link); + +	tcp_ca_update__destroy(skel); +} +  void test_bpf_tcp_ca(void)  {  	if (test__start_subtest("dctcp")) @@ -399,4 +549,14 @@ void test_bpf_tcp_ca(void)  		test_incompl_cong_ops();  	if (test__start_subtest("unsupp_cong_op"))  		test_unsupp_cong_op(); +	if (test__start_subtest("update_ca")) +		test_update_ca(); +	if (test__start_subtest("update_wrong")) +		test_update_wrong(); +	if (test__start_subtest("mixed_links")) +		test_mixed_links(); +	if (test__start_subtest("multi_links")) +		test_multi_links(); +	if (test__start_subtest("link_replace")) +		test_link_replace();  } diff --git a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c index 5ca252823294..731c343897d8 100644 --- a/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c +++ b/tools/testing/selftests/bpf/prog_tests/bpf_verif_scale.c @@ -144,6 +144,12 @@ void test_verif_scale_pyperf600_nounroll()  	scale_test("pyperf600_nounroll.bpf.o", BPF_PROG_TYPE_RAW_TRACEPOINT, false);  } +void test_verif_scale_pyperf600_iter() +{ +	/* open-coded BPF iterator version */ +	scale_test("pyperf600_iter.bpf.o", BPF_PROG_TYPE_RAW_TRACEPOINT, false); +} +  void test_verif_scale_loop1()  {  	scale_test("loop1.bpf.o", BPF_PROG_TYPE_RAW_TRACEPOINT, false); diff --git a/tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c b/tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c index 621c57222191..63ee892bc757 100644 --- a/tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c +++ b/tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c @@ -56,8 +56,9 @@ static bool assert_storage_noexist(struct bpf_map *map, const void *key)  static bool connect_send(const char *cgroup_path)  { -	bool res = true;  	int server_fd = -1, client_fd = -1; +	char message[] = "message"; +	bool res = true;  	if (join_cgroup(cgroup_path))  		goto out_clean; @@ -70,7 +71,10 @@ static bool connect_send(const char *cgroup_path)  	if (client_fd < 0)  		goto out_clean; -	if (send(client_fd, "message", strlen("message"), 0) < 0) +	if (send(client_fd, &message, sizeof(message), 0) < 0) +		goto out_clean; + +	if (read(server_fd, &message, sizeof(message)) < 0)  		goto out_clean;  	res = false; diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c b/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c index b3f7985c8504..adda85f97058 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_kfunc.c @@ -84,6 +84,7 @@ static const char * const success_tests[] = {  	"test_cgrp_xchg_release",  	"test_cgrp_get_release",  	"test_cgrp_get_ancestors", +	"test_cgrp_from_id",  };  void test_cgrp_kfunc(void) diff --git a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c index 2cc759956e3b..63e776f4176e 100644 --- a/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c +++ b/tools/testing/selftests/bpf/prog_tests/cgrp_local_storage.c @@ -193,7 +193,7 @@ out:  	cgrp_ls_sleepable__destroy(skel);  } -static void test_no_rcu_lock(__u64 cgroup_id) +static void test_yes_rcu_lock(__u64 cgroup_id)  {  	struct cgrp_ls_sleepable *skel;  	int err; @@ -204,7 +204,7 @@ static void test_no_rcu_lock(__u64 cgroup_id)  	skel->bss->target_pid = syscall(SYS_gettid); -	bpf_program__set_autoload(skel->progs.no_rcu_lock, true); +	bpf_program__set_autoload(skel->progs.yes_rcu_lock, true);  	err = cgrp_ls_sleepable__load(skel);  	if (!ASSERT_OK(err, "skel_load"))  		goto out; @@ -220,7 +220,7 @@ out:  	cgrp_ls_sleepable__destroy(skel);  } -static void test_rcu_lock(void) +static void test_no_rcu_lock(void)  {  	struct cgrp_ls_sleepable *skel;  	int err; @@ -229,7 +229,7 @@ static void test_rcu_lock(void)  	if (!ASSERT_OK_PTR(skel, "skel_open"))  		return; -	bpf_program__set_autoload(skel->progs.yes_rcu_lock, true); +	bpf_program__set_autoload(skel->progs.no_rcu_lock, true);  	err = cgrp_ls_sleepable__load(skel);  	ASSERT_ERR(err, "skel_load"); @@ -256,10 +256,10 @@ void test_cgrp_local_storage(void)  		test_negative();  	if (test__start_subtest("cgroup_iter_sleepable"))  		test_cgroup_iter_sleepable(cgroup_fd, cgroup_id); +	if (test__start_subtest("yes_rcu_lock")) +		test_yes_rcu_lock(cgroup_id);  	if (test__start_subtest("no_rcu_lock")) -		test_no_rcu_lock(cgroup_id); -	if (test__start_subtest("rcu_lock")) -		test_rcu_lock(); +		test_no_rcu_lock();  	close(cgroup_fd);  } diff --git a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c index 224f016b0a53..2a55f717fc07 100644 --- a/tools/testing/selftests/bpf/prog_tests/cls_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/cls_redirect.c @@ -13,6 +13,7 @@  #include "progs/test_cls_redirect.h"  #include "test_cls_redirect.skel.h" +#include "test_cls_redirect_dynptr.skel.h"  #include "test_cls_redirect_subprogs.skel.h"  #define ENCAP_IP INADDR_LOOPBACK @@ -446,6 +447,28 @@ cleanup:  	close_fds((int *)conns, sizeof(conns) / sizeof(conns[0][0]));  } +static void test_cls_redirect_dynptr(void) +{ +	struct test_cls_redirect_dynptr *skel; +	int err; + +	skel = test_cls_redirect_dynptr__open(); +	if (!ASSERT_OK_PTR(skel, "skel_open")) +		return; + +	skel->rodata->ENCAPSULATION_IP = htonl(ENCAP_IP); +	skel->rodata->ENCAPSULATION_PORT = htons(ENCAP_PORT); + +	err = test_cls_redirect_dynptr__load(skel); +	if (!ASSERT_OK(err, "skel_load")) +		goto cleanup; + +	test_cls_redirect_common(skel->progs.cls_redirect); + +cleanup: +	test_cls_redirect_dynptr__destroy(skel); +} +  static void test_cls_redirect_inlined(void)  {  	struct test_cls_redirect *skel; @@ -496,4 +519,6 @@ void test_cls_redirect(void)  		test_cls_redirect_inlined();  	if (test__start_subtest("cls_redirect_subprogs"))  		test_cls_redirect_subprogs(); +	if (test__start_subtest("cls_redirect_dynptr")) +		test_cls_redirect_dynptr();  } diff --git a/tools/testing/selftests/bpf/prog_tests/cpumask.c b/tools/testing/selftests/bpf/prog_tests/cpumask.c index 5fbe457c4ebe..cdf4acc18e4c 100644 --- a/tools/testing/selftests/bpf/prog_tests/cpumask.c +++ b/tools/testing/selftests/bpf/prog_tests/cpumask.c @@ -16,7 +16,7 @@ static const char * const cpumask_success_testcases[] = {  	"test_copy_any_anyand",  	"test_insert_leave",  	"test_insert_remove_release", -	"test_insert_kptr_get_release", +	"test_global_mask_rcu",  };  static void verify_success(const char *prog_name) diff --git a/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c b/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c new file mode 100644 index 000000000000..4951aa978f33 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/ctx_rewrite.c @@ -0,0 +1,917 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <limits.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <regex.h> +#include <test_progs.h> + +#include "bpf/btf.h" +#include "bpf_util.h" +#include "linux/filter.h" +#include "disasm.h" + +#define MAX_PROG_TEXT_SZ (32 * 1024) + +/* The code in this file serves the sole purpose of executing test cases + * specified in the test_cases array. Each test case specifies a program + * type, context field offset, and disassembly patterns that correspond + * to read and write instructions generated by + * verifier.c:convert_ctx_access() for accessing that field. + * + * For each test case, up to three programs are created: + * - One that uses BPF_LDX_MEM to read the context field. + * - One that uses BPF_STX_MEM to write to the context field. + * - One that uses BPF_ST_MEM to write to the context field. + * + * The disassembly of each program is then compared with the pattern + * specified in the test case. + */ +struct test_case { +	char *name; +	enum bpf_prog_type prog_type; +	enum bpf_attach_type expected_attach_type; +	int field_offset; +	int field_sz; +	/* Program generated for BPF_ST_MEM uses value 42 by default, +	 * this field allows to specify custom value. +	 */ +	struct { +		bool use; +		int value; +	} st_value; +	/* Pattern for BPF_LDX_MEM(field_sz, dst, ctx, field_offset) */ +	char *read; +	/* Pattern for BPF_STX_MEM(field_sz, ctx, src, field_offset) and +	 *             BPF_ST_MEM (field_sz, ctx, src, field_offset) +	 */ +	char *write; +	/* Pattern for BPF_ST_MEM(field_sz, ctx, src, field_offset), +	 * takes priority over `write`. +	 */ +	char *write_st; +	/* Pattern for BPF_STX_MEM (field_sz, ctx, src, field_offset), +	 * takes priority over `write`. +	 */ +	char *write_stx; +}; + +#define N(_prog_type, type, field, name_extra...)	\ +	.name = #_prog_type "." #field name_extra,	\ +	.prog_type = BPF_PROG_TYPE_##_prog_type,	\ +	.field_offset = offsetof(type, field),		\ +	.field_sz = sizeof(typeof(((type *)NULL)->field)) + +static struct test_case test_cases[] = { +/* Sign extension on s390 changes the pattern */ +#if defined(__x86_64__) || defined(__aarch64__) +	{ +		N(SCHED_CLS, struct __sk_buff, tstamp), +		.read  = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);" +			 "w11 &= 3;" +			 "if w11 != 0x3 goto pc+2;" +			 "$dst = 0;" +			 "goto pc+1;" +			 "$dst = *(u64 *)($ctx + sk_buff::tstamp);", +		.write = "r11 = *(u8 *)($ctx + sk_buff::__mono_tc_offset);" +			 "if w11 & 0x2 goto pc+1;" +			 "goto pc+2;" +			 "w11 &= -2;" +			 "*(u8 *)($ctx + sk_buff::__mono_tc_offset) = r11;" +			 "*(u64 *)($ctx + sk_buff::tstamp) = $src;", +	}, +#endif +	{ +		N(SCHED_CLS, struct __sk_buff, priority), +		.read  = "$dst = *(u32 *)($ctx + sk_buff::priority);", +		.write = "*(u32 *)($ctx + sk_buff::priority) = $src;", +	}, +	{ +		N(SCHED_CLS, struct __sk_buff, mark), +		.read  = "$dst = *(u32 *)($ctx + sk_buff::mark);", +		.write = "*(u32 *)($ctx + sk_buff::mark) = $src;", +	}, +	{ +		N(SCHED_CLS, struct __sk_buff, cb[0]), +		.read  = "$dst = *(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data));", +		.write = "*(u32 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::data)) = $src;", +	}, +	{ +		N(SCHED_CLS, struct __sk_buff, tc_classid), +		.read  = "$dst = *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid));", +		.write = "*(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src;", +	}, +	{ +		N(SCHED_CLS, struct __sk_buff, tc_index), +		.read  = "$dst = *(u16 *)($ctx + sk_buff::tc_index);", +		.write = "*(u16 *)($ctx + sk_buff::tc_index) = $src;", +	}, +	{ +		N(SCHED_CLS, struct __sk_buff, queue_mapping), +		.read      = "$dst = *(u16 *)($ctx + sk_buff::queue_mapping);", +		.write_stx = "if $src >= 0xffff goto pc+1;" +			     "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;", +		.write_st  = "*(u16 *)($ctx + sk_buff::queue_mapping) = $src;", +	}, +	{ +		/* This is a corner case in filter.c:bpf_convert_ctx_access() */ +		N(SCHED_CLS, struct __sk_buff, queue_mapping, ".ushrt_max"), +		.st_value = { true, USHRT_MAX }, +		.write_st = "goto pc+0;", +	}, +	{ +		N(CGROUP_SOCK, struct bpf_sock, bound_dev_if), +		.read  = "$dst = *(u32 *)($ctx + sock_common::skc_bound_dev_if);", +		.write = "*(u32 *)($ctx + sock_common::skc_bound_dev_if) = $src;", +	}, +	{ +		N(CGROUP_SOCK, struct bpf_sock, mark), +		.read  = "$dst = *(u32 *)($ctx + sock::sk_mark);", +		.write = "*(u32 *)($ctx + sock::sk_mark) = $src;", +	}, +	{ +		N(CGROUP_SOCK, struct bpf_sock, priority), +		.read  = "$dst = *(u32 *)($ctx + sock::sk_priority);", +		.write = "*(u32 *)($ctx + sock::sk_priority) = $src;", +	}, +	{ +		N(SOCK_OPS, struct bpf_sock_ops, replylong[0]), +		.read  = "$dst = *(u32 *)($ctx + bpf_sock_ops_kern::replylong);", +		.write = "*(u32 *)($ctx + bpf_sock_ops_kern::replylong) = $src;", +	}, +	{ +		N(CGROUP_SYSCTL, struct bpf_sysctl, file_pos), +#if __BYTE_ORDER == __LITTLE_ENDIAN +		.read  = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" +			 "$dst = *(u32 *)($dst +0);", +		.write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;" +			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" +			 "*(u32 *)(r9 +0) = $src;" +			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);", +#else +		.read  = "$dst = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" +			 "$dst = *(u32 *)($dst +4);", +		.write = "*(u64 *)($ctx + bpf_sysctl_kern::tmp_reg) = r9;" +			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::ppos);" +			 "*(u32 *)(r9 +4) = $src;" +			 "r9 = *(u64 *)($ctx + bpf_sysctl_kern::tmp_reg);", +#endif +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, sk), +		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::sk);", +		.expected_attach_type = BPF_CGROUP_GETSOCKOPT, +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, level), +		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::level);", +		.write = "*(u32 *)($ctx + bpf_sockopt_kern::level) = $src;", +		.expected_attach_type = BPF_CGROUP_SETSOCKOPT, +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, optname), +		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optname);", +		.write = "*(u32 *)($ctx + bpf_sockopt_kern::optname) = $src;", +		.expected_attach_type = BPF_CGROUP_SETSOCKOPT, +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, optlen), +		.read  = "$dst = *(u32 *)($ctx + bpf_sockopt_kern::optlen);", +		.write = "*(u32 *)($ctx + bpf_sockopt_kern::optlen) = $src;", +		.expected_attach_type = BPF_CGROUP_SETSOCKOPT, +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, retval), +		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::current_task);" +			 "$dst = *(u64 *)($dst + task_struct::bpf_ctx);" +			 "$dst = *(u32 *)($dst + bpf_cg_run_ctx::retval);", +		.write = "*(u64 *)($ctx + bpf_sockopt_kern::tmp_reg) = r9;" +			 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::current_task);" +			 "r9 = *(u64 *)(r9 + task_struct::bpf_ctx);" +			 "*(u32 *)(r9 + bpf_cg_run_ctx::retval) = $src;" +			 "r9 = *(u64 *)($ctx + bpf_sockopt_kern::tmp_reg);", +		.expected_attach_type = BPF_CGROUP_GETSOCKOPT, +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, optval), +		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval);", +		.expected_attach_type = BPF_CGROUP_GETSOCKOPT, +	}, +	{ +		N(CGROUP_SOCKOPT, struct bpf_sockopt, optval_end), +		.read  = "$dst = *(u64 *)($ctx + bpf_sockopt_kern::optval_end);", +		.expected_attach_type = BPF_CGROUP_GETSOCKOPT, +	}, +}; + +#undef N + +static regex_t *ident_regex; +static regex_t *field_regex; + +static char *skip_space(char *str) +{ +	while (*str && isspace(*str)) +		++str; +	return str; +} + +static char *skip_space_and_semi(char *str) +{ +	while (*str && (isspace(*str) || *str == ';')) +		++str; +	return str; +} + +static char *match_str(char *str, char *prefix) +{ +	while (*str && *prefix && *str == *prefix) { +		++str; +		++prefix; +	} +	if (*prefix) +		return NULL; +	return str; +} + +static char *match_number(char *str, int num) +{ +	char *next; +	int snum = strtol(str, &next, 10); + +	if (next - str == 0 || num != snum) +		return NULL; + +	return next; +} + +static int find_field_offset_aux(struct btf *btf, int btf_id, char *field_name, int off) +{ +	const struct btf_type *type = btf__type_by_id(btf, btf_id); +	const struct btf_member *m; +	__u16 mnum; +	int i; + +	if (!type) { +		PRINT_FAIL("Can't find btf_type for id %d\n", btf_id); +		return -1; +	} + +	if (!btf_is_struct(type) && !btf_is_union(type)) { +		PRINT_FAIL("BTF id %d is not struct or union\n", btf_id); +		return -1; +	} + +	m = btf_members(type); +	mnum = btf_vlen(type); + +	for (i = 0; i < mnum; ++i, ++m) { +		const char *mname = btf__name_by_offset(btf, m->name_off); + +		if (strcmp(mname, "") == 0) { +			int msize = find_field_offset_aux(btf, m->type, field_name, +							  off + m->offset); +			if (msize >= 0) +				return msize; +		} + +		if (strcmp(mname, field_name)) +			continue; + +		return (off + m->offset) / 8; +	} + +	return -1; +} + +static int find_field_offset(struct btf *btf, char *pattern, regmatch_t *matches) +{ +	int type_sz  = matches[1].rm_eo - matches[1].rm_so; +	int field_sz = matches[2].rm_eo - matches[2].rm_so; +	char *type   = pattern + matches[1].rm_so; +	char *field  = pattern + matches[2].rm_so; +	char field_str[128] = {}; +	char type_str[128] = {}; +	int btf_id, field_offset; + +	if (type_sz >= sizeof(type_str)) { +		PRINT_FAIL("Malformed pattern: type ident is too long: %d\n", type_sz); +		return -1; +	} + +	if (field_sz >= sizeof(field_str)) { +		PRINT_FAIL("Malformed pattern: field ident is too long: %d\n", field_sz); +		return -1; +	} + +	strncpy(type_str, type, type_sz); +	strncpy(field_str, field, field_sz); +	btf_id = btf__find_by_name(btf, type_str); +	if (btf_id < 0) { +		PRINT_FAIL("No BTF info for type %s\n", type_str); +		return -1; +	} + +	field_offset = find_field_offset_aux(btf, btf_id, field_str, 0); +	if (field_offset < 0) { +		PRINT_FAIL("No BTF info for field %s::%s\n", type_str, field_str); +		return -1; +	} + +	return field_offset; +} + +static regex_t *compile_regex(char *pat) +{ +	regex_t *re; +	int err; + +	re = malloc(sizeof(regex_t)); +	if (!re) { +		PRINT_FAIL("Can't alloc regex\n"); +		return NULL; +	} + +	err = regcomp(re, pat, REG_EXTENDED); +	if (err) { +		char errbuf[512]; + +		regerror(err, re, errbuf, sizeof(errbuf)); +		PRINT_FAIL("Can't compile regex: %s\n", errbuf); +		free(re); +		return NULL; +	} + +	return re; +} + +static void free_regex(regex_t *re) +{ +	if (!re) +		return; + +	regfree(re); +	free(re); +} + +static u32 max_line_len(char *str) +{ +	u32 max_line = 0; +	char *next = str; + +	while (next) { +		next = strchr(str, '\n'); +		if (next) { +			max_line = max_t(u32, max_line, (next - str)); +			str = next + 1; +		} else { +			max_line = max_t(u32, max_line, strlen(str)); +		} +	} + +	return min(max_line, 60u); +} + +/* Print strings `pattern_origin` and `text_origin` side by side, + * assume `pattern_pos` and `text_pos` designate location within + * corresponding origin string where match diverges. + * The output should look like: + * + *   Can't match disassembly(left) with pattern(right): + *   r2 = *(u64 *)(r1 +0)  ;  $dst = *(u64 *)($ctx + bpf_sockopt_kern::sk1) + *                     ^                             ^ + *   r0 = 0                ; + *   exit                  ; + */ +static void print_match_error(FILE *out, +			      char *pattern_origin, char *text_origin, +			      char *pattern_pos, char *text_pos) +{ +	char *pattern = pattern_origin; +	char *text = text_origin; +	int middle = max_line_len(text) + 2; + +	fprintf(out, "Can't match disassembly(left) with pattern(right):\n"); +	while (*pattern || *text) { +		int column = 0; +		int mark1 = -1; +		int mark2 = -1; + +		/* Print one line from text */ +		while (*text && *text != '\n') { +			if (text == text_pos) +				mark1 = column; +			fputc(*text, out); +			++text; +			++column; +		} +		if (text == text_pos) +			mark1 = column; + +		/* Pad to the middle */ +		while (column < middle) { +			fputc(' ', out); +			++column; +		} +		fputs(";  ", out); +		column += 3; + +		/* Print one line from pattern, pattern lines are terminated by ';' */ +		while (*pattern && *pattern != ';') { +			if (pattern == pattern_pos) +				mark2 = column; +			fputc(*pattern, out); +			++pattern; +			++column; +		} +		if (pattern == pattern_pos) +			mark2 = column; + +		fputc('\n', out); +		if (*pattern) +			++pattern; +		if (*text) +			++text; + +		/* If pattern and text diverge at this line, print an +		 * additional line with '^' marks, highlighting +		 * positions where match fails. +		 */ +		if (mark1 > 0 || mark2 > 0) { +			for (column = 0; column <= max(mark1, mark2); ++column) { +				if (column == mark1 || column == mark2) +					fputc('^', out); +				else +					fputc(' ', out); +			} +			fputc('\n', out); +		} +	} +} + +/* Test if `text` matches `pattern`. Pattern consists of the following elements: + * + * - Field offset references: + * + *     <type>::<field> + * + *   When such reference is encountered BTF is used to compute numerical + *   value for the offset of <field> in <type>. The `text` is expected to + *   contain matching numerical value. + * + * - Field groups: + * + *     $(<type>::<field> [+ <type>::<field>]*) + * + *   Allows to specify an offset that is a sum of multiple field offsets. + *   The `text` is expected to contain matching numerical value. + * + * - Variable references, e.g. `$src`, `$dst`, `$ctx`. + *   These are substitutions specified in `reg_map` array. + *   If a substring of pattern is equal to `reg_map[i][0]` the `text` is + *   expected to contain `reg_map[i][1]` in the matching position. + * + * - Whitespace is ignored, ';' counts as whitespace for `pattern`. + * + * - Any other characters, `pattern` and `text` should match one-to-one. + * + * Example of a pattern: + * + *                    __________ fields group ________________ + *                   '                                        ' + *   *(u16 *)($ctx + $(sk_buff::cb + qdisc_skb_cb::tc_classid)) = $src; + *            ^^^^                   '______________________' + *     variable reference             field offset reference + */ +static bool match_pattern(struct btf *btf, char *pattern, char *text, char *reg_map[][2]) +{ +	char *pattern_origin = pattern; +	char *text_origin = text; +	regmatch_t matches[3]; + +_continue: +	while (*pattern) { +		if (!*text) +			goto err; + +		/* Skip whitespace */ +		if (isspace(*pattern) || *pattern == ';') { +			if (!isspace(*text) && text != text_origin && isalnum(text[-1])) +				goto err; +			pattern = skip_space_and_semi(pattern); +			text = skip_space(text); +			continue; +		} + +		/* Check for variable references */ +		for (int i = 0; reg_map[i][0]; ++i) { +			char *pattern_next, *text_next; + +			pattern_next = match_str(pattern, reg_map[i][0]); +			if (!pattern_next) +				continue; + +			text_next = match_str(text, reg_map[i][1]); +			if (!text_next) +				goto err; + +			pattern = pattern_next; +			text = text_next; +			goto _continue; +		} + +		/* Match field group: +		 *   $(sk_buff::cb + qdisc_skb_cb::tc_classid) +		 */ +		if (strncmp(pattern, "$(", 2) == 0) { +			char *group_start = pattern, *text_next; +			int acc_offset = 0; + +			pattern += 2; + +			for (;;) { +				int field_offset; + +				pattern = skip_space(pattern); +				if (!*pattern) { +					PRINT_FAIL("Unexpected end of pattern\n"); +					goto err; +				} + +				if (*pattern == ')') { +					++pattern; +					break; +				} + +				if (*pattern == '+') { +					++pattern; +					continue; +				} + +				printf("pattern: %s\n", pattern); +				if (regexec(field_regex, pattern, 3, matches, 0) != 0) { +					PRINT_FAIL("Field reference expected\n"); +					goto err; +				} + +				field_offset = find_field_offset(btf, pattern, matches); +				if (field_offset < 0) +					goto err; + +				pattern += matches[0].rm_eo; +				acc_offset += field_offset; +			} + +			text_next = match_number(text, acc_offset); +			if (!text_next) { +				PRINT_FAIL("No match for group offset %.*s (%d)\n", +					   (int)(pattern - group_start), +					   group_start, +					   acc_offset); +				goto err; +			} +			text = text_next; +		} + +		/* Match field reference: +		 *   sk_buff::cb +		 */ +		if (regexec(field_regex, pattern, 3, matches, 0) == 0) { +			int field_offset; +			char *text_next; + +			field_offset = find_field_offset(btf, pattern, matches); +			if (field_offset < 0) +				goto err; + +			text_next = match_number(text, field_offset); +			if (!text_next) { +				PRINT_FAIL("No match for field offset %.*s (%d)\n", +					   (int)matches[0].rm_eo, pattern, field_offset); +				goto err; +			} + +			pattern += matches[0].rm_eo; +			text = text_next; +			continue; +		} + +		/* If pattern points to identifier not followed by '::' +		 * skip the identifier to avoid n^2 application of the +		 * field reference rule. +		 */ +		if (regexec(ident_regex, pattern, 1, matches, 0) == 0) { +			if (strncmp(pattern, text, matches[0].rm_eo) != 0) +				goto err; + +			pattern += matches[0].rm_eo; +			text += matches[0].rm_eo; +			continue; +		} + +		/* Match literally */ +		if (*pattern != *text) +			goto err; + +		++pattern; +		++text; +	} + +	return true; + +err: +	test__fail(); +	print_match_error(stdout, pattern_origin, text_origin, pattern, text); +	return false; +} + +/* Request BPF program instructions after all rewrites are applied, + * e.g. verifier.c:convert_ctx_access() is done. + */ +static int get_xlated_program(int fd_prog, struct bpf_insn **buf, __u32 *cnt) +{ +	struct bpf_prog_info info = {}; +	__u32 info_len = sizeof(info); +	__u32 xlated_prog_len; +	__u32 buf_element_size = sizeof(struct bpf_insn); + +	if (bpf_prog_get_info_by_fd(fd_prog, &info, &info_len)) { +		perror("bpf_prog_get_info_by_fd failed"); +		return -1; +	} + +	xlated_prog_len = info.xlated_prog_len; +	if (xlated_prog_len % buf_element_size) { +		printf("Program length %d is not multiple of %d\n", +		       xlated_prog_len, buf_element_size); +		return -1; +	} + +	*cnt = xlated_prog_len / buf_element_size; +	*buf = calloc(*cnt, buf_element_size); +	if (!buf) { +		perror("can't allocate xlated program buffer"); +		return -ENOMEM; +	} + +	bzero(&info, sizeof(info)); +	info.xlated_prog_len = xlated_prog_len; +	info.xlated_prog_insns = (__u64)(unsigned long)*buf; +	if (bpf_prog_get_info_by_fd(fd_prog, &info, &info_len)) { +		perror("second bpf_prog_get_info_by_fd failed"); +		goto out_free_buf; +	} + +	return 0; + +out_free_buf: +	free(*buf); +	return -1; +} + +static void print_insn(void *private_data, const char *fmt, ...) +{ +	va_list args; + +	va_start(args, fmt); +	vfprintf((FILE *)private_data, fmt, args); +	va_end(args); +} + +/* Disassemble instructions to a stream */ +static void print_xlated(FILE *out, struct bpf_insn *insn, __u32 len) +{ +	const struct bpf_insn_cbs cbs = { +		.cb_print	= print_insn, +		.cb_call	= NULL, +		.cb_imm		= NULL, +		.private_data	= out, +	}; +	bool double_insn = false; +	int i; + +	for (i = 0; i < len; i++) { +		if (double_insn) { +			double_insn = false; +			continue; +		} + +		double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW); +		print_bpf_insn(&cbs, insn + i, true); +	} +} + +/* We share code with kernel BPF disassembler, it adds '(FF) ' prefix + * for each instruction (FF stands for instruction `code` byte). + * This function removes the prefix inplace for each line in `str`. + */ +static void remove_insn_prefix(char *str, int size) +{ +	const int prefix_size = 5; + +	int write_pos = 0, read_pos = prefix_size; +	int len = strlen(str); +	char c; + +	size = min(size, len); + +	while (read_pos < size) { +		c = str[read_pos++]; +		if (c == 0) +			break; +		str[write_pos++] = c; +		if (c == '\n') +			read_pos += prefix_size; +	} +	str[write_pos] = 0; +} + +struct prog_info { +	char *prog_kind; +	enum bpf_prog_type prog_type; +	enum bpf_attach_type expected_attach_type; +	struct bpf_insn *prog; +	u32 prog_len; +}; + +static void match_program(struct btf *btf, +			  struct prog_info *pinfo, +			  char *pattern, +			  char *reg_map[][2], +			  bool skip_first_insn) +{ +	struct bpf_insn *buf = NULL; +	int err = 0, prog_fd = 0; +	FILE *prog_out = NULL; +	char *text = NULL; +	__u32 cnt = 0; + +	text = calloc(MAX_PROG_TEXT_SZ, 1); +	if (!text) { +		PRINT_FAIL("Can't allocate %d bytes\n", MAX_PROG_TEXT_SZ); +		goto out; +	} + +	// TODO: log level +	LIBBPF_OPTS(bpf_prog_load_opts, opts); +	opts.log_buf = text; +	opts.log_size = MAX_PROG_TEXT_SZ; +	opts.log_level = 1 | 2 | 4; +	opts.expected_attach_type = pinfo->expected_attach_type; + +	prog_fd = bpf_prog_load(pinfo->prog_type, NULL, "GPL", +				pinfo->prog, pinfo->prog_len, &opts); +	if (prog_fd < 0) { +		PRINT_FAIL("Can't load program, errno %d (%s), verifier log:\n%s\n", +			   errno, strerror(errno), text); +		goto out; +	} + +	memset(text, 0, MAX_PROG_TEXT_SZ); + +	err = get_xlated_program(prog_fd, &buf, &cnt); +	if (err) { +		PRINT_FAIL("Can't load back BPF program\n"); +		goto out; +	} + +	prog_out = fmemopen(text, MAX_PROG_TEXT_SZ - 1, "w"); +	if (!prog_out) { +		PRINT_FAIL("Can't open memory stream\n"); +		goto out; +	} +	if (skip_first_insn) +		print_xlated(prog_out, buf + 1, cnt - 1); +	else +		print_xlated(prog_out, buf, cnt); +	fclose(prog_out); +	remove_insn_prefix(text, MAX_PROG_TEXT_SZ); + +	ASSERT_TRUE(match_pattern(btf, pattern, text, reg_map), +		    pinfo->prog_kind); + +out: +	if (prog_fd) +		close(prog_fd); +	free(buf); +	free(text); +} + +static void run_one_testcase(struct btf *btf, struct test_case *test) +{ +	struct prog_info pinfo = {}; +	int bpf_sz; + +	if (!test__start_subtest(test->name)) +		return; + +	switch (test->field_sz) { +	case 8: +		bpf_sz = BPF_DW; +		break; +	case 4: +		bpf_sz = BPF_W; +		break; +	case 2: +		bpf_sz = BPF_H; +		break; +	case 1: +		bpf_sz = BPF_B; +		break; +	default: +		PRINT_FAIL("Unexpected field size: %d, want 8,4,2 or 1\n", test->field_sz); +		return; +	} + +	pinfo.prog_type = test->prog_type; +	pinfo.expected_attach_type = test->expected_attach_type; + +	if (test->read) { +		struct bpf_insn ldx_prog[] = { +			BPF_LDX_MEM(bpf_sz, BPF_REG_2, BPF_REG_1, test->field_offset), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}; +		char *reg_map[][2] = { +			{ "$ctx", "r1" }, +			{ "$dst", "r2" }, +			{} +		}; + +		pinfo.prog_kind = "LDX"; +		pinfo.prog = ldx_prog; +		pinfo.prog_len = ARRAY_SIZE(ldx_prog); +		match_program(btf, &pinfo, test->read, reg_map, false); +	} + +	if (test->write || test->write_st || test->write_stx) { +		struct bpf_insn stx_prog[] = { +			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_STX_MEM(bpf_sz, BPF_REG_1, BPF_REG_2, test->field_offset), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}; +		char *stx_reg_map[][2] = { +			{ "$ctx", "r1" }, +			{ "$src", "r2" }, +			{} +		}; +		struct bpf_insn st_prog[] = { +			BPF_ST_MEM(bpf_sz, BPF_REG_1, test->field_offset, +				   test->st_value.use ? test->st_value.value : 42), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}; +		char *st_reg_map[][2] = { +			{ "$ctx", "r1" }, +			{ "$src", "42" }, +			{} +		}; + +		if (test->write || test->write_stx) { +			char *pattern = test->write_stx ? test->write_stx : test->write; + +			pinfo.prog_kind = "STX"; +			pinfo.prog = stx_prog; +			pinfo.prog_len = ARRAY_SIZE(stx_prog); +			match_program(btf, &pinfo, pattern, stx_reg_map, true); +		} + +		if (test->write || test->write_st) { +			char *pattern = test->write_st ? test->write_st : test->write; + +			pinfo.prog_kind = "ST"; +			pinfo.prog = st_prog; +			pinfo.prog_len = ARRAY_SIZE(st_prog); +			match_program(btf, &pinfo, pattern, st_reg_map, false); +		} +	} + +	test__end_subtest(); +} + +void test_ctx_rewrite(void) +{ +	struct btf *btf; +	int i; + +	field_regex = compile_regex("^([[:alpha:]_][[:alnum:]_]+)::([[:alpha:]_][[:alnum:]_]+)"); +	ident_regex = compile_regex("^[[:alpha:]_][[:alnum:]_]+"); +	if (!field_regex || !ident_regex) +		return; + +	btf = btf__load_vmlinux_btf(); +	if (!btf) { +		PRINT_FAIL("Can't load vmlinux BTF, errno %d (%s)\n", errno, strerror(errno)); +		goto out; +	} + +	for (i = 0; i < ARRAY_SIZE(test_cases); ++i) +		run_one_testcase(btf, &test_cases[i]); + +out: +	btf__free(btf); +	free_regex(field_regex); +	free_regex(ident_regex); +} diff --git a/tools/testing/selftests/bpf/prog_tests/decap_sanity.c b/tools/testing/selftests/bpf/prog_tests/decap_sanity.c index 2853883b7cbb..5c0ebe6ba866 100644 --- a/tools/testing/selftests/bpf/prog_tests/decap_sanity.c +++ b/tools/testing/selftests/bpf/prog_tests/decap_sanity.c @@ -10,14 +10,6 @@  #include "network_helpers.h"  #include "decap_sanity.skel.h" -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			goto fail;				\ -	}) -  #define NS_TEST "decap_sanity_ns"  #define IPV6_IFACE_ADDR "face::1"  #define UDP_TEST_PORT 7777 @@ -37,9 +29,9 @@ void test_decap_sanity(void)  	if (!ASSERT_OK_PTR(skel, "skel open_and_load"))  		return; -	SYS("ip netns add %s", NS_TEST); -	SYS("ip -net %s -6 addr add %s/128 dev lo nodad", NS_TEST, IPV6_IFACE_ADDR); -	SYS("ip -net %s link set dev lo up", NS_TEST); +	SYS(fail, "ip netns add %s", NS_TEST); +	SYS(fail, "ip -net %s -6 addr add %s/128 dev lo nodad", NS_TEST, IPV6_IFACE_ADDR); +	SYS(fail, "ip -net %s link set dev lo up", NS_TEST);  	nstoken = open_netns(NS_TEST);  	if (!ASSERT_OK_PTR(nstoken, "open_netns")) @@ -80,6 +72,6 @@ fail:  		bpf_tc_hook_destroy(&qdisc_hook);  		close_netns(nstoken);  	} -	system("ip netns del " NS_TEST " &> /dev/null"); +	SYS_NOFAIL("ip netns del " NS_TEST " &> /dev/null");  	decap_sanity__destroy(skel);  } diff --git a/tools/testing/selftests/bpf/prog_tests/dynptr.c b/tools/testing/selftests/bpf/prog_tests/dynptr.c index b99264ec0d9c..d176c34a7d2e 100644 --- a/tools/testing/selftests/bpf/prog_tests/dynptr.c +++ b/tools/testing/selftests/bpf/prog_tests/dynptr.c @@ -2,20 +2,32 @@  /* Copyright (c) 2022 Facebook */  #include <test_progs.h> +#include <network_helpers.h>  #include "dynptr_fail.skel.h"  #include "dynptr_success.skel.h" -static const char * const success_tests[] = { -	"test_read_write", -	"test_data_slice", -	"test_ringbuf", +enum test_setup_type { +	SETUP_SYSCALL_SLEEP, +	SETUP_SKB_PROG,  }; -static void verify_success(const char *prog_name) +static struct { +	const char *prog_name; +	enum test_setup_type type; +} success_tests[] = { +	{"test_read_write", SETUP_SYSCALL_SLEEP}, +	{"test_dynptr_data", SETUP_SYSCALL_SLEEP}, +	{"test_ringbuf", SETUP_SYSCALL_SLEEP}, +	{"test_skb_readonly", SETUP_SKB_PROG}, +	{"test_dynptr_skb_data", SETUP_SKB_PROG}, +}; + +static void verify_success(const char *prog_name, enum test_setup_type setup_type)  {  	struct dynptr_success *skel;  	struct bpf_program *prog;  	struct bpf_link *link; +       int err;  	skel = dynptr_success__open();  	if (!ASSERT_OK_PTR(skel, "dynptr_success__open")) @@ -23,23 +35,53 @@ static void verify_success(const char *prog_name)  	skel->bss->pid = getpid(); -	dynptr_success__load(skel); -	if (!ASSERT_OK_PTR(skel, "dynptr_success__load")) -		goto cleanup; -  	prog = bpf_object__find_program_by_name(skel->obj, prog_name);  	if (!ASSERT_OK_PTR(prog, "bpf_object__find_program_by_name"))  		goto cleanup; -	link = bpf_program__attach(prog); -	if (!ASSERT_OK_PTR(link, "bpf_program__attach")) +       bpf_program__set_autoload(prog, true); + +	err = dynptr_success__load(skel); +	if (!ASSERT_OK(err, "dynptr_success__load"))  		goto cleanup; -	usleep(1); +	switch (setup_type) { +	case SETUP_SYSCALL_SLEEP: +		link = bpf_program__attach(prog); +		if (!ASSERT_OK_PTR(link, "bpf_program__attach")) +			goto cleanup; -	ASSERT_EQ(skel->bss->err, 0, "err"); +		usleep(1); + +		bpf_link__destroy(link); +		break; +	case SETUP_SKB_PROG: +	{ +		int prog_fd; +		char buf[64]; + +		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, +		); -	bpf_link__destroy(link); +		prog_fd = bpf_program__fd(prog); +		if (!ASSERT_GE(prog_fd, 0, "prog_fd")) +			goto cleanup; + +		err = bpf_prog_test_run_opts(prog_fd, &topts); + +		if (!ASSERT_OK(err, "test_run")) +			goto cleanup; + +		break; +	} +	} + +	ASSERT_EQ(skel->bss->err, 0, "err");  cleanup:  	dynptr_success__destroy(skel); @@ -50,10 +92,10 @@ void test_dynptr(void)  	int i;  	for (i = 0; i < ARRAY_SIZE(success_tests); i++) { -		if (!test__start_subtest(success_tests[i])) +		if (!test__start_subtest(success_tests[i].prog_name))  			continue; -		verify_success(success_tests[i]); +		verify_success(success_tests[i].prog_name, success_tests[i].type);  	}  	RUN_TESTS(dynptr_fail); diff --git a/tools/testing/selftests/bpf/prog_tests/empty_skb.c b/tools/testing/selftests/bpf/prog_tests/empty_skb.c index 32dd731e9070..3b77d8a422db 100644 --- a/tools/testing/selftests/bpf/prog_tests/empty_skb.c +++ b/tools/testing/selftests/bpf/prog_tests/empty_skb.c @@ -4,11 +4,6 @@  #include <net/if.h>  #include "empty_skb.skel.h" -#define SYS(cmd) ({ \ -	if (!ASSERT_OK(system(cmd), (cmd))) \ -		goto out; \ -}) -  void test_empty_skb(void)  {  	LIBBPF_OPTS(bpf_test_run_opts, tattr); @@ -93,18 +88,18 @@ void test_empty_skb(void)  		},  	}; -	SYS("ip netns add empty_skb"); +	SYS(out, "ip netns add empty_skb");  	tok = open_netns("empty_skb"); -	SYS("ip link add veth0 type veth peer veth1"); -	SYS("ip link set dev veth0 up"); -	SYS("ip link set dev veth1 up"); -	SYS("ip addr add 10.0.0.1/8 dev veth0"); -	SYS("ip addr add 10.0.0.2/8 dev veth1"); +	SYS(out, "ip link add veth0 type veth peer veth1"); +	SYS(out, "ip link set dev veth0 up"); +	SYS(out, "ip link set dev veth1 up"); +	SYS(out, "ip addr add 10.0.0.1/8 dev veth0"); +	SYS(out, "ip addr add 10.0.0.2/8 dev veth1");  	veth_ifindex = if_nametoindex("veth0"); -	SYS("ip link add ipip0 type ipip local 10.0.0.1 remote 10.0.0.2"); -	SYS("ip link set ipip0 up"); -	SYS("ip addr add 192.168.1.1/16 dev ipip0"); +	SYS(out, "ip link add ipip0 type ipip local 10.0.0.1 remote 10.0.0.2"); +	SYS(out, "ip link set ipip0 up"); +	SYS(out, "ip addr add 192.168.1.1/16 dev ipip0");  	ipip_ifindex = if_nametoindex("ipip0");  	bpf_obj = empty_skb__open_and_load(); @@ -142,5 +137,5 @@ out:  		empty_skb__destroy(bpf_obj);  	if (tok)  		close_netns(tok); -	system("ip netns del empty_skb"); +	SYS_NOFAIL("ip netns del empty_skb");  } diff --git a/tools/testing/selftests/bpf/prog_tests/fib_lookup.c b/tools/testing/selftests/bpf/prog_tests/fib_lookup.c index 61ccddccf485..a1e712105811 100644 --- a/tools/testing/selftests/bpf/prog_tests/fib_lookup.c +++ b/tools/testing/selftests/bpf/prog_tests/fib_lookup.c @@ -8,14 +8,6 @@  #include "network_helpers.h"  #include "fib_lookup.skel.h" -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			goto fail;				\ -	}) -  #define NS_TEST			"fib_lookup_ns"  #define IPV6_IFACE_ADDR		"face::face"  #define IPV6_NUD_FAILED_ADDR	"face::1" @@ -59,16 +51,24 @@ static int setup_netns(void)  {  	int err; -	SYS("ip link add veth1 type veth peer name veth2"); -	SYS("ip link set dev veth1 up"); +	SYS(fail, "ip link add veth1 type veth peer name veth2"); +	SYS(fail, "ip link set dev veth1 up"); + +	err = write_sysctl("/proc/sys/net/ipv4/neigh/veth1/gc_stale_time", "900"); +	if (!ASSERT_OK(err, "write_sysctl(net.ipv4.neigh.veth1.gc_stale_time)")) +		goto fail; + +	err = write_sysctl("/proc/sys/net/ipv6/neigh/veth1/gc_stale_time", "900"); +	if (!ASSERT_OK(err, "write_sysctl(net.ipv6.neigh.veth1.gc_stale_time)")) +		goto fail; -	SYS("ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR); -	SYS("ip neigh add %s dev veth1 nud failed", IPV6_NUD_FAILED_ADDR); -	SYS("ip neigh add %s dev veth1 lladdr %s nud stale", IPV6_NUD_STALE_ADDR, DMAC); +	SYS(fail, "ip addr add %s/64 dev veth1 nodad", IPV6_IFACE_ADDR); +	SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV6_NUD_FAILED_ADDR); +	SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV6_NUD_STALE_ADDR, DMAC); -	SYS("ip addr add %s/24 dev veth1 nodad", IPV4_IFACE_ADDR); -	SYS("ip neigh add %s dev veth1 nud failed", IPV4_NUD_FAILED_ADDR); -	SYS("ip neigh add %s dev veth1 lladdr %s nud stale", IPV4_NUD_STALE_ADDR, DMAC); +	SYS(fail, "ip addr add %s/24 dev veth1", IPV4_IFACE_ADDR); +	SYS(fail, "ip neigh add %s dev veth1 nud failed", IPV4_NUD_FAILED_ADDR); +	SYS(fail, "ip neigh add %s dev veth1 lladdr %s nud stale", IPV4_NUD_STALE_ADDR, DMAC);  	err = write_sysctl("/proc/sys/net/ipv4/conf/veth1/forwarding", "1");  	if (!ASSERT_OK(err, "write_sysctl(net.ipv4.conf.veth1.forwarding)")) @@ -140,7 +140,7 @@ void test_fib_lookup(void)  		return;  	prog_fd = bpf_program__fd(skel->progs.fib_lookup); -	SYS("ip netns add %s", NS_TEST); +	SYS(fail, "ip netns add %s", NS_TEST);  	nstoken = open_netns(NS_TEST);  	if (!ASSERT_OK_PTR(nstoken, "open_netns")) @@ -166,7 +166,7 @@ void test_fib_lookup(void)  		if (!ASSERT_OK(err, "bpf_prog_test_run_opts"))  			continue; -		ASSERT_EQ(tests[i].expected_ret, skel->bss->fib_lookup_ret, +		ASSERT_EQ(skel->bss->fib_lookup_ret, tests[i].expected_ret,  			  "fib_lookup_ret");  		ret = memcmp(tests[i].dmac, fib_params->dmac, sizeof(tests[i].dmac)); @@ -182,6 +182,6 @@ void test_fib_lookup(void)  fail:  	if (nstoken)  		close_netns(nstoken); -	system("ip netns del " NS_TEST " &> /dev/null"); +	SYS_NOFAIL("ip netns del " NS_TEST " &> /dev/null");  	fib_lookup__destroy(skel);  } diff --git a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c index 7acca37a3d2b..c4773173a4e4 100644 --- a/tools/testing/selftests/bpf/prog_tests/flow_dissector.c +++ b/tools/testing/selftests/bpf/prog_tests/flow_dissector.c @@ -346,6 +346,30 @@ struct test tests[] = {  		.retval = BPF_OK,  	},  	{ +		.name = "ipv6-empty-flow-label", +		.pkt.ipv6 = { +			.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), +			.iph.nexthdr = IPPROTO_TCP, +			.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), +			.iph.flow_lbl = { 0x00, 0x00, 0x00 }, +			.tcp.doff = 5, +			.tcp.source = 80, +			.tcp.dest = 8080, +		}, +		.keys = { +			.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, +			.nhoff = ETH_HLEN, +			.thoff = ETH_HLEN + sizeof(struct ipv6hdr), +			.addr_proto = ETH_P_IPV6, +			.ip_proto = IPPROTO_TCP, +			.n_proto = __bpf_constant_htons(ETH_P_IPV6), +			.sport = 80, +			.dport = 8080, +		}, +		.flags = BPF_FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL, +		.retval = BPF_OK, +	}, +	{  		.name = "ipip-encap",  		.pkt.ipip = {  			.eth.h_proto = __bpf_constant_htons(ETH_P_IP), diff --git a/tools/testing/selftests/bpf/prog_tests/get_branch_snapshot.c b/tools/testing/selftests/bpf/prog_tests/get_branch_snapshot.c index 3948da12a528..0394a1156d99 100644 --- a/tools/testing/selftests/bpf/prog_tests/get_branch_snapshot.c +++ b/tools/testing/selftests/bpf/prog_tests/get_branch_snapshot.c @@ -37,8 +37,8 @@ static int create_perf_events(void)  	/* create perf event */  	attr.size = sizeof(attr); -	attr.type = PERF_TYPE_RAW; -	attr.config = 0x1b00; +	attr.type = PERF_TYPE_HARDWARE; +	attr.config = PERF_COUNT_HW_CPU_CYCLES;  	attr.sample_type = PERF_SAMPLE_BRANCH_STACK;  	attr.branch_sample_type = PERF_SAMPLE_BRANCH_KERNEL |  		PERF_SAMPLE_BRANCH_USER | PERF_SAMPLE_BRANCH_ANY; diff --git a/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c b/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c index 5308de1ed478..2715c68301f5 100644 --- a/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/get_stackid_cannot_attach.c @@ -65,6 +65,7 @@ void test_get_stackid_cannot_attach(void)  	skel->links.oncpu = bpf_program__attach_perf_event(skel->progs.oncpu,  							   pmu_fd);  	ASSERT_OK_PTR(skel->links.oncpu, "attach_perf_event_callchain"); +	bpf_link__destroy(skel->links.oncpu);  	close(pmu_fd);  	/* add exclude_callchain_kernel, attach should fail */ diff --git a/tools/testing/selftests/bpf/prog_tests/iters.c b/tools/testing/selftests/bpf/prog_tests/iters.c new file mode 100644 index 000000000000..10804ae5ae97 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/iters.c @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include <test_progs.h> + +#include "iters.skel.h" +#include "iters_state_safety.skel.h" +#include "iters_looping.skel.h" +#include "iters_num.skel.h" +#include "iters_testmod_seq.skel.h" + +static void subtest_num_iters(void) +{ +	struct iters_num *skel; +	int err; + +	skel = iters_num__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) +		return; + +	err = iters_num__attach(skel); +	if (!ASSERT_OK(err, "skel_attach")) +		goto cleanup; + +	usleep(1); +	iters_num__detach(skel); + +#define VALIDATE_CASE(case_name)					\ +	ASSERT_EQ(skel->bss->res_##case_name,				\ +		  skel->rodata->exp_##case_name,			\ +		  #case_name) + +	VALIDATE_CASE(empty_zero); +	VALIDATE_CASE(empty_int_min); +	VALIDATE_CASE(empty_int_max); +	VALIDATE_CASE(empty_minus_one); + +	VALIDATE_CASE(simple_sum); +	VALIDATE_CASE(neg_sum); +	VALIDATE_CASE(very_neg_sum); +	VALIDATE_CASE(neg_pos_sum); + +	VALIDATE_CASE(invalid_range); +	VALIDATE_CASE(max_range); +	VALIDATE_CASE(e2big_range); + +	VALIDATE_CASE(succ_elem_cnt); +	VALIDATE_CASE(overfetched_elem_cnt); +	VALIDATE_CASE(fail_elem_cnt); + +#undef VALIDATE_CASE + +cleanup: +	iters_num__destroy(skel); +} + +static void subtest_testmod_seq_iters(void) +{ +	struct iters_testmod_seq *skel; +	int err; + +	if (!env.has_testmod) { +		test__skip(); +		return; +	} + +	skel = iters_testmod_seq__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) +		return; + +	err = iters_testmod_seq__attach(skel); +	if (!ASSERT_OK(err, "skel_attach")) +		goto cleanup; + +	usleep(1); +	iters_testmod_seq__detach(skel); + +#define VALIDATE_CASE(case_name)					\ +	ASSERT_EQ(skel->bss->res_##case_name,				\ +		  skel->rodata->exp_##case_name,			\ +		  #case_name) + +	VALIDATE_CASE(empty); +	VALIDATE_CASE(full); +	VALIDATE_CASE(truncated); + +#undef VALIDATE_CASE + +cleanup: +	iters_testmod_seq__destroy(skel); +} + +void test_iters(void) +{ +	RUN_TESTS(iters_state_safety); +	RUN_TESTS(iters_looping); +	RUN_TESTS(iters); + +	if (env.has_testmod) +		RUN_TESTS(iters_testmod_seq); + +	if (test__start_subtest("num")) +		subtest_num_iters(); +	if (test__start_subtest("testmod_seq")) +		subtest_testmod_seq_iters(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c index 113dba349a57..2173c4bb555e 100644 --- a/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c +++ b/tools/testing/selftests/bpf/prog_tests/kprobe_multi_test.c @@ -338,7 +338,12 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)  	 * Filtering out duplicates by using hashmap__add, which won't  	 * add existing entry.  	 */ -	f = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r"); + +	if (access("/sys/kernel/tracing/trace", F_OK) == 0) +		f = fopen("/sys/kernel/tracing/available_filter_functions", "r"); +	else +		f = fopen("/sys/kernel/debug/tracing/available_filter_functions", "r"); +  	if (!f)  		return -EINVAL; @@ -376,8 +381,10 @@ static int get_syms(char ***symsp, size_t *cntp, bool kernel)  			continue;  		err = hashmap__add(map, name, 0); -		if (err == -EEXIST) +		if (err == -EEXIST) { +			err = 0;  			continue; +		}  		if (err)  			goto error; diff --git a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c index 9c1a18573ffd..1eab286b14fe 100644 --- a/tools/testing/selftests/bpf/prog_tests/l4lb_all.c +++ b/tools/testing/selftests/bpf/prog_tests/l4lb_all.c @@ -93,4 +93,6 @@ void test_l4lb_all(void)  		test_l4lb("test_l4lb.bpf.o");  	if (test__start_subtest("l4lb_noinline"))  		test_l4lb("test_l4lb_noinline.bpf.o"); +	if (test__start_subtest("l4lb_noinline_dynptr")) +		test_l4lb("test_l4lb_noinline_dynptr.bpf.o");  } diff --git a/tools/testing/selftests/bpf/prog_tests/linked_list.c b/tools/testing/selftests/bpf/prog_tests/linked_list.c index 0ed8132ce1c3..f63309fd0e28 100644 --- a/tools/testing/selftests/bpf/prog_tests/linked_list.c +++ b/tools/testing/selftests/bpf/prog_tests/linked_list.c @@ -84,11 +84,11 @@ static struct {  	{ "double_push_back", "arg#1 expected pointer to allocated object" },  	{ "no_node_value_type", "bpf_list_node not found at offset=0" },  	{ "incorrect_value_type", -	  "operation on bpf_list_head expects arg#1 bpf_list_node at offset=0 in struct foo, " +	  "operation on bpf_list_head expects arg#1 bpf_list_node at offset=40 in struct foo, "  	  "but arg is at offset=0 in struct bar" },  	{ "incorrect_node_var_off", "variable ptr_ access var_off=(0x0; 0xffffffff) disallowed" }, -	{ "incorrect_node_off1", "bpf_list_node not found at offset=1" }, -	{ "incorrect_node_off2", "arg#1 offset=40, but expected bpf_list_node at offset=0 in struct foo" }, +	{ "incorrect_node_off1", "bpf_list_node not found at offset=41" }, +	{ "incorrect_node_off2", "arg#1 offset=0, but expected bpf_list_node at offset=40 in struct foo" },  	{ "no_head_type", "bpf_list_head not found at offset=0" },  	{ "incorrect_head_var_off1", "R1 doesn't have constant offset" },  	{ "incorrect_head_var_off2", "variable ptr_ access var_off=(0x0; 0xffffffff) disallowed" }, @@ -266,6 +266,59 @@ end:  	return NULL;  } +static void list_and_rb_node_same_struct(bool refcount_field) +{ +	int bpf_rb_node_btf_id, bpf_refcount_btf_id, foo_btf_id; +	struct btf *btf; +	int id, err; + +	btf = init_btf(); +	if (!ASSERT_OK_PTR(btf, "init_btf")) +		return; + +	bpf_rb_node_btf_id = btf__add_struct(btf, "bpf_rb_node", 24); +	if (!ASSERT_GT(bpf_rb_node_btf_id, 0, "btf__add_struct bpf_rb_node")) +		return; + +	if (refcount_field) { +		bpf_refcount_btf_id = btf__add_struct(btf, "bpf_refcount", 4); +		if (!ASSERT_GT(bpf_refcount_btf_id, 0, "btf__add_struct bpf_refcount")) +			return; +	} + +	id = btf__add_struct(btf, "bar", refcount_field ? 44 : 40); +	if (!ASSERT_GT(id, 0, "btf__add_struct bar")) +		return; +	err = btf__add_field(btf, "a", LIST_NODE, 0, 0); +	if (!ASSERT_OK(err, "btf__add_field bar::a")) +		return; +	err = btf__add_field(btf, "c", bpf_rb_node_btf_id, 128, 0); +	if (!ASSERT_OK(err, "btf__add_field bar::c")) +		return; +	if (refcount_field) { +		err = btf__add_field(btf, "ref", bpf_refcount_btf_id, 320, 0); +		if (!ASSERT_OK(err, "btf__add_field bar::ref")) +			return; +	} + +	foo_btf_id = btf__add_struct(btf, "foo", 20); +	if (!ASSERT_GT(foo_btf_id, 0, "btf__add_struct foo")) +		return; +	err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); +	if (!ASSERT_OK(err, "btf__add_field foo::a")) +		return; +	err = btf__add_field(btf, "b", SPIN_LOCK, 128, 0); +	if (!ASSERT_OK(err, "btf__add_field foo::b")) +		return; +	id = btf__add_decl_tag(btf, "contains:bar:a", foo_btf_id, 0); +	if (!ASSERT_GT(id, 0, "btf__add_decl_tag contains:bar:a")) +		return; + +	err = btf__load_into_kernel(btf); +	ASSERT_EQ(err, refcount_field ? 0 : -EINVAL, "check btf"); +	btf__free(btf); +} +  static void test_btf(void)  {  	struct btf *btf = NULL; @@ -717,39 +770,12 @@ static void test_btf(void)  	}  	while (test__start_subtest("btf: list_node and rb_node in same struct")) { -		btf = init_btf(); -		if (!ASSERT_OK_PTR(btf, "init_btf")) -			break; - -		id = btf__add_struct(btf, "bpf_rb_node", 24); -		if (!ASSERT_EQ(id, 5, "btf__add_struct bpf_rb_node")) -			break; -		id = btf__add_struct(btf, "bar", 40); -		if (!ASSERT_EQ(id, 6, "btf__add_struct bar")) -			break; -		err = btf__add_field(btf, "a", LIST_NODE, 0, 0); -		if (!ASSERT_OK(err, "btf__add_field bar::a")) -			break; -		err = btf__add_field(btf, "c", 5, 128, 0); -		if (!ASSERT_OK(err, "btf__add_field bar::c")) -			break; - -		id = btf__add_struct(btf, "foo", 20); -		if (!ASSERT_EQ(id, 7, "btf__add_struct foo")) -			break; -		err = btf__add_field(btf, "a", LIST_HEAD, 0, 0); -		if (!ASSERT_OK(err, "btf__add_field foo::a")) -			break; -		err = btf__add_field(btf, "b", SPIN_LOCK, 128, 0); -		if (!ASSERT_OK(err, "btf__add_field foo::b")) -			break; -		id = btf__add_decl_tag(btf, "contains:bar:a", 7, 0); -		if (!ASSERT_EQ(id, 8, "btf__add_decl_tag contains:bar:a")) -			break; +		list_and_rb_node_same_struct(true); +		break; +	} -		err = btf__load_into_kernel(btf); -		ASSERT_EQ(err, -EINVAL, "check btf"); -		btf__free(btf); +	while (test__start_subtest("btf: list_node and rb_node in same struct, no bpf_refcount")) { +		list_and_rb_node_same_struct(false);  		break;  	}  } diff --git a/tools/testing/selftests/bpf/prog_tests/local_kptr_stash.c b/tools/testing/selftests/bpf/prog_tests/local_kptr_stash.c new file mode 100644 index 000000000000..76f1da877f81 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/local_kptr_stash.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include <test_progs.h> +#include <network_helpers.h> + +#include "local_kptr_stash.skel.h" +static void test_local_kptr_stash_simple(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts, +		    .data_in = &pkt_v4, +		    .data_size_in = sizeof(pkt_v4), +		    .repeat = 1, +	); +	struct local_kptr_stash *skel; +	int ret; + +	skel = local_kptr_stash__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "local_kptr_stash__open_and_load")) +		return; + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.stash_rb_nodes), &opts); +	ASSERT_OK(ret, "local_kptr_stash_add_nodes run"); +	ASSERT_OK(opts.retval, "local_kptr_stash_add_nodes retval"); + +	local_kptr_stash__destroy(skel); +} + +static void test_local_kptr_stash_unstash(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts, +		    .data_in = &pkt_v4, +		    .data_size_in = sizeof(pkt_v4), +		    .repeat = 1, +	); +	struct local_kptr_stash *skel; +	int ret; + +	skel = local_kptr_stash__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "local_kptr_stash__open_and_load")) +		return; + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.stash_rb_nodes), &opts); +	ASSERT_OK(ret, "local_kptr_stash_add_nodes run"); +	ASSERT_OK(opts.retval, "local_kptr_stash_add_nodes retval"); + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.unstash_rb_node), &opts); +	ASSERT_OK(ret, "local_kptr_stash_add_nodes run"); +	ASSERT_EQ(opts.retval, 42, "local_kptr_stash_add_nodes retval"); + +	local_kptr_stash__destroy(skel); +} + +void test_local_kptr_stash_success(void) +{ +	if (test__start_subtest("local_kptr_stash_simple")) +		test_local_kptr_stash_simple(); +	if (test__start_subtest("local_kptr_stash_unstash")) +		test_local_kptr_stash_unstash(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/log_fixup.c b/tools/testing/selftests/bpf/prog_tests/log_fixup.c index f4ffdcabf4e4..dba71d98a227 100644 --- a/tools/testing/selftests/bpf/prog_tests/log_fixup.c +++ b/tools/testing/selftests/bpf/prog_tests/log_fixup.c @@ -24,6 +24,7 @@ static void bad_core_relo(size_t log_buf_size, enum trunc_type trunc_type)  	bpf_program__set_autoload(skel->progs.bad_relo, true);  	memset(log_buf, 0, sizeof(log_buf));  	bpf_program__set_log_buf(skel->progs.bad_relo, log_buf, log_buf_size ?: sizeof(log_buf)); +	bpf_program__set_log_level(skel->progs.bad_relo, 1 | 8); /* BPF_LOG_FIXED to force truncation */  	err = test_log_fixup__load(skel);  	if (!ASSERT_ERR(err, "load_fail")) @@ -134,6 +135,35 @@ cleanup:  	test_log_fixup__destroy(skel);  } +static void missing_kfunc(void) +{ +	char log_buf[8 * 1024]; +	struct test_log_fixup* skel; +	int err; + +	skel = test_log_fixup__open(); +	if (!ASSERT_OK_PTR(skel, "skel_open")) +		return; + +	bpf_program__set_autoload(skel->progs.use_missing_kfunc, true); +	bpf_program__set_log_buf(skel->progs.use_missing_kfunc, log_buf, sizeof(log_buf)); + +	err = test_log_fixup__load(skel); +	if (!ASSERT_ERR(err, "load_fail")) +		goto cleanup; + +	ASSERT_HAS_SUBSTR(log_buf, +			  "0: <invalid kfunc call>\n" +			  "kfunc 'bpf_nonexistent_kfunc' is referenced but wasn't resolved\n", +			  "log_buf"); + +	if (env.verbosity > VERBOSE_NONE) +		printf("LOG:   \n=================\n%s=================\n", log_buf); + +cleanup: +	test_log_fixup__destroy(skel); +} +  void test_log_fixup(void)  {  	if (test__start_subtest("bad_core_relo_trunc_none")) @@ -141,9 +171,11 @@ void test_log_fixup(void)  	if (test__start_subtest("bad_core_relo_trunc_partial"))  		bad_core_relo(300, TRUNC_PARTIAL /* truncate original log a bit */);  	if (test__start_subtest("bad_core_relo_trunc_full")) -		bad_core_relo(250, TRUNC_FULL  /* truncate also libbpf's message patch */); +		bad_core_relo(210, TRUNC_FULL  /* truncate also libbpf's message patch */);  	if (test__start_subtest("bad_core_relo_subprog"))  		bad_core_relo_subprog();  	if (test__start_subtest("missing_map"))  		missing_map(); +	if (test__start_subtest("missing_kfunc")) +		missing_kfunc();  } diff --git a/tools/testing/selftests/bpf/prog_tests/map_kptr.c b/tools/testing/selftests/bpf/prog_tests/map_kptr.c index 3533a4ecad01..8743df599567 100644 --- a/tools/testing/selftests/bpf/prog_tests/map_kptr.c +++ b/tools/testing/selftests/bpf/prog_tests/map_kptr.c @@ -4,70 +4,160 @@  #include "map_kptr.skel.h"  #include "map_kptr_fail.skel.h" +#include "rcu_tasks_trace_gp.skel.h"  static void test_map_kptr_success(bool test_run)  { +	LIBBPF_OPTS(bpf_test_run_opts, lopts);  	LIBBPF_OPTS(bpf_test_run_opts, opts,  		.data_in = &pkt_v4,  		.data_size_in = sizeof(pkt_v4),  		.repeat = 1,  	); +	int key = 0, ret, cpu;  	struct map_kptr *skel; -	int key = 0, ret; -	char buf[16]; +	char buf[16], *pbuf;  	skel = map_kptr__open_and_load();  	if (!ASSERT_OK_PTR(skel, "map_kptr__open_and_load"))  		return; -	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref), &opts); -	ASSERT_OK(ret, "test_map_kptr_ref refcount"); -	ASSERT_OK(opts.retval, "test_map_kptr_ref retval"); +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref1), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref1 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref1 retval");  	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref2), &opts);  	ASSERT_OK(ret, "test_map_kptr_ref2 refcount");  	ASSERT_OK(opts.retval, "test_map_kptr_ref2 retval"); +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_ls_map_kptr_ref1), &lopts); +	ASSERT_OK(ret, "test_ls_map_kptr_ref1 refcount"); +	ASSERT_OK(lopts.retval, "test_ls_map_kptr_ref1 retval"); + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_ls_map_kptr_ref2), &lopts); +	ASSERT_OK(ret, "test_ls_map_kptr_ref2 refcount"); +	ASSERT_OK(lopts.retval, "test_ls_map_kptr_ref2 retval"); +  	if (test_run)  		goto exit; +	cpu = libbpf_num_possible_cpus(); +	if (!ASSERT_GT(cpu, 0, "libbpf_num_possible_cpus")) +		goto exit; + +	pbuf = calloc(cpu, sizeof(buf)); +	if (!ASSERT_OK_PTR(pbuf, "calloc(pbuf)")) +		goto exit; +  	ret = bpf_map__update_elem(skel->maps.array_map,  				   &key, sizeof(key), buf, sizeof(buf), 0);  	ASSERT_OK(ret, "array_map update"); -	ret = bpf_map__update_elem(skel->maps.array_map, -				   &key, sizeof(key), buf, sizeof(buf), 0); -	ASSERT_OK(ret, "array_map update2"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); + +	ret = bpf_map__update_elem(skel->maps.pcpu_array_map, +				   &key, sizeof(key), pbuf, cpu * sizeof(buf), 0); +	ASSERT_OK(ret, "pcpu_array_map update"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); -	ret = bpf_map__update_elem(skel->maps.hash_map, -				   &key, sizeof(key), buf, sizeof(buf), 0); -	ASSERT_OK(ret, "hash_map update");  	ret = bpf_map__delete_elem(skel->maps.hash_map, &key, sizeof(key), 0);  	ASSERT_OK(ret, "hash_map delete"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); + +	ret = bpf_map__delete_elem(skel->maps.pcpu_hash_map, &key, sizeof(key), 0); +	ASSERT_OK(ret, "pcpu_hash_map delete"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); -	ret = bpf_map__update_elem(skel->maps.hash_malloc_map, -				   &key, sizeof(key), buf, sizeof(buf), 0); -	ASSERT_OK(ret, "hash_malloc_map update");  	ret = bpf_map__delete_elem(skel->maps.hash_malloc_map, &key, sizeof(key), 0);  	ASSERT_OK(ret, "hash_malloc_map delete"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); + +	ret = bpf_map__delete_elem(skel->maps.pcpu_hash_malloc_map, &key, sizeof(key), 0); +	ASSERT_OK(ret, "pcpu_hash_malloc_map delete"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); -	ret = bpf_map__update_elem(skel->maps.lru_hash_map, -				   &key, sizeof(key), buf, sizeof(buf), 0); -	ASSERT_OK(ret, "lru_hash_map update");  	ret = bpf_map__delete_elem(skel->maps.lru_hash_map, &key, sizeof(key), 0);  	ASSERT_OK(ret, "lru_hash_map delete"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); + +	ret = bpf_map__delete_elem(skel->maps.lru_pcpu_hash_map, &key, sizeof(key), 0); +	ASSERT_OK(ret, "lru_pcpu_hash_map delete"); +	skel->data->ref--; +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_map_kptr_ref3), &opts); +	ASSERT_OK(ret, "test_map_kptr_ref3 refcount"); +	ASSERT_OK(opts.retval, "test_map_kptr_ref3 retval"); +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.test_ls_map_kptr_ref_del), &lopts); +	ASSERT_OK(ret, "test_ls_map_kptr_ref_del delete"); +	skel->data->ref--; +	ASSERT_OK(lopts.retval, "test_ls_map_kptr_ref_del retval"); + +	free(pbuf);  exit:  	map_kptr__destroy(skel);  } -void test_map_kptr(void) +static int kern_sync_rcu_tasks_trace(struct rcu_tasks_trace_gp *rcu)  { -	if (test__start_subtest("success")) { +	long gp_seq = READ_ONCE(rcu->bss->gp_seq); +	LIBBPF_OPTS(bpf_test_run_opts, opts); + +	if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(rcu->progs.do_call_rcu_tasks_trace), +					      &opts), "do_call_rcu_tasks_trace")) +		return -EFAULT; +	if (!ASSERT_OK(opts.retval, "opts.retval == 0")) +		return -EFAULT; +	while (gp_seq == READ_ONCE(rcu->bss->gp_seq)) +		sched_yield(); +	return 0; +} + +void serial_test_map_kptr(void) +{ +	struct rcu_tasks_trace_gp *skel; + +	RUN_TESTS(map_kptr_fail); + +	skel = rcu_tasks_trace_gp__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "rcu_tasks_trace_gp__open_and_load")) +		return; +	if (!ASSERT_OK(rcu_tasks_trace_gp__attach(skel), "rcu_tasks_trace_gp__attach")) +		goto end; + +	if (test__start_subtest("success-map")) { +		test_map_kptr_success(true); + +		ASSERT_OK(kern_sync_rcu_tasks_trace(skel), "sync rcu_tasks_trace"); +		ASSERT_OK(kern_sync_rcu(), "sync rcu"); +		/* Observe refcount dropping to 1 on bpf_map_free_deferred */  		test_map_kptr_success(false); -		/* Do test_run twice, so that we see refcount going back to 1 -		 * after we leave it in map from first iteration. -		 */ + +		ASSERT_OK(kern_sync_rcu_tasks_trace(skel), "sync rcu_tasks_trace"); +		ASSERT_OK(kern_sync_rcu(), "sync rcu"); +		/* Observe refcount dropping to 1 on synchronous delete elem */  		test_map_kptr_success(true);  	} -	RUN_TESTS(map_kptr_fail); +end: +	rcu_tasks_trace_gp__destroy(skel); +	return;  } diff --git a/tools/testing/selftests/bpf/prog_tests/map_ops.c b/tools/testing/selftests/bpf/prog_tests/map_ops.c new file mode 100644 index 000000000000..be5e42a413b4 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/map_ops.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include <errno.h> +#include <sys/syscall.h> +#include <unistd.h> + +#include "test_map_ops.skel.h" +#include "test_progs.h" + +static void map_update(void) +{ +	(void)syscall(__NR_getpid); +} + +static void map_delete(void) +{ +	(void)syscall(__NR_getppid); +} + +static void map_push(void) +{ +	(void)syscall(__NR_getuid); +} + +static void map_pop(void) +{ +	(void)syscall(__NR_geteuid); +} + +static void map_peek(void) +{ +	(void)syscall(__NR_getgid); +} + +static void map_for_each_pass(void) +{ +	(void)syscall(__NR_gettid); +} + +static void map_for_each_fail(void) +{ +	(void)syscall(__NR_getpgid); +} + +static int setup(struct test_map_ops **skel) +{ +	int err = 0; + +	if (!skel) +		return -1; + +	*skel = test_map_ops__open(); +	if (!ASSERT_OK_PTR(*skel, "test_map_ops__open")) +		return -1; + +	(*skel)->rodata->pid = getpid(); + +	err = test_map_ops__load(*skel); +	if (!ASSERT_OK(err, "test_map_ops__load")) +		return err; + +	err = test_map_ops__attach(*skel); +	if (!ASSERT_OK(err, "test_map_ops__attach")) +		return err; + +	return err; +} + +static void teardown(struct test_map_ops **skel) +{ +	if (skel && *skel) +		test_map_ops__destroy(*skel); +} + +static void map_ops_update_delete_subtest(void) +{ +	struct test_map_ops *skel; + +	if (setup(&skel)) +		goto teardown; + +	map_update(); +	ASSERT_OK(skel->bss->err, "map_update_initial"); + +	map_update(); +	ASSERT_LT(skel->bss->err, 0, "map_update_existing"); +	ASSERT_EQ(skel->bss->err, -EEXIST, "map_update_existing"); + +	map_delete(); +	ASSERT_OK(skel->bss->err, "map_delete_existing"); + +	map_delete(); +	ASSERT_LT(skel->bss->err, 0, "map_delete_non_existing"); +	ASSERT_EQ(skel->bss->err, -ENOENT, "map_delete_non_existing"); + +teardown: +	teardown(&skel); +} + +static void map_ops_push_peek_pop_subtest(void) +{ +	struct test_map_ops *skel; + +	if (setup(&skel)) +		goto teardown; + +	map_push(); +	ASSERT_OK(skel->bss->err, "map_push_initial"); + +	map_push(); +	ASSERT_LT(skel->bss->err, 0, "map_push_when_full"); +	ASSERT_EQ(skel->bss->err, -E2BIG, "map_push_when_full"); + +	map_peek(); +	ASSERT_OK(skel->bss->err, "map_peek"); + +	map_pop(); +	ASSERT_OK(skel->bss->err, "map_pop"); + +	map_peek(); +	ASSERT_LT(skel->bss->err, 0, "map_peek_when_empty"); +	ASSERT_EQ(skel->bss->err, -ENOENT, "map_peek_when_empty"); + +	map_pop(); +	ASSERT_LT(skel->bss->err, 0, "map_pop_when_empty"); +	ASSERT_EQ(skel->bss->err, -ENOENT, "map_pop_when_empty"); + +teardown: +	teardown(&skel); +} + +static void map_ops_for_each_subtest(void) +{ +	struct test_map_ops *skel; + +	if (setup(&skel)) +		goto teardown; + +	map_for_each_pass(); +	/* expect to iterate over 1 element */ +	ASSERT_EQ(skel->bss->err, 1, "map_for_each_no_flags"); + +	map_for_each_fail(); +	ASSERT_LT(skel->bss->err, 0, "map_for_each_with_flags"); +	ASSERT_EQ(skel->bss->err, -EINVAL, "map_for_each_with_flags"); + +teardown: +	teardown(&skel); +} + +void test_map_ops(void) +{ +	if (test__start_subtest("map_ops_update_delete")) +		map_ops_update_delete_subtest(); + +	if (test__start_subtest("map_ops_push_peek_pop")) +		map_ops_push_peek_pop_subtest(); + +	if (test__start_subtest("map_ops_for_each")) +		map_ops_for_each_subtest(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c b/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c new file mode 100644 index 000000000000..c7636e18b1eb --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/module_fentry_shadow.c @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2022 Red Hat */ +#include <test_progs.h> +#include <bpf/btf.h> +#include "bpf/libbpf_internal.h" +#include "cgroup_helpers.h" + +static const char *module_name = "bpf_testmod"; +static const char *symbol_name = "bpf_fentry_shadow_test"; + +static int get_bpf_testmod_btf_fd(void) +{ +	struct bpf_btf_info info; +	char name[64]; +	__u32 id = 0, len; +	int err, fd; + +	while (true) { +		err = bpf_btf_get_next_id(id, &id); +		if (err) { +			log_err("failed to iterate BTF objects"); +			return err; +		} + +		fd = bpf_btf_get_fd_by_id(id); +		if (fd < 0) { +			if (errno == ENOENT) +				continue; /* expected race: BTF was unloaded */ +			err = -errno; +			log_err("failed to get FD for BTF object #%d", id); +			return err; +		} + +		len = sizeof(info); +		memset(&info, 0, sizeof(info)); +		info.name = ptr_to_u64(name); +		info.name_len = sizeof(name); + +		err = bpf_obj_get_info_by_fd(fd, &info, &len); +		if (err) { +			err = -errno; +			log_err("failed to get info for BTF object #%d", id); +			close(fd); +			return err; +		} + +		if (strcmp(name, module_name) == 0) +			return fd; + +		close(fd); +	} +	return -ENOENT; +} + +void test_module_fentry_shadow(void) +{ +	struct btf *vmlinux_btf = NULL, *mod_btf = NULL; +	int err, i; +	int btf_fd[2] = {}; +	int prog_fd[2] = {}; +	int link_fd[2] = {}; +	__s32 btf_id[2] = {}; + +	LIBBPF_OPTS(bpf_prog_load_opts, load_opts, +		.expected_attach_type = BPF_TRACE_FENTRY, +	); + +	const struct bpf_insn trace_program[] = { +		BPF_MOV64_IMM(BPF_REG_0, 0), +		BPF_EXIT_INSN(), +	}; + +	vmlinux_btf = btf__load_vmlinux_btf(); +	if (!ASSERT_OK_PTR(vmlinux_btf, "load_vmlinux_btf")) +		return; + +	btf_fd[1] = get_bpf_testmod_btf_fd(); +	if (!ASSERT_GE(btf_fd[1], 0, "get_bpf_testmod_btf_fd")) +		goto out; + +	mod_btf = btf_get_from_fd(btf_fd[1], vmlinux_btf); +	if (!ASSERT_OK_PTR(mod_btf, "btf_get_from_fd")) +		goto out; + +	btf_id[0] = btf__find_by_name_kind(vmlinux_btf, symbol_name, BTF_KIND_FUNC); +	if (!ASSERT_GT(btf_id[0], 0, "btf_find_by_name")) +		goto out; + +	btf_id[1] = btf__find_by_name_kind(mod_btf, symbol_name, BTF_KIND_FUNC); +	if (!ASSERT_GT(btf_id[1], 0, "btf_find_by_name")) +		goto out; + +	for (i = 0; i < 2; i++) { +		load_opts.attach_btf_id = btf_id[i]; +		load_opts.attach_btf_obj_fd = btf_fd[i]; +		prog_fd[i] = bpf_prog_load(BPF_PROG_TYPE_TRACING, NULL, "GPL", +					   trace_program, +					   sizeof(trace_program) / sizeof(struct bpf_insn), +					   &load_opts); +		if (!ASSERT_GE(prog_fd[i], 0, "bpf_prog_load")) +			goto out; + +		/* If the verifier incorrectly resolves addresses of the +		 * shadowed functions and uses the same address for both the +		 * vmlinux and the bpf_testmod functions, this will fail on +		 * attempting to create two trampolines for the same address, +		 * which is forbidden. +		 */ +		link_fd[i] = bpf_link_create(prog_fd[i], 0, BPF_TRACE_FENTRY, NULL); +		if (!ASSERT_GE(link_fd[i], 0, "bpf_link_create")) +			goto out; +	} + +	err = bpf_prog_test_run_opts(prog_fd[0], NULL); +	ASSERT_OK(err, "running test"); + +out: +	btf__free(vmlinux_btf); +	btf__free(mod_btf); +	for (i = 0; i < 2; i++) { +		if (btf_fd[i]) +			close(btf_fd[i]); +		if (prog_fd[i] > 0) +			close(prog_fd[i]); +		if (link_fd[i] > 0) +			close(link_fd[i]); +	} +} diff --git a/tools/testing/selftests/bpf/prog_tests/mptcp.c b/tools/testing/selftests/bpf/prog_tests/mptcp.c index 59f08d6d1d53..cd0c42fff7c0 100644 --- a/tools/testing/selftests/bpf/prog_tests/mptcp.c +++ b/tools/testing/selftests/bpf/prog_tests/mptcp.c @@ -7,6 +7,8 @@  #include "network_helpers.h"  #include "mptcp_sock.skel.h" +#define NS_TEST "mptcp_ns" +  #ifndef TCP_CA_NAME_MAX  #define TCP_CA_NAME_MAX	16  #endif @@ -138,12 +140,20 @@ out:  static void test_base(void)  { +	struct nstoken *nstoken = NULL;  	int server_fd, cgroup_fd;  	cgroup_fd = test__join_cgroup("/mptcp");  	if (!ASSERT_GE(cgroup_fd, 0, "test__join_cgroup"))  		return; +	SYS(fail, "ip netns add %s", NS_TEST); +	SYS(fail, "ip -net %s link set dev lo up", NS_TEST); + +	nstoken = open_netns(NS_TEST); +	if (!ASSERT_OK_PTR(nstoken, "open_netns")) +		goto fail; +  	/* without MPTCP */  	server_fd = start_server(AF_INET, SOCK_STREAM, NULL, 0, 0);  	if (!ASSERT_GE(server_fd, 0, "start_server")) @@ -157,13 +167,18 @@ with_mptcp:  	/* with MPTCP */  	server_fd = start_mptcp_server(AF_INET, NULL, 0, 0);  	if (!ASSERT_GE(server_fd, 0, "start_mptcp_server")) -		goto close_cgroup_fd; +		goto fail;  	ASSERT_OK(run_test(cgroup_fd, server_fd, true), "run_test mptcp");  	close(server_fd); -close_cgroup_fd: +fail: +	if (nstoken) +		close_netns(nstoken); + +	SYS_NOFAIL("ip netns del " NS_TEST " &> /dev/null"); +  	close(cgroup_fd);  } diff --git a/tools/testing/selftests/bpf/prog_tests/parse_tcp_hdr_opt.c b/tools/testing/selftests/bpf/prog_tests/parse_tcp_hdr_opt.c new file mode 100644 index 000000000000..daa952711d8f --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/parse_tcp_hdr_opt.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <test_progs.h> +#include <network_helpers.h> +#include "test_parse_tcp_hdr_opt.skel.h" +#include "test_parse_tcp_hdr_opt_dynptr.skel.h" +#include "test_tcp_hdr_options.h" + +struct test_pkt { +	struct ipv6_packet pk6_v6; +	u8 options[16]; +} __packed; + +struct test_pkt pkt = { +	.pk6_v6.eth.h_proto = __bpf_constant_htons(ETH_P_IPV6), +	.pk6_v6.iph.nexthdr = IPPROTO_TCP, +	.pk6_v6.iph.payload_len = __bpf_constant_htons(MAGIC_BYTES), +	.pk6_v6.tcp.urg_ptr = 123, +	.pk6_v6.tcp.doff = 9, /* 16 bytes of options */ + +	.options = { +		TCPOPT_MSS, 4, 0x05, 0xB4, TCPOPT_NOP, TCPOPT_NOP, +		0, 6, 0xBB, 0xBB, 0xBB, 0xBB, TCPOPT_EOL +	}, +}; + +static void test_parse_opt(void) +{ +	struct test_parse_tcp_hdr_opt *skel; +	struct bpf_program *prog; +	char buf[128]; +	int err; + +	LIBBPF_OPTS(bpf_test_run_opts, topts, +		    .data_in = &pkt, +		    .data_size_in = sizeof(pkt), +		    .data_out = buf, +		    .data_size_out = sizeof(buf), +		    .repeat = 3, +	); + +	skel = test_parse_tcp_hdr_opt__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) +		return; + +	pkt.options[6] = skel->rodata->tcp_hdr_opt_kind_tpr; +	prog = skel->progs.xdp_ingress_v6; + +	err = bpf_prog_test_run_opts(bpf_program__fd(prog), &topts); +	ASSERT_OK(err, "ipv6 test_run"); +	ASSERT_EQ(topts.retval, XDP_PASS, "ipv6 test_run retval"); +	ASSERT_EQ(skel->bss->server_id, 0xBBBBBBBB, "server id"); + +	test_parse_tcp_hdr_opt__destroy(skel); +} + +static void test_parse_opt_dynptr(void) +{ +	struct test_parse_tcp_hdr_opt_dynptr *skel; +	struct bpf_program *prog; +	char buf[128]; +	int err; + +	LIBBPF_OPTS(bpf_test_run_opts, topts, +		    .data_in = &pkt, +		    .data_size_in = sizeof(pkt), +		    .data_out = buf, +		    .data_size_out = sizeof(buf), +		    .repeat = 3, +	); + +	skel = test_parse_tcp_hdr_opt_dynptr__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "skel_open_and_load")) +		return; + +	pkt.options[6] = skel->rodata->tcp_hdr_opt_kind_tpr; +	prog = skel->progs.xdp_ingress_v6; + +	err = bpf_prog_test_run_opts(bpf_program__fd(prog), &topts); +	ASSERT_OK(err, "ipv6 test_run"); +	ASSERT_EQ(topts.retval, XDP_PASS, "ipv6 test_run retval"); +	ASSERT_EQ(skel->bss->server_id, 0xBBBBBBBB, "server id"); + +	test_parse_tcp_hdr_opt_dynptr__destroy(skel); +} + +void test_parse_tcp_hdr_opt(void) +{ +	if (test__start_subtest("parse_tcp_hdr_opt")) +		test_parse_opt(); +	if (test__start_subtest("parse_tcp_hdr_opt_dynptr")) +		test_parse_opt_dynptr(); +} diff --git a/tools/testing/selftests/bpf/prog_tests/perf_event_stackmap.c b/tools/testing/selftests/bpf/prog_tests/perf_event_stackmap.c index 33144c9432ae..f4aad35afae1 100644 --- a/tools/testing/selftests/bpf/prog_tests/perf_event_stackmap.c +++ b/tools/testing/selftests/bpf/prog_tests/perf_event_stackmap.c @@ -63,7 +63,8 @@ void test_perf_event_stackmap(void)  			PERF_SAMPLE_BRANCH_NO_FLAGS |  			PERF_SAMPLE_BRANCH_NO_CYCLES |  			PERF_SAMPLE_BRANCH_CALL_STACK, -		.sample_period = 5000, +		.freq = 1, +		.sample_freq = read_perf_max_sample_freq(),  		.size = sizeof(struct perf_event_attr),  	};  	struct perf_event_stackmap *skel; diff --git a/tools/testing/selftests/bpf/prog_tests/rbtree.c b/tools/testing/selftests/bpf/prog_tests/rbtree.c index 156fa95c42f6..e9300c96607d 100644 --- a/tools/testing/selftests/bpf/prog_tests/rbtree.c +++ b/tools/testing/selftests/bpf/prog_tests/rbtree.c @@ -77,6 +77,29 @@ static void test_rbtree_first_and_remove(void)  	rbtree__destroy(skel);  } +static void test_rbtree_api_release_aliasing(void) +{ +	LIBBPF_OPTS(bpf_test_run_opts, opts, +		    .data_in = &pkt_v4, +		    .data_size_in = sizeof(pkt_v4), +		    .repeat = 1, +	); +	struct rbtree *skel; +	int ret; + +	skel = rbtree__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "rbtree__open_and_load")) +		return; + +	ret = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.rbtree_api_release_aliasing), &opts); +	ASSERT_OK(ret, "rbtree_api_release_aliasing"); +	ASSERT_OK(opts.retval, "rbtree_api_release_aliasing retval"); +	ASSERT_EQ(skel->data->first_data[0], 42, "rbtree_api_release_aliasing first rbtree_remove()"); +	ASSERT_EQ(skel->data->first_data[1], -1, "rbtree_api_release_aliasing second rbtree_remove()"); + +	rbtree__destroy(skel); +} +  void test_rbtree_success(void)  {  	if (test__start_subtest("rbtree_add_nodes")) @@ -85,6 +108,8 @@ void test_rbtree_success(void)  		test_rbtree_add_and_remove();  	if (test__start_subtest("rbtree_first_and_remove"))  		test_rbtree_first_and_remove(); +	if (test__start_subtest("rbtree_api_release_aliasing")) +		test_rbtree_api_release_aliasing();  }  #define BTF_FAIL_TEST(suffix)									\ diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c index 447d8560ecb6..3f1f58d3a729 100644 --- a/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c +++ b/tools/testing/selftests/bpf/prog_tests/rcu_read_lock.c @@ -25,10 +25,10 @@ static void test_success(void)  	bpf_program__set_autoload(skel->progs.get_cgroup_id, true);  	bpf_program__set_autoload(skel->progs.task_succ, true); -	bpf_program__set_autoload(skel->progs.no_lock, true);  	bpf_program__set_autoload(skel->progs.two_regions, true);  	bpf_program__set_autoload(skel->progs.non_sleepable_1, true);  	bpf_program__set_autoload(skel->progs.non_sleepable_2, true); +	bpf_program__set_autoload(skel->progs.task_trusted_non_rcuptr, true);  	err = rcu_read_lock__load(skel);  	if (!ASSERT_OK(err, "skel_load"))  		goto out; @@ -69,6 +69,7 @@ out:  static const char * const inproper_region_tests[] = {  	"miss_lock", +	"no_lock",  	"miss_unlock",  	"non_sleepable_rcu_mismatch",  	"inproper_sleepable_helper", @@ -99,7 +100,6 @@ out:  }  static const char * const rcuptr_misuse_tests[] = { -	"task_untrusted_non_rcuptr",  	"task_untrusted_rcuptr",  	"cross_rcu_region",  }; @@ -128,17 +128,8 @@ out:  void test_rcu_read_lock(void)  { -	struct btf *vmlinux_btf;  	int cgroup_fd; -	vmlinux_btf = btf__load_vmlinux_btf(); -	if (!ASSERT_OK_PTR(vmlinux_btf, "could not load vmlinux BTF")) -		return; -	if (btf__find_by_name_kind(vmlinux_btf, "rcu", BTF_KIND_TYPE_TAG) < 0) { -		test__skip(); -		goto out; -	} -  	cgroup_fd = test__join_cgroup("/rcu_read_lock");  	if (!ASSERT_GE(cgroup_fd, 0, "join_cgroup /rcu_read_lock"))  		goto out; @@ -153,6 +144,5 @@ void test_rcu_read_lock(void)  	if (test__start_subtest("negative_tests_rcuptr_misuse"))  		test_rcuptr_misuse();  	close(cgroup_fd); -out: -	btf__free(vmlinux_btf); +out:;  } diff --git a/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c b/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c new file mode 100644 index 000000000000..595cbf92bff5 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/refcounted_kptr.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include <test_progs.h> +#include <network_helpers.h> + +#include "refcounted_kptr.skel.h" +#include "refcounted_kptr_fail.skel.h" + +void test_refcounted_kptr(void) +{ +} + +void test_refcounted_kptr_fail(void) +{ +} diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c index d63a20fbed33..b15b343ebb6b 100644 --- a/tools/testing/selftests/bpf/prog_tests/send_signal.c +++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c @@ -64,8 +64,12 @@ static void test_send_signal_common(struct perf_event_attr *attr,  		ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read");  		/* wait a little for signal handler */ -		for (int i = 0; i < 1000000000 && !sigusr1_received; i++) +		for (int i = 0; i < 1000000000 && !sigusr1_received; i++) {  			j /= i + j + 1; +			if (!attr) +				/* trigger the nanosleep tracepoint program. */ +				usleep(1); +		}  		buf[0] = sigusr1_received ? '2' : '0';  		ASSERT_EQ(sigusr1_received, 1, "sigusr1_received"); diff --git a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c index 567e07c19ecc..141c1e5944ee 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c +++ b/tools/testing/selftests/bpf/prog_tests/sockmap_listen.c @@ -18,6 +18,12 @@  #include <string.h>  #include <sys/select.h>  #include <unistd.h> +#include <linux/vm_sockets.h> + +/* workaround for older vm_sockets.h */ +#ifndef VMADDR_CID_LOCAL +#define VMADDR_CID_LOCAL 1 +#endif  #include <bpf/bpf.h>  #include <bpf/libbpf.h> @@ -251,6 +257,16 @@ static void init_addr_loopback6(struct sockaddr_storage *ss, socklen_t *len)  	*len = sizeof(*addr6);  } +static void init_addr_loopback_vsock(struct sockaddr_storage *ss, socklen_t *len) +{ +	struct sockaddr_vm *addr = memset(ss, 0, sizeof(*ss)); + +	addr->svm_family = AF_VSOCK; +	addr->svm_port = VMADDR_PORT_ANY; +	addr->svm_cid = VMADDR_CID_LOCAL; +	*len = sizeof(*addr); +} +  static void init_addr_loopback(int family, struct sockaddr_storage *ss,  			       socklen_t *len)  { @@ -261,6 +277,9 @@ static void init_addr_loopback(int family, struct sockaddr_storage *ss,  	case AF_INET6:  		init_addr_loopback6(ss, len);  		return; +	case AF_VSOCK: +		init_addr_loopback_vsock(ss, len); +		return;  	default:  		FAIL("unsupported address family %d", family);  	} @@ -1478,6 +1497,8 @@ static const char *family_str(sa_family_t family)  		return "IPv6";  	case AF_UNIX:  		return "Unix"; +	case AF_VSOCK: +		return "VSOCK";  	default:  		return "unknown";  	} @@ -1689,6 +1710,151 @@ static void test_unix_redir(struct test_sockmap_listen *skel, struct bpf_map *ma  	unix_skb_redir_to_connected(skel, map, sotype);  } +/* Returns two connected loopback vsock sockets */ +static int vsock_socketpair_connectible(int sotype, int *v0, int *v1) +{ +	struct sockaddr_storage addr; +	socklen_t len = sizeof(addr); +	int s, p, c; + +	s = socket_loopback(AF_VSOCK, sotype); +	if (s < 0) +		return -1; + +	c = xsocket(AF_VSOCK, sotype | SOCK_NONBLOCK, 0); +	if (c == -1) +		goto close_srv; + +	if (getsockname(s, sockaddr(&addr), &len) < 0) +		goto close_cli; + +	if (connect(c, sockaddr(&addr), len) < 0 && errno != EINPROGRESS) { +		FAIL_ERRNO("connect"); +		goto close_cli; +	} + +	len = sizeof(addr); +	p = accept_timeout(s, sockaddr(&addr), &len, IO_TIMEOUT_SEC); +	if (p < 0) +		goto close_cli; + +	*v0 = p; +	*v1 = c; + +	return 0; + +close_cli: +	close(c); +close_srv: +	close(s); + +	return -1; +} + +static void vsock_unix_redir_connectible(int sock_mapfd, int verd_mapfd, +					 enum redir_mode mode, int sotype) +{ +	const char *log_prefix = redir_mode_str(mode); +	char a = 'a', b = 'b'; +	int u0, u1, v0, v1; +	int sfd[2]; +	unsigned int pass; +	int err, n; +	u32 key; + +	zero_verdict_count(verd_mapfd); + +	if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0, sfd)) +		return; + +	u0 = sfd[0]; +	u1 = sfd[1]; + +	err = vsock_socketpair_connectible(sotype, &v0, &v1); +	if (err) { +		FAIL("vsock_socketpair_connectible() failed"); +		goto close_uds; +	} + +	err = add_to_sockmap(sock_mapfd, u0, v0); +	if (err) { +		FAIL("add_to_sockmap failed"); +		goto close_vsock; +	} + +	n = write(v1, &a, sizeof(a)); +	if (n < 0) +		FAIL_ERRNO("%s: write", log_prefix); +	if (n == 0) +		FAIL("%s: incomplete write", log_prefix); +	if (n < 1) +		goto out; + +	n = recv(mode == REDIR_INGRESS ? u0 : u1, &b, sizeof(b), MSG_DONTWAIT); +	if (n < 0) +		FAIL("%s: recv() err, errno=%d", log_prefix, errno); +	if (n == 0) +		FAIL("%s: incomplete recv", log_prefix); +	if (b != a) +		FAIL("%s: vsock socket map failed, %c != %c", log_prefix, a, b); + +	key = SK_PASS; +	err = xbpf_map_lookup_elem(verd_mapfd, &key, &pass); +	if (err) +		goto out; +	if (pass != 1) +		FAIL("%s: want pass count 1, have %d", log_prefix, pass); +out: +	key = 0; +	bpf_map_delete_elem(sock_mapfd, &key); +	key = 1; +	bpf_map_delete_elem(sock_mapfd, &key); + +close_vsock: +	close(v0); +	close(v1); + +close_uds: +	close(u0); +	close(u1); +} + +static void vsock_unix_skb_redir_connectible(struct test_sockmap_listen *skel, +					     struct bpf_map *inner_map, +					     int sotype) +{ +	int verdict = bpf_program__fd(skel->progs.prog_skb_verdict); +	int verdict_map = bpf_map__fd(skel->maps.verdict_map); +	int sock_map = bpf_map__fd(inner_map); +	int err; + +	err = xbpf_prog_attach(verdict, sock_map, BPF_SK_SKB_VERDICT, 0); +	if (err) +		return; + +	skel->bss->test_ingress = false; +	vsock_unix_redir_connectible(sock_map, verdict_map, REDIR_EGRESS, sotype); +	skel->bss->test_ingress = true; +	vsock_unix_redir_connectible(sock_map, verdict_map, REDIR_INGRESS, sotype); + +	xbpf_prog_detach2(verdict, sock_map, BPF_SK_SKB_VERDICT); +} + +static void test_vsock_redir(struct test_sockmap_listen *skel, struct bpf_map *map) +{ +	const char *family_name, *map_name; +	char s[MAX_TEST_NAME]; + +	family_name = family_str(AF_VSOCK); +	map_name = map_type_str(map); +	snprintf(s, sizeof(s), "%s %s %s", map_name, family_name, __func__); +	if (!test__start_subtest(s)) +		return; + +	vsock_unix_skb_redir_connectible(skel, map, SOCK_STREAM); +	vsock_unix_skb_redir_connectible(skel, map, SOCK_SEQPACKET); +} +  static void test_reuseport(struct test_sockmap_listen *skel,  			   struct bpf_map *map, int family, int sotype)  { @@ -2060,12 +2226,14 @@ void serial_test_sockmap_listen(void)  	run_tests(skel, skel->maps.sock_map, AF_INET6);  	test_unix_redir(skel, skel->maps.sock_map, SOCK_DGRAM);  	test_unix_redir(skel, skel->maps.sock_map, SOCK_STREAM); +	test_vsock_redir(skel, skel->maps.sock_map);  	skel->bss->test_sockmap = false;  	run_tests(skel, skel->maps.sock_hash, AF_INET);  	run_tests(skel, skel->maps.sock_hash, AF_INET6);  	test_unix_redir(skel, skel->maps.sock_hash, SOCK_DGRAM);  	test_unix_redir(skel, skel->maps.sock_hash, SOCK_STREAM); +	test_vsock_redir(skel, skel->maps.sock_hash);  	test_sockmap_listen__destroy(skel);  } diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c index 60d952719d27..4512dd808c33 100644 --- a/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c +++ b/tools/testing/selftests/bpf/prog_tests/sockopt_sk.c @@ -3,6 +3,7 @@  #include "cgroup_helpers.h"  #include <linux/tcp.h> +#include <linux/netlink.h>  #include "sockopt_sk.skel.h"  #ifndef SOL_TCP @@ -183,6 +184,33 @@ static int getsetsockopt(void)  		goto err;  	} +	/* optval=NULL case is handled correctly */ + +	close(fd); +	fd = socket(AF_NETLINK, SOCK_RAW, 0); +	if (fd < 0) { +		log_err("Failed to create AF_NETLINK socket"); +		return -1; +	} + +	buf.u32 = 1; +	optlen = sizeof(__u32); +	err = setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &buf, optlen); +	if (err) { +		log_err("Unexpected getsockopt(NETLINK_ADD_MEMBERSHIP) err=%d errno=%d", +			err, errno); +		goto err; +	} + +	optlen = 0; +	err = getsockopt(fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &optlen); +	if (err) { +		log_err("Unexpected getsockopt(NETLINK_LIST_MEMBERSHIPS) err=%d errno=%d", +			err, errno); +		goto err; +	} +	ASSERT_EQ(optlen, 4, "Unexpected NETLINK_LIST_MEMBERSHIPS value"); +  	free(big_buf);  	close(fd);  	return 0; diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c index 9ad09a6c538a..b7ba5cd47d96 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id.c @@ -7,13 +7,12 @@ void test_stacktrace_build_id(void)  	int control_map_fd, stackid_hmap_fd, stackmap_fd, stack_amap_fd;  	struct test_stacktrace_build_id *skel; -	int err, stack_trace_len; +	int err, stack_trace_len, build_id_size;  	__u32 key, prev_key, val, duration = 0; -	char buf[256]; -	int i, j; +	char buf[BPF_BUILD_ID_SIZE];  	struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH];  	int build_id_matches = 0; -	int retry = 1; +	int i, retry = 1;  retry:  	skel = test_stacktrace_build_id__open_and_load(); @@ -52,9 +51,10 @@ retry:  		  "err %d errno %d\n", err, errno))  		goto cleanup; -	err = extract_build_id(buf, 256); +	build_id_size = read_build_id("urandom_read", buf, sizeof(buf)); +	err = build_id_size < 0 ? build_id_size : 0; -	if (CHECK(err, "get build_id with readelf", +	if (CHECK(err, "read_build_id",  		  "err %d errno %d\n", err, errno))  		goto cleanup; @@ -64,8 +64,6 @@ retry:  		goto cleanup;  	do { -		char build_id[64]; -  		err = bpf_map_lookup_elem(stackmap_fd, &key, id_offs);  		if (CHECK(err, "lookup_elem from stackmap",  			  "err %d, errno %d\n", err, errno)) @@ -73,10 +71,7 @@ retry:  		for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i)  			if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID &&  			    id_offs[i].offset != 0) { -				for (j = 0; j < 20; ++j) -					sprintf(build_id + 2 * j, "%02x", -						id_offs[i].build_id[j] & 0xff); -				if (strstr(buf, build_id) != NULL) +				if (memcmp(buf, id_offs[i].build_id, build_id_size) == 0)  					build_id_matches = 1;  			}  		prev_key = key; diff --git a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c index f4ea1a215ce4..5db9eec24b5b 100644 --- a/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c +++ b/tools/testing/selftests/bpf/prog_tests/stacktrace_build_id_nmi.c @@ -2,21 +2,6 @@  #include <test_progs.h>  #include "test_stacktrace_build_id.skel.h" -static __u64 read_perf_max_sample_freq(void) -{ -	__u64 sample_freq = 5000; /* fallback to 5000 on error */ -	FILE *f; -	__u32 duration = 0; - -	f = fopen("/proc/sys/kernel/perf_event_max_sample_rate", "r"); -	if (f == NULL) -		return sample_freq; -	CHECK(fscanf(f, "%llu", &sample_freq) != 1, "Get max sample rate", -		  "return default value: 5000,err %d\n", -errno); -	fclose(f); -	return sample_freq; -} -  void test_stacktrace_build_id_nmi(void)  {  	int control_map_fd, stackid_hmap_fd, stackmap_fd; @@ -28,11 +13,10 @@ void test_stacktrace_build_id_nmi(void)  		.config = PERF_COUNT_HW_CPU_CYCLES,  	};  	__u32 key, prev_key, val, duration = 0; -	char buf[256]; -	int i, j; +	char buf[BPF_BUILD_ID_SIZE];  	struct bpf_stack_build_id id_offs[PERF_MAX_STACK_DEPTH]; -	int build_id_matches = 0; -	int retry = 1; +	int build_id_matches = 0, build_id_size; +	int i, retry = 1;  	attr.sample_freq = read_perf_max_sample_freq(); @@ -94,7 +78,8 @@ retry:  		  "err %d errno %d\n", err, errno))  		goto cleanup; -	err = extract_build_id(buf, 256); +	build_id_size = read_build_id("urandom_read", buf, sizeof(buf)); +	err = build_id_size < 0 ? build_id_size : 0;  	if (CHECK(err, "get build_id with readelf",  		  "err %d errno %d\n", err, errno)) @@ -106,8 +91,6 @@ retry:  		goto cleanup;  	do { -		char build_id[64]; -  		err = bpf_map__lookup_elem(skel->maps.stackmap, &key, sizeof(key),  					   id_offs, sizeof(id_offs), 0);  		if (CHECK(err, "lookup_elem from stackmap", @@ -116,10 +99,7 @@ retry:  		for (i = 0; i < PERF_MAX_STACK_DEPTH; ++i)  			if (id_offs[i].status == BPF_STACK_BUILD_ID_VALID &&  			    id_offs[i].offset != 0) { -				for (j = 0; j < 20; ++j) -					sprintf(build_id + 2 * j, "%02x", -						id_offs[i].build_id[j] & 0xff); -				if (strstr(buf, build_id) != NULL) +				if (memcmp(buf, id_offs[i].build_id, build_id_size) == 0)  					build_id_matches = 1;  			}  		prev_key = key; diff --git a/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c b/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c index c717741bf8b6..c91eda624657 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c +++ b/tools/testing/selftests/bpf/prog_tests/task_fd_query_tp.c @@ -17,8 +17,13 @@ static void test_task_fd_query_tp_core(const char *probe_name,  	if (CHECK(err, "bpf_prog_test_load", "err %d errno %d\n", err, errno))  		goto close_prog; -	snprintf(buf, sizeof(buf), -		 "/sys/kernel/debug/tracing/events/%s/id", probe_name); +	if (access("/sys/kernel/tracing/trace", F_OK) == 0) { +		snprintf(buf, sizeof(buf), +			 "/sys/kernel/tracing/events/%s/id", probe_name); +	} else { +		snprintf(buf, sizeof(buf), +			 "/sys/kernel/debug/tracing/events/%s/id", probe_name); +	}  	efd = open(buf, O_RDONLY, 0);  	if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))  		goto close_prog; diff --git a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c index f79fa5bc9a8d..740d5f644b40 100644 --- a/tools/testing/selftests/bpf/prog_tests/task_kfunc.c +++ b/tools/testing/selftests/bpf/prog_tests/task_kfunc.c @@ -73,11 +73,12 @@ static const char * const success_tests[] = {  	"test_task_acquire_release_current",  	"test_task_acquire_leave_in_map",  	"test_task_xchg_release", -	"test_task_get_release", +	"test_task_map_acquire_release",  	"test_task_current_acquire_release",  	"test_task_from_pid_arg",  	"test_task_from_pid_current",  	"test_task_from_pid_invalid", +	"task_kfunc_acquire_trusted_walked",  };  void test_task_kfunc(void) diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c index bca5e6839ac4..6ee22c3b251a 100644 --- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c @@ -137,24 +137,16 @@ static int get_ifaddr(const char *name, char *ifaddr)  	return 0;  } -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			goto fail;				\ -	}) -  static int netns_setup_links_and_routes(struct netns_setup_result *result)  {  	struct nstoken *nstoken = NULL;  	char veth_src_fwd_addr[IFADDR_STR_LEN+1] = {}; -	SYS("ip link add veth_src type veth peer name veth_src_fwd"); -	SYS("ip link add veth_dst type veth peer name veth_dst_fwd"); +	SYS(fail, "ip link add veth_src type veth peer name veth_src_fwd"); +	SYS(fail, "ip link add veth_dst type veth peer name veth_dst_fwd"); -	SYS("ip link set veth_dst_fwd address " MAC_DST_FWD); -	SYS("ip link set veth_dst address " MAC_DST); +	SYS(fail, "ip link set veth_dst_fwd address " MAC_DST_FWD); +	SYS(fail, "ip link set veth_dst address " MAC_DST);  	if (get_ifaddr("veth_src_fwd", veth_src_fwd_addr))  		goto fail; @@ -175,27 +167,27 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)  	if (!ASSERT_GT(result->ifindex_veth_dst_fwd, 0, "ifindex_veth_dst_fwd"))  		goto fail; -	SYS("ip link set veth_src netns " NS_SRC); -	SYS("ip link set veth_src_fwd netns " NS_FWD); -	SYS("ip link set veth_dst_fwd netns " NS_FWD); -	SYS("ip link set veth_dst netns " NS_DST); +	SYS(fail, "ip link set veth_src netns " NS_SRC); +	SYS(fail, "ip link set veth_src_fwd netns " NS_FWD); +	SYS(fail, "ip link set veth_dst_fwd netns " NS_FWD); +	SYS(fail, "ip link set veth_dst netns " NS_DST);  	/** setup in 'src' namespace */  	nstoken = open_netns(NS_SRC);  	if (!ASSERT_OK_PTR(nstoken, "setns src"))  		goto fail; -	SYS("ip addr add " IP4_SRC "/32 dev veth_src"); -	SYS("ip addr add " IP6_SRC "/128 dev veth_src nodad"); -	SYS("ip link set dev veth_src up"); +	SYS(fail, "ip addr add " IP4_SRC "/32 dev veth_src"); +	SYS(fail, "ip addr add " IP6_SRC "/128 dev veth_src nodad"); +	SYS(fail, "ip link set dev veth_src up"); -	SYS("ip route add " IP4_DST "/32 dev veth_src scope global"); -	SYS("ip route add " IP4_NET "/16 dev veth_src scope global"); -	SYS("ip route add " IP6_DST "/128 dev veth_src scope global"); +	SYS(fail, "ip route add " IP4_DST "/32 dev veth_src scope global"); +	SYS(fail, "ip route add " IP4_NET "/16 dev veth_src scope global"); +	SYS(fail, "ip route add " IP6_DST "/128 dev veth_src scope global"); -	SYS("ip neigh add " IP4_DST " dev veth_src lladdr %s", +	SYS(fail, "ip neigh add " IP4_DST " dev veth_src lladdr %s",  	    veth_src_fwd_addr); -	SYS("ip neigh add " IP6_DST " dev veth_src lladdr %s", +	SYS(fail, "ip neigh add " IP6_DST " dev veth_src lladdr %s",  	    veth_src_fwd_addr);  	close_netns(nstoken); @@ -209,15 +201,15 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)  	 * needs v4 one in order to start ARP probing. IP4_NET route is added  	 * to the endpoints so that the ARP processing will reply.  	 */ -	SYS("ip addr add " IP4_SLL "/32 dev veth_src_fwd"); -	SYS("ip addr add " IP4_DLL "/32 dev veth_dst_fwd"); -	SYS("ip link set dev veth_src_fwd up"); -	SYS("ip link set dev veth_dst_fwd up"); +	SYS(fail, "ip addr add " IP4_SLL "/32 dev veth_src_fwd"); +	SYS(fail, "ip addr add " IP4_DLL "/32 dev veth_dst_fwd"); +	SYS(fail, "ip link set dev veth_src_fwd up"); +	SYS(fail, "ip link set dev veth_dst_fwd up"); -	SYS("ip route add " IP4_SRC "/32 dev veth_src_fwd scope global"); -	SYS("ip route add " IP6_SRC "/128 dev veth_src_fwd scope global"); -	SYS("ip route add " IP4_DST "/32 dev veth_dst_fwd scope global"); -	SYS("ip route add " IP6_DST "/128 dev veth_dst_fwd scope global"); +	SYS(fail, "ip route add " IP4_SRC "/32 dev veth_src_fwd scope global"); +	SYS(fail, "ip route add " IP6_SRC "/128 dev veth_src_fwd scope global"); +	SYS(fail, "ip route add " IP4_DST "/32 dev veth_dst_fwd scope global"); +	SYS(fail, "ip route add " IP6_DST "/128 dev veth_dst_fwd scope global");  	close_netns(nstoken); @@ -226,16 +218,16 @@ static int netns_setup_links_and_routes(struct netns_setup_result *result)  	if (!ASSERT_OK_PTR(nstoken, "setns dst"))  		goto fail; -	SYS("ip addr add " IP4_DST "/32 dev veth_dst"); -	SYS("ip addr add " IP6_DST "/128 dev veth_dst nodad"); -	SYS("ip link set dev veth_dst up"); +	SYS(fail, "ip addr add " IP4_DST "/32 dev veth_dst"); +	SYS(fail, "ip addr add " IP6_DST "/128 dev veth_dst nodad"); +	SYS(fail, "ip link set dev veth_dst up"); -	SYS("ip route add " IP4_SRC "/32 dev veth_dst scope global"); -	SYS("ip route add " IP4_NET "/16 dev veth_dst scope global"); -	SYS("ip route add " IP6_SRC "/128 dev veth_dst scope global"); +	SYS(fail, "ip route add " IP4_SRC "/32 dev veth_dst scope global"); +	SYS(fail, "ip route add " IP4_NET "/16 dev veth_dst scope global"); +	SYS(fail, "ip route add " IP6_SRC "/128 dev veth_dst scope global"); -	SYS("ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD); -	SYS("ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD); +	SYS(fail, "ip neigh add " IP4_SRC " dev veth_dst lladdr " MAC_DST_FWD); +	SYS(fail, "ip neigh add " IP6_SRC " dev veth_dst lladdr " MAC_DST_FWD);  	close_netns(nstoken); @@ -375,7 +367,7 @@ done:  static int test_ping(int family, const char *addr)  { -	SYS("ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr); +	SYS(fail, "ip netns exec " NS_SRC " %s " PING_ARGS " %s > /dev/null", ping_command(family), addr);  	return 0;  fail:  	return -1; @@ -953,7 +945,7 @@ static int tun_open(char *name)  	if (!ASSERT_OK(err, "ioctl TUNSETIFF"))  		goto fail; -	SYS("ip link set dev %s up", name); +	SYS(fail, "ip link set dev %s up", name);  	return fd;  fail: @@ -1076,23 +1068,23 @@ static void test_tc_redirect_peer_l3(struct netns_setup_result *setup_result)  	XGRESS_FILTER_ADD(&qdisc_veth_dst_fwd, BPF_TC_EGRESS, skel->progs.tc_chk, 0);  	/* Setup route and neigh tables */ -	SYS("ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24"); -	SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24"); +	SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP4_TUN_SRC "/24"); +	SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP4_TUN_FWD "/24"); -	SYS("ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad"); -	SYS("ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad"); +	SYS(fail, "ip -netns " NS_SRC " addr add dev tun_src " IP6_TUN_SRC "/64 nodad"); +	SYS(fail, "ip -netns " NS_FWD " addr add dev tun_fwd " IP6_TUN_FWD "/64 nodad"); -	SYS("ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global"); -	SYS("ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD +	SYS(fail, "ip -netns " NS_SRC " route del " IP4_DST "/32 dev veth_src scope global"); +	SYS(fail, "ip -netns " NS_SRC " route add " IP4_DST "/32 via " IP4_TUN_FWD  	    " dev tun_src scope global"); -	SYS("ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global"); -	SYS("ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global"); -	SYS("ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD +	SYS(fail, "ip -netns " NS_DST " route add " IP4_TUN_SRC "/32 dev veth_dst scope global"); +	SYS(fail, "ip -netns " NS_SRC " route del " IP6_DST "/128 dev veth_src scope global"); +	SYS(fail, "ip -netns " NS_SRC " route add " IP6_DST "/128 via " IP6_TUN_FWD  	    " dev tun_src scope global"); -	SYS("ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global"); +	SYS(fail, "ip -netns " NS_DST " route add " IP6_TUN_SRC "/128 dev veth_dst scope global"); -	SYS("ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD); -	SYS("ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD); +	SYS(fail, "ip -netns " NS_DST " neigh add " IP4_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD); +	SYS(fail, "ip -netns " NS_DST " neigh add " IP6_TUN_SRC " dev veth_dst lladdr " MAC_DST_FWD);  	if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))  		goto fail; diff --git a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c index 5cf85d0f9827..13bcaeb028b8 100644 --- a/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c +++ b/tools/testing/selftests/bpf/prog_tests/tcp_hdr_options.c @@ -151,7 +151,7 @@ static int check_hdr_opt(const struct bpf_test_option *exp,  			 const struct bpf_test_option *act,  			 const char *hdr_desc)  { -	if (!ASSERT_OK(memcmp(exp, act, sizeof(*exp)), hdr_desc)) { +	if (!ASSERT_EQ(memcmp(exp, act, sizeof(*exp)), 0, hdr_desc)) {  		print_option(exp, "expected: ");  		print_option(act, "  actual: ");  		return -1; @@ -169,7 +169,7 @@ static int check_hdr_stg(const struct hdr_stg *exp, int fd,  		  "map_lookup(hdr_stg_map_fd)"))  		return -1; -	if (!ASSERT_OK(memcmp(exp, &act, sizeof(*exp)), stg_desc)) { +	if (!ASSERT_EQ(memcmp(exp, &act, sizeof(*exp)), 0, stg_desc)) {  		print_hdr_stg(exp, "expected: ");  		print_hdr_stg(&act, "  actual: ");  		return -1; diff --git a/tools/testing/selftests/bpf/prog_tests/test_ima.c b/tools/testing/selftests/bpf/prog_tests/test_ima.c index b13feceb38f1..810b14981c2e 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_ima.c +++ b/tools/testing/selftests/bpf/prog_tests/test_ima.c @@ -70,7 +70,7 @@ void test_test_ima(void)  	u64 bin_true_sample;  	char cmd[256]; -	int err, duration = 0; +	int err, duration = 0, fresh_digest_idx = 0;  	struct ima *skel = NULL;  	skel = ima__open_and_load(); @@ -129,7 +129,15 @@ void test_test_ima(void)  	/*  	 * Test #3  	 * - Goal: confirm that bpf_ima_inode_hash() returns a non-fresh digest -	 * - Expected result: 2 samples (/bin/true: non-fresh, fresh) +	 * - Expected result: +	 *   1 sample (/bin/true: fresh) if commit 62622dab0a28 applied +	 *   2 samples (/bin/true: non-fresh, fresh) if commit 62622dab0a28 is +	 *     not applied +	 * +	 * If commit 62622dab0a28 ("ima: return IMA digest value only when +	 * IMA_COLLECTED flag is set") is applied, bpf_ima_inode_hash() refuses +	 * to give a non-fresh digest, hence the correct result is 1 instead of +	 * 2.  	 */  	test_init(skel->bss); @@ -144,13 +152,18 @@ void test_test_ima(void)  		goto close_clean;  	err = ring_buffer__consume(ringbuf); -	ASSERT_EQ(err, 2, "num_samples_or_err"); -	ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); -	ASSERT_NEQ(ima_hash_from_bpf[1], 0, "ima_hash"); -	ASSERT_EQ(ima_hash_from_bpf[0], bin_true_sample, "sample_equal_or_err"); +	ASSERT_GE(err, 1, "num_samples_or_err"); +	if (err == 2) { +		ASSERT_NEQ(ima_hash_from_bpf[0], 0, "ima_hash"); +		ASSERT_EQ(ima_hash_from_bpf[0], bin_true_sample, +			  "sample_equal_or_err"); +		fresh_digest_idx = 1; +	} + +	ASSERT_NEQ(ima_hash_from_bpf[fresh_digest_idx], 0, "ima_hash");  	/* IMA refreshed the digest. */ -	ASSERT_NEQ(ima_hash_from_bpf[1], bin_true_sample, -		   "sample_different_or_err"); +	ASSERT_NEQ(ima_hash_from_bpf[fresh_digest_idx], bin_true_sample, +		   "sample_equal_or_err");  	/*  	 * Test #4 diff --git a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c index 9c77cd6b1eaf..bcf2e1905ed7 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_local_storage.c +++ b/tools/testing/selftests/bpf/prog_tests/test_local_storage.c @@ -13,8 +13,6 @@  #include "network_helpers.h"  #include "task_local_storage_helpers.h" -static unsigned int duration; -  #define TEST_STORAGE_VALUE 0xbeefdead  struct storage { @@ -25,7 +23,7 @@ struct storage {  /* Fork and exec the provided rm binary and return the exit code of the   * forked process and its pid.   */ -static int run_self_unlink(int *monitored_pid, const char *rm_path) +static int run_self_unlink(struct local_storage *skel, const char *rm_path)  {  	int child_pid, child_status, ret;  	int null_fd; @@ -37,7 +35,7 @@ static int run_self_unlink(int *monitored_pid, const char *rm_path)  		dup2(null_fd, STDERR_FILENO);  		close(null_fd); -		*monitored_pid = getpid(); +		skel->bss->monitored_pid = getpid();  		/* Use the copied /usr/bin/rm to delete itself  		 * /tmp/copy_of_rm /tmp/copy_of_rm.  		 */ @@ -46,6 +44,7 @@ static int run_self_unlink(int *monitored_pid, const char *rm_path)  			exit(errno);  	} else if (child_pid > 0) {  		waitpid(child_pid, &child_status, 0); +		ASSERT_EQ(skel->data->task_storage_result, 0, "task_storage_result");  		return WEXITSTATUS(child_status);  	} @@ -60,36 +59,30 @@ static bool check_syscall_operations(int map_fd, int obj_fd)  	/* Looking up an existing element should fail initially */  	err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val, 0); -	if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem", -		  "err:%d errno:%d\n", err, errno)) +	if (!ASSERT_EQ(err, -ENOENT, "bpf_map_lookup_elem"))  		return false;  	/* Create a new element */  	err = bpf_map_update_elem(map_fd, &obj_fd, &val, BPF_NOEXIST); -	if (CHECK(err < 0, "bpf_map_update_elem", "err:%d errno:%d\n", err, -		  errno)) +	if (!ASSERT_OK(err, "bpf_map_update_elem"))  		return false;  	/* Lookup the newly created element */  	err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val, 0); -	if (CHECK(err < 0, "bpf_map_lookup_elem", "err:%d errno:%d", err, -		  errno)) +	if (!ASSERT_OK(err, "bpf_map_lookup_elem"))  		return false;  	/* Check the value of the newly created element */ -	if (CHECK(lookup_val.value != val.value, "bpf_map_lookup_elem", -		  "value got = %x errno:%d", lookup_val.value, val.value)) +	if (!ASSERT_EQ(lookup_val.value, val.value, "bpf_map_lookup_elem"))  		return false;  	err = bpf_map_delete_elem(map_fd, &obj_fd); -	if (CHECK(err, "bpf_map_delete_elem()", "err:%d errno:%d\n", err, -		  errno)) +	if (!ASSERT_OK(err, "bpf_map_delete_elem()"))  		return false;  	/* The lookup should fail, now that the element has been deleted */  	err = bpf_map_lookup_elem_flags(map_fd, &obj_fd, &lookup_val, 0); -	if (CHECK(!err || errno != ENOENT, "bpf_map_lookup_elem", -		  "err:%d errno:%d\n", err, errno)) +	if (!ASSERT_EQ(err, -ENOENT, "bpf_map_lookup_elem"))  		return false;  	return true; @@ -104,35 +97,32 @@ void test_test_local_storage(void)  	char cmd[256];  	skel = local_storage__open_and_load(); -	if (CHECK(!skel, "skel_load", "lsm skeleton failed\n")) +	if (!ASSERT_OK_PTR(skel, "skel_load"))  		goto close_prog;  	err = local_storage__attach(skel); -	if (CHECK(err, "attach", "lsm attach failed: %d\n", err)) +	if (!ASSERT_OK(err, "attach"))  		goto close_prog;  	task_fd = sys_pidfd_open(getpid(), 0); -	if (CHECK(task_fd < 0, "pidfd_open", -		  "failed to get pidfd err:%d, errno:%d", task_fd, errno)) +	if (!ASSERT_GE(task_fd, 0, "pidfd_open"))  		goto close_prog;  	if (!check_syscall_operations(bpf_map__fd(skel->maps.task_storage_map),  				      task_fd))  		goto close_prog; -	if (CHECK(!mkdtemp(tmp_dir_path), "mkdtemp", -		  "unable to create tmpdir: %d\n", errno)) +	if (!ASSERT_OK_PTR(mkdtemp(tmp_dir_path), "mkdtemp"))  		goto close_prog;  	snprintf(tmp_exec_path, sizeof(tmp_exec_path), "%s/copy_of_rm",  		 tmp_dir_path);  	snprintf(cmd, sizeof(cmd), "cp /bin/rm %s", tmp_exec_path); -	if (CHECK_FAIL(system(cmd))) +	if (!ASSERT_OK(system(cmd), "system(cp)"))  		goto close_prog_rmdir;  	rm_fd = open(tmp_exec_path, O_RDONLY); -	if (CHECK(rm_fd < 0, "open", "failed to open %s err:%d, errno:%d", -		  tmp_exec_path, rm_fd, errno)) +	if (!ASSERT_GE(rm_fd, 0, "open(tmp_exec_path)"))  		goto close_prog_rmdir;  	if (!check_syscall_operations(bpf_map__fd(skel->maps.inode_storage_map), @@ -144,8 +134,8 @@ void test_test_local_storage(void)  	 * unlink its executable. This operation should be denied by the loaded  	 * LSM program.  	 */ -	err = run_self_unlink(&skel->bss->monitored_pid, tmp_exec_path); -	if (CHECK(err != EPERM, "run_self_unlink", "err %d want EPERM\n", err)) +	err = run_self_unlink(skel, tmp_exec_path); +	if (!ASSERT_EQ(err, EPERM, "run_self_unlink"))  		goto close_prog_rmdir;  	/* Set the process being monitored to be the current process */ @@ -156,18 +146,16 @@ void test_test_local_storage(void)  	 */  	snprintf(cmd, sizeof(cmd), "mv %s/copy_of_rm %s/check_null_ptr",  		 tmp_dir_path, tmp_dir_path); -	if (CHECK_FAIL(system(cmd))) +	if (!ASSERT_OK(system(cmd), "system(mv)"))  		goto close_prog_rmdir; -	CHECK(skel->data->inode_storage_result != 0, "inode_storage_result", -	      "inode_local_storage not set\n"); +	ASSERT_EQ(skel->data->inode_storage_result, 0, "inode_storage_result");  	serv_sk = start_server(AF_INET6, SOCK_STREAM, NULL, 0, 0); -	if (CHECK(serv_sk < 0, "start_server", "failed to start server\n")) +	if (!ASSERT_GE(serv_sk, 0, "start_server"))  		goto close_prog_rmdir; -	CHECK(skel->data->sk_storage_result != 0, "sk_storage_result", -	      "sk_local_storage not set\n"); +	ASSERT_EQ(skel->data->sk_storage_result, 0, "sk_storage_result");  	if (!check_syscall_operations(bpf_map__fd(skel->maps.sk_storage_map),  				      serv_sk)) diff --git a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c index 07ad457f3370..d149ab98798d 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_tunnel.c +++ b/tools/testing/selftests/bpf/prog_tests/test_tunnel.c @@ -89,32 +89,20 @@  #define IP6VXLAN_TUNL_DEV0 "ip6vxlan00"  #define IP6VXLAN_TUNL_DEV1 "ip6vxlan11" -#define PING_ARGS "-i 0.01 -c 3 -w 10 -q" - -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			goto fail;				\ -	}) +#define IPIP_TUNL_DEV0 "ipip00" +#define IPIP_TUNL_DEV1 "ipip11" -#define SYS_NOFAIL(fmt, ...)					\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		system(cmd);					\ -	}) +#define PING_ARGS "-i 0.01 -c 3 -w 10 -q"  static int config_device(void)  { -	SYS("ip netns add at_ns0"); -	SYS("ip link add veth0 address " MAC_VETH1 " type veth peer name veth1"); -	SYS("ip link set veth0 netns at_ns0"); -	SYS("ip addr add " IP4_ADDR1_VETH1 "/24 dev veth1"); -	SYS("ip link set dev veth1 up mtu 1500"); -	SYS("ip netns exec at_ns0 ip addr add " IP4_ADDR_VETH0 "/24 dev veth0"); -	SYS("ip netns exec at_ns0 ip link set dev veth0 up mtu 1500"); +	SYS(fail, "ip netns add at_ns0"); +	SYS(fail, "ip link add veth0 address " MAC_VETH1 " type veth peer name veth1"); +	SYS(fail, "ip link set veth0 netns at_ns0"); +	SYS(fail, "ip addr add " IP4_ADDR1_VETH1 "/24 dev veth1"); +	SYS(fail, "ip link set dev veth1 up mtu 1500"); +	SYS(fail, "ip netns exec at_ns0 ip addr add " IP4_ADDR_VETH0 "/24 dev veth0"); +	SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up mtu 1500");  	return 0;  fail: @@ -132,23 +120,23 @@ static void cleanup(void)  static int add_vxlan_tunnel(void)  {  	/* at_ns0 namespace */ -	SYS("ip netns exec at_ns0 ip link add dev %s type vxlan external gbp dstport 4789", +	SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external gbp dstport 4789",  	    VXLAN_TUNL_DEV0); -	SYS("ip netns exec at_ns0 ip link set dev %s address %s up", +	SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",  	    VXLAN_TUNL_DEV0, MAC_TUNL_DEV0); -	SYS("ip netns exec at_ns0 ip addr add dev %s %s/24", +	SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",  	    VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0); -	SYS("ip netns exec at_ns0 ip neigh add %s lladdr %s dev %s", +	SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev %s",  	    IP4_ADDR_TUNL_DEV1, MAC_TUNL_DEV1, VXLAN_TUNL_DEV0); -	SYS("ip netns exec at_ns0 ip neigh add %s lladdr %s dev veth0", +	SYS(fail, "ip netns exec at_ns0 ip neigh add %s lladdr %s dev veth0",  	    IP4_ADDR2_VETH1, MAC_VETH1);  	/* root namespace */ -	SYS("ip link add dev %s type vxlan external gbp dstport 4789", +	SYS(fail, "ip link add dev %s type vxlan external gbp dstport 4789",  	    VXLAN_TUNL_DEV1); -	SYS("ip link set dev %s address %s up", VXLAN_TUNL_DEV1, MAC_TUNL_DEV1); -	SYS("ip addr add dev %s %s/24", VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1); -	SYS("ip neigh add %s lladdr %s dev %s", +	SYS(fail, "ip link set dev %s address %s up", VXLAN_TUNL_DEV1, MAC_TUNL_DEV1); +	SYS(fail, "ip addr add dev %s %s/24", VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1); +	SYS(fail, "ip neigh add %s lladdr %s dev %s",  	    IP4_ADDR_TUNL_DEV0, MAC_TUNL_DEV0, VXLAN_TUNL_DEV1);  	return 0; @@ -165,26 +153,26 @@ static void delete_vxlan_tunnel(void)  static int add_ip6vxlan_tunnel(void)  { -	SYS("ip netns exec at_ns0 ip -6 addr add %s/96 dev veth0", +	SYS(fail, "ip netns exec at_ns0 ip -6 addr add %s/96 dev veth0",  	    IP6_ADDR_VETH0); -	SYS("ip netns exec at_ns0 ip link set dev veth0 up"); -	SYS("ip -6 addr add %s/96 dev veth1", IP6_ADDR1_VETH1); -	SYS("ip -6 addr add %s/96 dev veth1", IP6_ADDR2_VETH1); -	SYS("ip link set dev veth1 up"); +	SYS(fail, "ip netns exec at_ns0 ip link set dev veth0 up"); +	SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR1_VETH1); +	SYS(fail, "ip -6 addr add %s/96 dev veth1", IP6_ADDR2_VETH1); +	SYS(fail, "ip link set dev veth1 up");  	/* at_ns0 namespace */ -	SYS("ip netns exec at_ns0 ip link add dev %s type vxlan external dstport 4789", +	SYS(fail, "ip netns exec at_ns0 ip link add dev %s type vxlan external dstport 4789",  	    IP6VXLAN_TUNL_DEV0); -	SYS("ip netns exec at_ns0 ip addr add dev %s %s/24", +	SYS(fail, "ip netns exec at_ns0 ip addr add dev %s %s/24",  	    IP6VXLAN_TUNL_DEV0, IP4_ADDR_TUNL_DEV0); -	SYS("ip netns exec at_ns0 ip link set dev %s address %s up", +	SYS(fail, "ip netns exec at_ns0 ip link set dev %s address %s up",  	    IP6VXLAN_TUNL_DEV0, MAC_TUNL_DEV0);  	/* root namespace */ -	SYS("ip link add dev %s type vxlan external dstport 4789", +	SYS(fail, "ip link add dev %s type vxlan external dstport 4789",  	    IP6VXLAN_TUNL_DEV1); -	SYS("ip addr add dev %s %s/24", IP6VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1); -	SYS("ip link set dev %s address %s up", +	SYS(fail, "ip addr add dev %s %s/24", IP6VXLAN_TUNL_DEV1, IP4_ADDR_TUNL_DEV1); +	SYS(fail, "ip link set dev %s address %s up",  	    IP6VXLAN_TUNL_DEV1, MAC_TUNL_DEV1);  	return 0; @@ -203,9 +191,82 @@ static void delete_ip6vxlan_tunnel(void)  	SYS_NOFAIL("ip link delete dev %s", IP6VXLAN_TUNL_DEV1);  } +enum ipip_encap { +	NONE	= 0, +	FOU	= 1, +	GUE	= 2, +}; + +static int set_ipip_encap(const char *ipproto, const char *type) +{ +	SYS(fail, "ip -n at_ns0 fou add port 5555 %s", ipproto); +	SYS(fail, "ip -n at_ns0 link set dev %s type ipip encap %s", +	    IPIP_TUNL_DEV0, type); +	SYS(fail, "ip -n at_ns0 link set dev %s type ipip encap-dport 5555", +	    IPIP_TUNL_DEV0); + +	return 0; +fail: +	return -1; +} + +static int add_ipip_tunnel(enum ipip_encap encap) +{ +	int err; +	const char *ipproto, *type; + +	switch (encap) { +	case FOU: +		ipproto = "ipproto 4"; +		type = "fou"; +		break; +	case GUE: +		ipproto = "gue"; +		type = ipproto; +		break; +	default: +		ipproto = NULL; +		type = ipproto; +	} + +	/* at_ns0 namespace */ +	SYS(fail, "ip -n at_ns0 link add dev %s type ipip local %s remote %s", +	    IPIP_TUNL_DEV0, IP4_ADDR_VETH0, IP4_ADDR1_VETH1); + +	if (type && ipproto) { +		err = set_ipip_encap(ipproto, type); +		if (!ASSERT_OK(err, "set_ipip_encap")) +			goto fail; +	} + +	SYS(fail, "ip -n at_ns0 link set dev %s up", IPIP_TUNL_DEV0); +	SYS(fail, "ip -n at_ns0 addr add dev %s %s/24", +	    IPIP_TUNL_DEV0, IP4_ADDR_TUNL_DEV0); + +	/* root namespace */ +	if (type && ipproto) +		SYS(fail, "ip fou add port 5555 %s", ipproto); +	SYS(fail, "ip link add dev %s type ipip external", IPIP_TUNL_DEV1); +	SYS(fail, "ip link set dev %s up", IPIP_TUNL_DEV1); +	SYS(fail, "ip addr add dev %s %s/24", IPIP_TUNL_DEV1, +	    IP4_ADDR_TUNL_DEV1); + +	return 0; +fail: +	return -1; +} + +static void delete_ipip_tunnel(void) +{ +	SYS_NOFAIL("ip -n at_ns0 link delete dev %s", IPIP_TUNL_DEV0); +	SYS_NOFAIL("ip -n at_ns0 fou del port 5555 2> /dev/null"); +	SYS_NOFAIL("ip link delete dev %s", IPIP_TUNL_DEV1); +	SYS_NOFAIL("ip fou del port 5555 2> /dev/null"); +} +  static int test_ping(int family, const char *addr)  { -	SYS("%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr); +	SYS(fail, "%s %s %s > /dev/null", ping_command(family), PING_ARGS, addr);  	return 0;  fail:  	return -1; @@ -401,10 +462,80 @@ done:  		test_tunnel_kern__destroy(skel);  } -#define RUN_TEST(name)							\ +static void test_ipip_tunnel(enum ipip_encap encap) +{ +	struct test_tunnel_kern *skel = NULL; +	struct nstoken *nstoken; +	int set_src_prog_fd, get_src_prog_fd; +	int ifindex = -1; +	int err; +	DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook, +			    .attach_point = BPF_TC_INGRESS); + +	/* add ipip tunnel */ +	err = add_ipip_tunnel(encap); +	if (!ASSERT_OK(err, "add_ipip_tunnel")) +		goto done; + +	/* load and attach bpf prog to tunnel dev tc hook point */ +	skel = test_tunnel_kern__open_and_load(); +	if (!ASSERT_OK_PTR(skel, "test_tunnel_kern__open_and_load")) +		goto done; +	ifindex = if_nametoindex(IPIP_TUNL_DEV1); +	if (!ASSERT_NEQ(ifindex, 0, "ipip11 ifindex")) +		goto done; +	tc_hook.ifindex = ifindex; + +	switch (encap) { +	case FOU: +		get_src_prog_fd = bpf_program__fd( +			skel->progs.ipip_encap_get_tunnel); +		set_src_prog_fd = bpf_program__fd( +			skel->progs.ipip_fou_set_tunnel); +		break; +	case GUE: +		get_src_prog_fd = bpf_program__fd( +			skel->progs.ipip_encap_get_tunnel); +		set_src_prog_fd = bpf_program__fd( +			skel->progs.ipip_gue_set_tunnel); +		break; +	default: +		get_src_prog_fd = bpf_program__fd( +			skel->progs.ipip_get_tunnel); +		set_src_prog_fd = bpf_program__fd( +			skel->progs.ipip_set_tunnel); +	} + +	if (!ASSERT_GE(set_src_prog_fd, 0, "bpf_program__fd")) +		goto done; +	if (!ASSERT_GE(get_src_prog_fd, 0, "bpf_program__fd")) +		goto done; +	if (attach_tc_prog(&tc_hook, get_src_prog_fd, set_src_prog_fd)) +		goto done; + +	/* ping from root namespace test */ +	err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV0); +	if (!ASSERT_OK(err, "test_ping")) +		goto done; + +	/* ping from at_ns0 namespace test */ +	nstoken = open_netns("at_ns0"); +	err = test_ping(AF_INET, IP4_ADDR_TUNL_DEV1); +	if (!ASSERT_OK(err, "test_ping")) +		goto done; +	close_netns(nstoken); + +done: +	/* delete ipip tunnel */ +	delete_ipip_tunnel(); +	if (skel) +		test_tunnel_kern__destroy(skel); +} + +#define RUN_TEST(name, ...)						\  	({								\  		if (test__start_subtest(#name)) {			\ -			test_ ## name();				\ +			test_ ## name(__VA_ARGS__);			\  		}							\  	}) @@ -415,6 +546,9 @@ static void *test_tunnel_run_tests(void *arg)  	RUN_TEST(vxlan_tunnel);  	RUN_TEST(ip6vxlan_tunnel); +	RUN_TEST(ipip_tunnel, NONE); +	RUN_TEST(ipip_tunnel, FOU); +	RUN_TEST(ipip_tunnel, GUE);  	cleanup(); diff --git a/tools/testing/selftests/bpf/prog_tests/timer.c b/tools/testing/selftests/bpf/prog_tests/timer.c index 7eb049214859..290c21dbe65a 100644 --- a/tools/testing/selftests/bpf/prog_tests/timer.c +++ b/tools/testing/selftests/bpf/prog_tests/timer.c @@ -29,6 +29,9 @@ static int timer(struct timer *timer_skel)  	/* check that timer_cb2() was executed twice */  	ASSERT_EQ(timer_skel->bss->bss_data, 10, "bss_data"); +	/* check that timer_cb3() was executed twice */ +	ASSERT_EQ(timer_skel->bss->abs_data, 12, "abs_data"); +  	/* check that there were no errors in timer execution */  	ASSERT_EQ(timer_skel->bss->err, 0, "err"); diff --git a/tools/testing/selftests/bpf/prog_tests/tp_attach_query.c b/tools/testing/selftests/bpf/prog_tests/tp_attach_query.c index 770fcc3bb1ba..655d69f0ff0b 100644 --- a/tools/testing/selftests/bpf/prog_tests/tp_attach_query.c +++ b/tools/testing/selftests/bpf/prog_tests/tp_attach_query.c @@ -16,8 +16,13 @@ void serial_test_tp_attach_query(void)  	for (i = 0; i < num_progs; i++)  		obj[i] = NULL; -	snprintf(buf, sizeof(buf), -		 "/sys/kernel/debug/tracing/events/sched/sched_switch/id"); +	if (access("/sys/kernel/tracing/trace", F_OK) == 0) { +		snprintf(buf, sizeof(buf), +			 "/sys/kernel/tracing/events/sched/sched_switch/id"); +	} else { +		snprintf(buf, sizeof(buf), +			 "/sys/kernel/debug/tracing/events/sched/sched_switch/id"); +	}  	efd = open(buf, O_RDONLY, 0);  	if (CHECK(efd < 0, "open", "err %d errno %d\n", efd, errno))  		return; diff --git a/tools/testing/selftests/bpf/prog_tests/trace_printk.c b/tools/testing/selftests/bpf/prog_tests/trace_printk.c index cade7f12315f..7b9124d506a5 100644 --- a/tools/testing/selftests/bpf/prog_tests/trace_printk.c +++ b/tools/testing/selftests/bpf/prog_tests/trace_printk.c @@ -5,7 +5,8 @@  #include "trace_printk.lskel.h" -#define TRACEBUF	"/sys/kernel/debug/tracing/trace_pipe" +#define TRACEFS_PIPE	"/sys/kernel/tracing/trace_pipe" +#define DEBUGFS_PIPE	"/sys/kernel/debug/tracing/trace_pipe"  #define SEARCHMSG	"testing,testing"  void serial_test_trace_printk(void) @@ -34,8 +35,11 @@ void serial_test_trace_printk(void)  	if (!ASSERT_OK(err, "trace_printk__attach"))  		goto cleanup; -	fp = fopen(TRACEBUF, "r"); -	if (!ASSERT_OK_PTR(fp, "fopen(TRACEBUF)")) +	if (access(TRACEFS_PIPE, F_OK) == 0) +		fp = fopen(TRACEFS_PIPE, "r"); +	else +		fp = fopen(DEBUGFS_PIPE, "r"); +	if (!ASSERT_OK_PTR(fp, "fopen(TRACE_PIPE)"))  		goto cleanup;  	/* We do not want to wait forever if this test fails... */ diff --git a/tools/testing/selftests/bpf/prog_tests/trace_vprintk.c b/tools/testing/selftests/bpf/prog_tests/trace_vprintk.c index 7a4e313e8558..44ea2fd88f4c 100644 --- a/tools/testing/selftests/bpf/prog_tests/trace_vprintk.c +++ b/tools/testing/selftests/bpf/prog_tests/trace_vprintk.c @@ -5,7 +5,8 @@  #include "trace_vprintk.lskel.h" -#define TRACEBUF	"/sys/kernel/debug/tracing/trace_pipe" +#define TRACEFS_PIPE	"/sys/kernel/tracing/trace_pipe" +#define DEBUGFS_PIPE	"/sys/kernel/debug/tracing/trace_pipe"  #define SEARCHMSG	"1,2,3,4,5,6,7,8,9,10"  void serial_test_trace_vprintk(void) @@ -27,8 +28,11 @@ void serial_test_trace_vprintk(void)  	if (!ASSERT_OK(err, "trace_vprintk__attach"))  		goto cleanup; -	fp = fopen(TRACEBUF, "r"); -	if (!ASSERT_OK_PTR(fp, "fopen(TRACEBUF)")) +	if (access(TRACEFS_PIPE, F_OK) == 0) +		fp = fopen(TRACEFS_PIPE, "r"); +	else +		fp = fopen(DEBUGFS_PIPE, "r"); +	if (!ASSERT_OK_PTR(fp, "fopen(TRACE_PIPE)"))  		goto cleanup;  	/* We do not want to wait forever if this test fails... */ diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c index 48dc9472e160..1c75a32186d6 100644 --- a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c +++ b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c @@ -53,6 +53,8 @@ static void test_fentry(void)  	ASSERT_EQ(skel->bss->t5_ret, 1, "t5 ret"); +	ASSERT_EQ(skel->bss->t6, 1, "t6 ret"); +  	tracing_struct__detach(skel);  destroy_skel:  	tracing_struct__destroy(skel); diff --git a/tools/testing/selftests/bpf/prog_tests/uprobe_autoattach.c b/tools/testing/selftests/bpf/prog_tests/uprobe_autoattach.c index 6558c857e620..d5b3377aa33c 100644 --- a/tools/testing/selftests/bpf/prog_tests/uprobe_autoattach.c +++ b/tools/testing/selftests/bpf/prog_tests/uprobe_autoattach.c @@ -3,7 +3,6 @@  #include <test_progs.h>  #include "test_uprobe_autoattach.skel.h" -#include "progs/bpf_misc.h"  /* uprobe attach point */  static noinline int autoattach_trigger_func(int arg1, int arg2, int arg3, diff --git a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c index 3a13e102c149..e51721df14fc 100644 --- a/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c +++ b/tools/testing/selftests/bpf/prog_tests/user_ringbuf.c @@ -590,7 +590,7 @@ static void *kick_kernel_cb(void *arg)  	/* Kick the kernel, causing it to drain the ring buffer and then wake  	 * up the test thread waiting on epoll.  	 */ -	syscall(__NR_getrlimit); +	syscall(__NR_prlimit64);  	return NULL;  } diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c new file mode 100644 index 000000000000..2497716ee379 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/verifier.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include <test_progs.h> + +#include "cap_helpers.h" +#include "verifier_and.skel.h" +#include "verifier_array_access.skel.h" +#include "verifier_basic_stack.skel.h" +#include "verifier_bounds.skel.h" +#include "verifier_bounds_deduction.skel.h" +#include "verifier_bounds_deduction_non_const.skel.h" +#include "verifier_bounds_mix_sign_unsign.skel.h" +#include "verifier_bpf_get_stack.skel.h" +#include "verifier_btf_ctx_access.skel.h" +#include "verifier_cfg.skel.h" +#include "verifier_cgroup_inv_retcode.skel.h" +#include "verifier_cgroup_skb.skel.h" +#include "verifier_cgroup_storage.skel.h" +#include "verifier_const_or.skel.h" +#include "verifier_ctx.skel.h" +#include "verifier_ctx_sk_msg.skel.h" +#include "verifier_d_path.skel.h" +#include "verifier_direct_packet_access.skel.h" +#include "verifier_direct_stack_access_wraparound.skel.h" +#include "verifier_div0.skel.h" +#include "verifier_div_overflow.skel.h" +#include "verifier_helper_access_var_len.skel.h" +#include "verifier_helper_packet_access.skel.h" +#include "verifier_helper_restricted.skel.h" +#include "verifier_helper_value_access.skel.h" +#include "verifier_int_ptr.skel.h" +#include "verifier_jeq_infer_not_null.skel.h" +#include "verifier_ld_ind.skel.h" +#include "verifier_leak_ptr.skel.h" +#include "verifier_loops1.skel.h" +#include "verifier_lwt.skel.h" +#include "verifier_map_in_map.skel.h" +#include "verifier_map_ptr.skel.h" +#include "verifier_map_ptr_mixing.skel.h" +#include "verifier_map_ret_val.skel.h" +#include "verifier_masking.skel.h" +#include "verifier_meta_access.skel.h" +#include "verifier_netfilter_ctx.skel.h" +#include "verifier_netfilter_retcode.skel.h" +#include "verifier_prevent_map_lookup.skel.h" +#include "verifier_raw_stack.skel.h" +#include "verifier_raw_tp_writable.skel.h" +#include "verifier_reg_equal.skel.h" +#include "verifier_ref_tracking.skel.h" +#include "verifier_regalloc.skel.h" +#include "verifier_ringbuf.skel.h" +#include "verifier_runtime_jit.skel.h" +#include "verifier_search_pruning.skel.h" +#include "verifier_sock.skel.h" +#include "verifier_spill_fill.skel.h" +#include "verifier_spin_lock.skel.h" +#include "verifier_stack_ptr.skel.h" +#include "verifier_subreg.skel.h" +#include "verifier_uninit.skel.h" +#include "verifier_unpriv.skel.h" +#include "verifier_unpriv_perf.skel.h" +#include "verifier_value_adj_spill.skel.h" +#include "verifier_value.skel.h" +#include "verifier_value_illegal_alu.skel.h" +#include "verifier_value_or_null.skel.h" +#include "verifier_value_ptr_arith.skel.h" +#include "verifier_var_off.skel.h" +#include "verifier_xadd.skel.h" +#include "verifier_xdp.skel.h" +#include "verifier_xdp_direct_packet_access.skel.h" + +#define MAX_ENTRIES 11 + +struct test_val { +	unsigned int index; +	int foo[MAX_ENTRIES]; +}; + +__maybe_unused +static void run_tests_aux(const char *skel_name, +			  skel_elf_bytes_fn elf_bytes_factory, +			  pre_execution_cb pre_execution_cb) +{ +	struct test_loader tester = {}; +	__u64 old_caps; +	int err; + +	/* test_verifier tests are executed w/o CAP_SYS_ADMIN, do the same here */ +	err = cap_disable_effective(1ULL << CAP_SYS_ADMIN, &old_caps); +	if (err) { +		PRINT_FAIL("failed to drop CAP_SYS_ADMIN: %i, %s\n", err, strerror(err)); +		return; +	} + +	test_loader__set_pre_execution_cb(&tester, pre_execution_cb); +	test_loader__run_subtests(&tester, skel_name, elf_bytes_factory); +	test_loader_fini(&tester); + +	err = cap_enable_effective(old_caps, NULL); +	if (err) +		PRINT_FAIL("failed to restore CAP_SYS_ADMIN: %i, %s\n", err, strerror(err)); +} + +#define RUN(skel) run_tests_aux(#skel, skel##__elf_bytes, NULL) + +void test_verifier_and(void)                  { RUN(verifier_and); } +void test_verifier_basic_stack(void)          { RUN(verifier_basic_stack); } +void test_verifier_bounds(void)               { RUN(verifier_bounds); } +void test_verifier_bounds_deduction(void)     { RUN(verifier_bounds_deduction); } +void test_verifier_bounds_deduction_non_const(void)     { RUN(verifier_bounds_deduction_non_const); } +void test_verifier_bounds_mix_sign_unsign(void) { RUN(verifier_bounds_mix_sign_unsign); } +void test_verifier_bpf_get_stack(void)        { RUN(verifier_bpf_get_stack); } +void test_verifier_btf_ctx_access(void)       { RUN(verifier_btf_ctx_access); } +void test_verifier_cfg(void)                  { RUN(verifier_cfg); } +void test_verifier_cgroup_inv_retcode(void)   { RUN(verifier_cgroup_inv_retcode); } +void test_verifier_cgroup_skb(void)           { RUN(verifier_cgroup_skb); } +void test_verifier_cgroup_storage(void)       { RUN(verifier_cgroup_storage); } +void test_verifier_const_or(void)             { RUN(verifier_const_or); } +void test_verifier_ctx(void)                  { RUN(verifier_ctx); } +void test_verifier_ctx_sk_msg(void)           { RUN(verifier_ctx_sk_msg); } +void test_verifier_d_path(void)               { RUN(verifier_d_path); } +void test_verifier_direct_packet_access(void) { RUN(verifier_direct_packet_access); } +void test_verifier_direct_stack_access_wraparound(void) { RUN(verifier_direct_stack_access_wraparound); } +void test_verifier_div0(void)                 { RUN(verifier_div0); } +void test_verifier_div_overflow(void)         { RUN(verifier_div_overflow); } +void test_verifier_helper_access_var_len(void) { RUN(verifier_helper_access_var_len); } +void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_access); } +void test_verifier_helper_restricted(void)    { RUN(verifier_helper_restricted); } +void test_verifier_helper_value_access(void)  { RUN(verifier_helper_value_access); } +void test_verifier_int_ptr(void)              { RUN(verifier_int_ptr); } +void test_verifier_jeq_infer_not_null(void)   { RUN(verifier_jeq_infer_not_null); } +void test_verifier_ld_ind(void)               { RUN(verifier_ld_ind); } +void test_verifier_leak_ptr(void)             { RUN(verifier_leak_ptr); } +void test_verifier_loops1(void)               { RUN(verifier_loops1); } +void test_verifier_lwt(void)                  { RUN(verifier_lwt); } +void test_verifier_map_in_map(void)           { RUN(verifier_map_in_map); } +void test_verifier_map_ptr(void)              { RUN(verifier_map_ptr); } +void test_verifier_map_ptr_mixing(void)       { RUN(verifier_map_ptr_mixing); } +void test_verifier_map_ret_val(void)          { RUN(verifier_map_ret_val); } +void test_verifier_masking(void)              { RUN(verifier_masking); } +void test_verifier_meta_access(void)          { RUN(verifier_meta_access); } +void test_verifier_netfilter_ctx(void)        { RUN(verifier_netfilter_ctx); } +void test_verifier_netfilter_retcode(void)    { RUN(verifier_netfilter_retcode); } +void test_verifier_prevent_map_lookup(void)   { RUN(verifier_prevent_map_lookup); } +void test_verifier_raw_stack(void)            { RUN(verifier_raw_stack); } +void test_verifier_raw_tp_writable(void)      { RUN(verifier_raw_tp_writable); } +void test_verifier_reg_equal(void)            { RUN(verifier_reg_equal); } +void test_verifier_ref_tracking(void)         { RUN(verifier_ref_tracking); } +void test_verifier_regalloc(void)             { RUN(verifier_regalloc); } +void test_verifier_ringbuf(void)              { RUN(verifier_ringbuf); } +void test_verifier_runtime_jit(void)          { RUN(verifier_runtime_jit); } +void test_verifier_search_pruning(void)       { RUN(verifier_search_pruning); } +void test_verifier_sock(void)                 { RUN(verifier_sock); } +void test_verifier_spill_fill(void)           { RUN(verifier_spill_fill); } +void test_verifier_spin_lock(void)            { RUN(verifier_spin_lock); } +void test_verifier_stack_ptr(void)            { RUN(verifier_stack_ptr); } +void test_verifier_subreg(void)               { RUN(verifier_subreg); } +void test_verifier_uninit(void)               { RUN(verifier_uninit); } +void test_verifier_unpriv(void)               { RUN(verifier_unpriv); } +void test_verifier_unpriv_perf(void)          { RUN(verifier_unpriv_perf); } +void test_verifier_value_adj_spill(void)      { RUN(verifier_value_adj_spill); } +void test_verifier_value(void)                { RUN(verifier_value); } +void test_verifier_value_illegal_alu(void)    { RUN(verifier_value_illegal_alu); } +void test_verifier_value_or_null(void)        { RUN(verifier_value_or_null); } +void test_verifier_var_off(void)              { RUN(verifier_var_off); } +void test_verifier_xadd(void)                 { RUN(verifier_xadd); } +void test_verifier_xdp(void)                  { RUN(verifier_xdp); } +void test_verifier_xdp_direct_packet_access(void) { RUN(verifier_xdp_direct_packet_access); } + +static int init_test_val_map(struct bpf_object *obj, char *map_name) +{ +	struct test_val value = { +		.index = (6 + 1) * sizeof(int), +		.foo[6] = 0xabcdef12, +	}; +	struct bpf_map *map; +	int err, key = 0; + +	map = bpf_object__find_map_by_name(obj, map_name); +	if (!map) { +		PRINT_FAIL("Can't find map '%s'\n", map_name); +		return -EINVAL; +	} + +	err = bpf_map_update_elem(bpf_map__fd(map), &key, &value, 0); +	if (err) { +		PRINT_FAIL("Error while updating map '%s': %d\n", map_name, err); +		return err; +	} + +	return 0; +} + +static int init_array_access_maps(struct bpf_object *obj) +{ +	return init_test_val_map(obj, "map_array_ro"); +} + +void test_verifier_array_access(void) +{ +	run_tests_aux("verifier_array_access", +		      verifier_array_access__elf_bytes, +		      init_array_access_maps); +} + +static int init_value_ptr_arith_maps(struct bpf_object *obj) +{ +	return init_test_val_map(obj, "map_array_48b"); +} + +void test_verifier_value_ptr_arith(void) +{ +	run_tests_aux("verifier_value_ptr_arith", +		      verifier_value_ptr_arith__elf_bytes, +		      init_value_ptr_arith_maps); +} diff --git a/tools/testing/selftests/bpf/prog_tests/verifier_log.c b/tools/testing/selftests/bpf/prog_tests/verifier_log.c new file mode 100644 index 000000000000..8337c6bc5b95 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/verifier_log.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include <test_progs.h> +#include <bpf/btf.h> + +#include "test_log_buf.skel.h" + + +static bool check_prog_load(int prog_fd, bool expect_err, const char *tag) +{ +	if (expect_err) { +		if (!ASSERT_LT(prog_fd, 0, tag)) { +			close(prog_fd); +			return false; +		} +	} else /* !expect_err */ { +		if (!ASSERT_GT(prog_fd, 0, tag)) +			return false; +	} +	if (prog_fd >= 0) +		close(prog_fd); +	return true; +} + +static struct { +	/* strategically placed before others to avoid accidental modification by kernel */ +	char filler[1024]; +	char buf[1024]; +	/* strategically placed after buf[] to catch more accidental corruptions */ +	char reference[1024]; +} logs; +static const struct bpf_insn *insns; +static size_t insn_cnt; + +static int load_prog(struct bpf_prog_load_opts *opts, bool expect_load_error) +{ +	int prog_fd; + +	prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, "log_prog", +				"GPL", insns, insn_cnt, opts); +	check_prog_load(prog_fd, expect_load_error, "prog_load"); + +	return prog_fd; +} + +static void verif_log_subtest(const char *name, bool expect_load_error, int log_level) +{ +	LIBBPF_OPTS(bpf_prog_load_opts, opts); +	char *exp_log, prog_name[16], op_name[32]; +	struct test_log_buf *skel; +	struct bpf_program *prog; +	size_t fixed_log_sz; +	__u32 log_true_sz_fixed, log_true_sz_rolling; +	int i, mode, err, prog_fd, res; + +	skel = test_log_buf__open(); +	if (!ASSERT_OK_PTR(skel, "skel_open")) +		return; + +	bpf_object__for_each_program(prog, skel->obj) { +		if (strcmp(bpf_program__name(prog), name) == 0) +			bpf_program__set_autoload(prog, true); +		else +			bpf_program__set_autoload(prog, false); +	} + +	err = test_log_buf__load(skel); +	if (!expect_load_error && !ASSERT_OK(err, "unexpected_load_failure")) +		goto cleanup; +	if (expect_load_error && !ASSERT_ERR(err, "unexpected_load_success")) +		goto cleanup; + +	insns = bpf_program__insns(skel->progs.good_prog); +	insn_cnt = bpf_program__insn_cnt(skel->progs.good_prog); + +	opts.log_buf = logs.reference; +	opts.log_size = sizeof(logs.reference); +	opts.log_level = log_level | 8 /* BPF_LOG_FIXED */; +	load_prog(&opts, expect_load_error); + +	fixed_log_sz = strlen(logs.reference) + 1; +	if (!ASSERT_GT(fixed_log_sz, 50, "fixed_log_sz")) +		goto cleanup; +	memset(logs.reference + fixed_log_sz, 0, sizeof(logs.reference) - fixed_log_sz); + +	/* validate BPF_LOG_FIXED works as verifier log used to work, that is: +	 * we get -ENOSPC and beginning of the full verifier log. This only +	 * works for log_level 2 and log_level 1 + failed program. For log +	 * level 2 we don't reset log at all. For log_level 1 + failed program +	 * we don't get to verification stats output. With log level 1 +	 * for successful program  final result will be just verifier stats. +	 * But if provided too short log buf, kernel will NULL-out log->ubuf +	 * and will stop emitting further log. This means we'll never see +	 * predictable verifier stats. +	 * Long story short, we do the following -ENOSPC test only for +	 * predictable combinations. +	 */ +	if (log_level >= 2 || expect_load_error) { +		opts.log_buf = logs.buf; +		opts.log_level = log_level | 8; /* fixed-length log */ +		opts.log_size = 25; + +		prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, "log_fixed25", +					"GPL", insns, insn_cnt, &opts); +		if (!ASSERT_EQ(prog_fd, -ENOSPC, "unexpected_log_fixed_prog_load_result")) { +			if (prog_fd >= 0) +				close(prog_fd); +			goto cleanup; +		} +		if (!ASSERT_EQ(strlen(logs.buf), 24, "log_fixed_25")) +			goto cleanup; +		if (!ASSERT_STRNEQ(logs.buf, logs.reference, 24, "log_fixed_contents_25")) +			goto cleanup; +	} + +	/* validate rolling verifier log logic: try all variations of log buf +	 * length to force various truncation scenarios +	 */ +	opts.log_buf = logs.buf; + +	/* rotating mode, then fixed mode */ +	for (mode = 1; mode >= 0; mode--) { +		/* prefill logs.buf with 'A's to detect any write beyond allowed length */ +		memset(logs.filler, 'A', sizeof(logs.filler)); +		logs.filler[sizeof(logs.filler) - 1] = '\0'; +		memset(logs.buf, 'A', sizeof(logs.buf)); +		logs.buf[sizeof(logs.buf) - 1] = '\0'; + +		for (i = 1; i < fixed_log_sz; i++) { +			opts.log_size = i; +			opts.log_level = log_level | (mode ? 0 : 8 /* BPF_LOG_FIXED */); + +			snprintf(prog_name, sizeof(prog_name), +				 "log_%s_%d", mode ? "roll" : "fixed", i); +			prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, prog_name, +						"GPL", insns, insn_cnt, &opts); + +			snprintf(op_name, sizeof(op_name), +				 "log_%s_prog_load_%d", mode ? "roll" : "fixed", i); +			if (!ASSERT_EQ(prog_fd, -ENOSPC, op_name)) { +				if (prog_fd >= 0) +					close(prog_fd); +				goto cleanup; +			} + +			snprintf(op_name, sizeof(op_name), +				 "log_%s_strlen_%d", mode ? "roll" : "fixed", i); +			ASSERT_EQ(strlen(logs.buf), i - 1, op_name); + +			if (mode) +				exp_log = logs.reference + fixed_log_sz - i; +			else +				exp_log = logs.reference; + +			snprintf(op_name, sizeof(op_name), +				 "log_%s_contents_%d", mode ? "roll" : "fixed", i); +			if (!ASSERT_STRNEQ(logs.buf, exp_log, i - 1, op_name)) { +				printf("CMP:%d\nS1:'%s'\nS2:'%s'\n", +					strncmp(logs.buf, exp_log, i - 1), +					logs.buf, exp_log); +				goto cleanup; +			} + +			/* check that unused portions of logs.buf is not overwritten */ +			snprintf(op_name, sizeof(op_name), +				 "log_%s_unused_%d", mode ? "roll" : "fixed", i); +			if (!ASSERT_STREQ(logs.buf + i, logs.filler + i, op_name)) { +				printf("CMP:%d\nS1:'%s'\nS2:'%s'\n", +					strcmp(logs.buf + i, logs.filler + i), +					logs.buf + i, logs.filler + i); +				goto cleanup; +			} +		} +	} + +	/* (FIXED) get actual log size */ +	opts.log_buf = logs.buf; +	opts.log_level = log_level | 8; /* BPF_LOG_FIXED */ +	opts.log_size = sizeof(logs.buf); +	opts.log_true_size = 0; +	res = load_prog(&opts, expect_load_error); +	ASSERT_NEQ(res, -ENOSPC, "prog_load_res_fixed"); + +	log_true_sz_fixed = opts.log_true_size; +	ASSERT_GT(log_true_sz_fixed, 0, "log_true_sz_fixed"); + +	/* (FIXED, NULL) get actual log size */ +	opts.log_buf = NULL; +	opts.log_level = log_level | 8; /* BPF_LOG_FIXED */ +	opts.log_size = 0; +	opts.log_true_size = 0; +	res = load_prog(&opts, expect_load_error); +	ASSERT_NEQ(res, -ENOSPC, "prog_load_res_fixed_null"); +	ASSERT_EQ(opts.log_true_size, log_true_sz_fixed, "log_sz_fixed_null_eq"); + +	/* (ROLLING) get actual log size */ +	opts.log_buf = logs.buf; +	opts.log_level = log_level; +	opts.log_size = sizeof(logs.buf); +	opts.log_true_size = 0; +	res = load_prog(&opts, expect_load_error); +	ASSERT_NEQ(res, -ENOSPC, "prog_load_res_rolling"); + +	log_true_sz_rolling = opts.log_true_size; +	ASSERT_EQ(log_true_sz_rolling, log_true_sz_fixed, "log_true_sz_eq"); + +	/* (ROLLING, NULL) get actual log size */ +	opts.log_buf = NULL; +	opts.log_level = log_level; +	opts.log_size = 0; +	opts.log_true_size = 0; +	res = load_prog(&opts, expect_load_error); +	ASSERT_NEQ(res, -ENOSPC, "prog_load_res_rolling_null"); +	ASSERT_EQ(opts.log_true_size, log_true_sz_rolling, "log_true_sz_null_eq"); + +	/* (FIXED) expect -ENOSPC for one byte short log */ +	opts.log_buf = logs.buf; +	opts.log_level = log_level | 8; /* BPF_LOG_FIXED */ +	opts.log_size = log_true_sz_fixed - 1; +	opts.log_true_size = 0; +	res = load_prog(&opts, true /* should fail */); +	ASSERT_EQ(res, -ENOSPC, "prog_load_res_too_short_fixed"); + +	/* (FIXED) expect *not* -ENOSPC with exact log_true_size buffer */ +	opts.log_buf = logs.buf; +	opts.log_level = log_level | 8; /* BPF_LOG_FIXED */ +	opts.log_size = log_true_sz_fixed; +	opts.log_true_size = 0; +	res = load_prog(&opts, expect_load_error); +	ASSERT_NEQ(res, -ENOSPC, "prog_load_res_just_right_fixed"); + +	/* (ROLLING) expect -ENOSPC for one byte short log */ +	opts.log_buf = logs.buf; +	opts.log_level = log_level; +	opts.log_size = log_true_sz_rolling - 1; +	res = load_prog(&opts, true /* should fail */); +	ASSERT_EQ(res, -ENOSPC, "prog_load_res_too_short_rolling"); + +	/* (ROLLING) expect *not* -ENOSPC with exact log_true_size buffer */ +	opts.log_buf = logs.buf; +	opts.log_level = log_level; +	opts.log_size = log_true_sz_rolling; +	opts.log_true_size = 0; +	res = load_prog(&opts, expect_load_error); +	ASSERT_NEQ(res, -ENOSPC, "prog_load_res_just_right_rolling"); + +cleanup: +	test_log_buf__destroy(skel); +} + +static const void *btf_data; +static u32 btf_data_sz; + +static int load_btf(struct bpf_btf_load_opts *opts, bool expect_err) +{ +	int fd; + +	fd = bpf_btf_load(btf_data, btf_data_sz, opts); +	if (fd >= 0) +		close(fd); +	if (expect_err) +		ASSERT_LT(fd, 0, "btf_load_failure"); +	else /* !expect_err */ +		ASSERT_GT(fd, 0, "btf_load_success"); +	return fd; +} + +static void verif_btf_log_subtest(bool bad_btf) +{ +	LIBBPF_OPTS(bpf_btf_load_opts, opts); +	struct btf *btf; +	struct btf_type *t; +	char *exp_log, op_name[32]; +	size_t fixed_log_sz; +	__u32 log_true_sz_fixed, log_true_sz_rolling; +	int i, res; + +	/* prepare simple BTF contents */ +	btf = btf__new_empty(); +	if (!ASSERT_OK_PTR(btf, "btf_new_empty")) +		return; +	res = btf__add_int(btf, "whatever", 4, 0); +	if (!ASSERT_GT(res, 0, "btf_add_int_id")) +		goto cleanup; +	if (bad_btf) { +		/* btf__add_int() doesn't allow bad value of size, so we'll just +		 * force-cast btf_type pointer and manually override size to invalid +		 * 3 if we need to simulate failure +		 */ +		t = (void *)btf__type_by_id(btf, res); +		if (!ASSERT_OK_PTR(t, "int_btf_type")) +			goto cleanup; +		t->size = 3; +	} + +	btf_data = btf__raw_data(btf, &btf_data_sz); +	if (!ASSERT_OK_PTR(btf_data, "btf_data")) +		goto cleanup; + +	load_btf(&opts, bad_btf); + +	opts.log_buf = logs.reference; +	opts.log_size = sizeof(logs.reference); +	opts.log_level = 1 | 8 /* BPF_LOG_FIXED */; +	load_btf(&opts, bad_btf); + +	fixed_log_sz = strlen(logs.reference) + 1; +	if (!ASSERT_GT(fixed_log_sz, 50, "fixed_log_sz")) +		goto cleanup; +	memset(logs.reference + fixed_log_sz, 0, sizeof(logs.reference) - fixed_log_sz); + +	/* validate BPF_LOG_FIXED truncation works as verifier log used to work */ +	opts.log_buf = logs.buf; +	opts.log_level = 1 | 8; /* fixed-length log */ +	opts.log_size = 25; +	res = load_btf(&opts, true); +	ASSERT_EQ(res, -ENOSPC, "half_log_fd"); +	ASSERT_EQ(strlen(logs.buf), 24, "log_fixed_25"); +	ASSERT_STRNEQ(logs.buf, logs.reference, 24, op_name); + +	/* validate rolling verifier log logic: try all variations of log buf +	 * length to force various truncation scenarios +	 */ +	opts.log_buf = logs.buf; +	opts.log_level = 1; /* rolling log */ + +	/* prefill logs.buf with 'A's to detect any write beyond allowed length */ +	memset(logs.filler, 'A', sizeof(logs.filler)); +	logs.filler[sizeof(logs.filler) - 1] = '\0'; +	memset(logs.buf, 'A', sizeof(logs.buf)); +	logs.buf[sizeof(logs.buf) - 1] = '\0'; + +	for (i = 1; i < fixed_log_sz; i++) { +		opts.log_size = i; + +		snprintf(op_name, sizeof(op_name), "log_roll_btf_load_%d", i); +		res = load_btf(&opts, true); +		if (!ASSERT_EQ(res, -ENOSPC, op_name)) +			goto cleanup; + +		exp_log = logs.reference + fixed_log_sz - i; +		snprintf(op_name, sizeof(op_name), "log_roll_contents_%d", i); +		if (!ASSERT_STREQ(logs.buf, exp_log, op_name)) { +			printf("CMP:%d\nS1:'%s'\nS2:'%s'\n", +				strcmp(logs.buf, exp_log), +				logs.buf, exp_log); +			goto cleanup; +		} + +		/* check that unused portions of logs.buf are not overwritten */ +		snprintf(op_name, sizeof(op_name), "log_roll_unused_tail_%d", i); +		if (!ASSERT_STREQ(logs.buf + i, logs.filler + i, op_name)) { +			printf("CMP:%d\nS1:'%s'\nS2:'%s'\n", +				strcmp(logs.buf + i, logs.filler + i), +				logs.buf + i, logs.filler + i); +			goto cleanup; +		} +	} + +	/* (FIXED) get actual log size */ +	opts.log_buf = logs.buf; +	opts.log_level = 1 | 8; /* BPF_LOG_FIXED */ +	opts.log_size = sizeof(logs.buf); +	opts.log_true_size = 0; +	res = load_btf(&opts, bad_btf); +	ASSERT_NEQ(res, -ENOSPC, "btf_load_res_fixed"); + +	log_true_sz_fixed = opts.log_true_size; +	ASSERT_GT(log_true_sz_fixed, 0, "log_true_sz_fixed"); + +	/* (FIXED, NULL) get actual log size */ +	opts.log_buf = NULL; +	opts.log_level = 1 | 8; /* BPF_LOG_FIXED */ +	opts.log_size = 0; +	opts.log_true_size = 0; +	res = load_btf(&opts, bad_btf); +	ASSERT_NEQ(res, -ENOSPC, "btf_load_res_fixed_null"); +	ASSERT_EQ(opts.log_true_size, log_true_sz_fixed, "log_sz_fixed_null_eq"); + +	/* (ROLLING) get actual log size */ +	opts.log_buf = logs.buf; +	opts.log_level = 1; +	opts.log_size = sizeof(logs.buf); +	opts.log_true_size = 0; +	res = load_btf(&opts, bad_btf); +	ASSERT_NEQ(res, -ENOSPC, "btf_load_res_rolling"); + +	log_true_sz_rolling = opts.log_true_size; +	ASSERT_EQ(log_true_sz_rolling, log_true_sz_fixed, "log_true_sz_eq"); + +	/* (ROLLING, NULL) get actual log size */ +	opts.log_buf = NULL; +	opts.log_level = 1; +	opts.log_size = 0; +	opts.log_true_size = 0; +	res = load_btf(&opts, bad_btf); +	ASSERT_NEQ(res, -ENOSPC, "btf_load_res_rolling_null"); +	ASSERT_EQ(opts.log_true_size, log_true_sz_rolling, "log_true_sz_null_eq"); + +	/* (FIXED) expect -ENOSPC for one byte short log */ +	opts.log_buf = logs.buf; +	opts.log_level = 1 | 8; /* BPF_LOG_FIXED */ +	opts.log_size = log_true_sz_fixed - 1; +	opts.log_true_size = 0; +	res = load_btf(&opts, true); +	ASSERT_EQ(res, -ENOSPC, "btf_load_res_too_short_fixed"); + +	/* (FIXED) expect *not* -ENOSPC with exact log_true_size buffer */ +	opts.log_buf = logs.buf; +	opts.log_level = 1 | 8; /* BPF_LOG_FIXED */ +	opts.log_size = log_true_sz_fixed; +	opts.log_true_size = 0; +	res = load_btf(&opts, bad_btf); +	ASSERT_NEQ(res, -ENOSPC, "btf_load_res_just_right_fixed"); + +	/* (ROLLING) expect -ENOSPC for one byte short log */ +	opts.log_buf = logs.buf; +	opts.log_level = 1; +	opts.log_size = log_true_sz_rolling - 1; +	res = load_btf(&opts, true); +	ASSERT_EQ(res, -ENOSPC, "btf_load_res_too_short_rolling"); + +	/* (ROLLING) expect *not* -ENOSPC with exact log_true_size buffer */ +	opts.log_buf = logs.buf; +	opts.log_level = 1; +	opts.log_size = log_true_sz_rolling; +	opts.log_true_size = 0; +	res = load_btf(&opts, bad_btf); +	ASSERT_NEQ(res, -ENOSPC, "btf_load_res_just_right_rolling"); + +cleanup: +	btf__free(btf); +} + +void test_verifier_log(void) +{ +	if (test__start_subtest("good_prog-level1")) +		verif_log_subtest("good_prog", false, 1); +	if (test__start_subtest("good_prog-level2")) +		verif_log_subtest("good_prog", false, 2); +	if (test__start_subtest("bad_prog-level1")) +		verif_log_subtest("bad_prog", true, 1); +	if (test__start_subtest("bad_prog-level2")) +		verif_log_subtest("bad_prog", true, 2); +	if (test__start_subtest("bad_btf")) +		verif_btf_log_subtest(true /* bad btf */); +	if (test__start_subtest("good_btf")) +		verif_btf_log_subtest(false /* !bad btf */); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_attach.c b/tools/testing/selftests/bpf/prog_tests/xdp_attach.c index d4cd9f873c14..fa3cac5488f5 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_attach.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_attach.c @@ -4,11 +4,10 @@  #define IFINDEX_LO 1  #define XDP_FLAGS_REPLACE		(1U << 4) -void serial_test_xdp_attach(void) +static void test_xdp_attach(const char *file)  {  	__u32 duration = 0, id1, id2, id0 = 0, len;  	struct bpf_object *obj1, *obj2, *obj3; -	const char *file = "./test_xdp.bpf.o";  	struct bpf_prog_info info = {};  	int err, fd1, fd2, fd3;  	LIBBPF_OPTS(bpf_xdp_attach_opts, opts); @@ -85,3 +84,11 @@ out_2:  out_1:  	bpf_object__close(obj1);  } + +void serial_test_xdp_attach(void) +{ +	if (test__start_subtest("xdp_attach")) +		test_xdp_attach("./test_xdp.bpf.o"); +	if (test__start_subtest("xdp_attach_dynptr")) +		test_xdp_attach("./test_xdp_dynptr.bpf.o"); +} diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c b/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c index 5e3a26b15ec6..d19f79048ff6 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_bonding.c @@ -141,41 +141,33 @@ static const char * const xmit_policy_names[] = {  static int bonding_setup(struct skeletons *skeletons, int mode, int xmit_policy,  			 int bond_both_attach)  { -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			return -1;				\ -	}) - -	SYS("ip netns add ns_dst"); -	SYS("ip link add veth1_1 type veth peer name veth2_1 netns ns_dst"); -	SYS("ip link add veth1_2 type veth peer name veth2_2 netns ns_dst"); - -	SYS("ip link add bond1 type bond mode %s xmit_hash_policy %s", +	SYS(fail, "ip netns add ns_dst"); +	SYS(fail, "ip link add veth1_1 type veth peer name veth2_1 netns ns_dst"); +	SYS(fail, "ip link add veth1_2 type veth peer name veth2_2 netns ns_dst"); + +	SYS(fail, "ip link add bond1 type bond mode %s xmit_hash_policy %s",  	    mode_names[mode], xmit_policy_names[xmit_policy]); -	SYS("ip link set bond1 up address " BOND1_MAC_STR " addrgenmode none"); -	SYS("ip -netns ns_dst link add bond2 type bond mode %s xmit_hash_policy %s", +	SYS(fail, "ip link set bond1 up address " BOND1_MAC_STR " addrgenmode none"); +	SYS(fail, "ip -netns ns_dst link add bond2 type bond mode %s xmit_hash_policy %s",  	    mode_names[mode], xmit_policy_names[xmit_policy]); -	SYS("ip -netns ns_dst link set bond2 up address " BOND2_MAC_STR " addrgenmode none"); +	SYS(fail, "ip -netns ns_dst link set bond2 up address " BOND2_MAC_STR " addrgenmode none"); -	SYS("ip link set veth1_1 master bond1"); +	SYS(fail, "ip link set veth1_1 master bond1");  	if (bond_both_attach == BOND_BOTH_AND_ATTACH) { -		SYS("ip link set veth1_2 master bond1"); +		SYS(fail, "ip link set veth1_2 master bond1");  	} else { -		SYS("ip link set veth1_2 up addrgenmode none"); +		SYS(fail, "ip link set veth1_2 up addrgenmode none");  		if (xdp_attach(skeletons, skeletons->xdp_dummy->progs.xdp_dummy_prog, "veth1_2"))  			return -1;  	} -	SYS("ip -netns ns_dst link set veth2_1 master bond2"); +	SYS(fail, "ip -netns ns_dst link set veth2_1 master bond2");  	if (bond_both_attach == BOND_BOTH_AND_ATTACH) -		SYS("ip -netns ns_dst link set veth2_2 master bond2"); +		SYS(fail, "ip -netns ns_dst link set veth2_2 master bond2");  	else -		SYS("ip -netns ns_dst link set veth2_2 up addrgenmode none"); +		SYS(fail, "ip -netns ns_dst link set veth2_2 up addrgenmode none");  	/* Load a dummy program on sending side as with veth peer needs to have a  	 * XDP program loaded as well. @@ -194,8 +186,8 @@ static int bonding_setup(struct skeletons *skeletons, int mode, int xmit_policy,  	}  	return 0; - -#undef SYS +fail: +	return -1;  }  static void bonding_cleanup(struct skeletons *skeletons) 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 7271a18ab3e2..498d3bdaa4b0 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c @@ -12,14 +12,6 @@  #include <uapi/linux/netdev.h>  #include "test_xdp_do_redirect.skel.h" -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			goto out;				\ -	}) -  struct udp_packet {  	struct ethhdr eth;  	struct ipv6hdr iph; @@ -95,12 +87,12 @@ static void test_max_pkt_size(int fd)  void test_xdp_do_redirect(void)  {  	int err, xdp_prog_fd, tc_prog_fd, ifindex_src, ifindex_dst; -	char data[sizeof(pkt_udp) + sizeof(__u32)]; +	char data[sizeof(pkt_udp) + sizeof(__u64)];  	struct test_xdp_do_redirect *skel = NULL;  	struct nstoken *nstoken = NULL;  	struct bpf_link *link;  	LIBBPF_OPTS(bpf_xdp_query_opts, query_opts); -	struct xdp_md ctx_in = { .data = sizeof(__u32), +	struct xdp_md ctx_in = { .data = sizeof(__u64),  				 .data_end = sizeof(data) };  	DECLARE_LIBBPF_OPTS(bpf_test_run_opts, opts,  			    .data_in = &data, @@ -114,8 +106,9 @@ void test_xdp_do_redirect(void)  	DECLARE_LIBBPF_OPTS(bpf_tc_hook, tc_hook,  			    .attach_point = BPF_TC_INGRESS); -	memcpy(&data[sizeof(__u32)], &pkt_udp, sizeof(pkt_udp)); +	memcpy(&data[sizeof(__u64)], &pkt_udp, sizeof(pkt_udp));  	*((__u32 *)data) = 0x42; /* metadata test value */ +	*((__u32 *)data + 4) = 0;  	skel = test_xdp_do_redirect__open();  	if (!ASSERT_OK_PTR(skel, "skel")) @@ -127,19 +120,19 @@ void test_xdp_do_redirect(void)  	 * iface and NUM_PKTS-2 in the TC hook. We match the packets on the UDP  	 * payload.  	 */ -	SYS("ip netns add testns"); +	SYS(out, "ip netns add testns");  	nstoken = open_netns("testns");  	if (!ASSERT_OK_PTR(nstoken, "setns"))  		goto out; -	SYS("ip link add veth_src type veth peer name veth_dst"); -	SYS("ip link set dev veth_src address 00:11:22:33:44:55"); -	SYS("ip link set dev veth_dst address 66:77:88:99:aa:bb"); -	SYS("ip link set dev veth_src up"); -	SYS("ip link set dev veth_dst up"); -	SYS("ip addr add dev veth_src fc00::1/64"); -	SYS("ip addr add dev veth_dst fc00::2/64"); -	SYS("ip neigh add fc00::2 dev veth_src lladdr 66:77:88:99:aa:bb"); +	SYS(out, "ip link add veth_src type veth peer name veth_dst"); +	SYS(out, "ip link set dev veth_src address 00:11:22:33:44:55"); +	SYS(out, "ip link set dev veth_dst address 66:77:88:99:aa:bb"); +	SYS(out, "ip link set dev veth_src up"); +	SYS(out, "ip link set dev veth_dst up"); +	SYS(out, "ip addr add dev veth_src fc00::1/64"); +	SYS(out, "ip addr add dev veth_dst fc00::2/64"); +	SYS(out, "ip neigh add fc00::2 dev veth_src lladdr 66:77:88:99:aa:bb");  	/* We enable forwarding in the test namespace because that will cause  	 * the packets that go through the kernel stack (with XDP_PASS) to be @@ -152,7 +145,7 @@ void test_xdp_do_redirect(void)  	 * code didn't have this, so we keep the test behaviour to make sure the  	 * bug doesn't resurface.  	 */ -	SYS("sysctl -qw net.ipv6.conf.all.forwarding=1"); +	SYS(out, "sysctl -qw net.ipv6.conf.all.forwarding=1");  	ifindex_src = if_nametoindex("veth_src");  	ifindex_dst = if_nametoindex("veth_dst"); @@ -167,8 +160,7 @@ void test_xdp_do_redirect(void)  	if (!ASSERT_EQ(query_opts.feature_flags,  		       NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | -		       NETDEV_XDP_ACT_NDO_XMIT | NETDEV_XDP_ACT_RX_SG | -		       NETDEV_XDP_ACT_NDO_XMIT_SG, +		       NETDEV_XDP_ACT_RX_SG,  		       "veth_src query_opts.feature_flags"))  		goto out; @@ -178,9 +170,34 @@ void test_xdp_do_redirect(void)  	if (!ASSERT_EQ(query_opts.feature_flags,  		       NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | +		       NETDEV_XDP_ACT_RX_SG, +		       "veth_dst query_opts.feature_flags")) +		goto out; + +	/* Enable GRO */ +	SYS(out, "ethtool -K veth_src gro on"); +	SYS(out, "ethtool -K veth_dst gro on"); + +	err = bpf_xdp_query(ifindex_src, XDP_FLAGS_DRV_MODE, &query_opts); +	if (!ASSERT_OK(err, "veth_src bpf_xdp_query gro on")) +		goto out; + +	if (!ASSERT_EQ(query_opts.feature_flags, +		       NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT |  		       NETDEV_XDP_ACT_NDO_XMIT | NETDEV_XDP_ACT_RX_SG |  		       NETDEV_XDP_ACT_NDO_XMIT_SG, -		       "veth_dst query_opts.feature_flags")) +		       "veth_src query_opts.feature_flags gro on")) +		goto out; + +	err = bpf_xdp_query(ifindex_dst, XDP_FLAGS_DRV_MODE, &query_opts); +	if (!ASSERT_OK(err, "veth_dst bpf_xdp_query gro on")) +		goto out; + +	if (!ASSERT_EQ(query_opts.feature_flags, +		       NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT | +		       NETDEV_XDP_ACT_NDO_XMIT | NETDEV_XDP_ACT_RX_SG | +		       NETDEV_XDP_ACT_NDO_XMIT_SG, +		       "veth_dst query_opts.feature_flags gro on"))  		goto out;  	memcpy(skel->rodata->expect_dst, &pkt_udp.eth.h_dest, ETH_ALEN); @@ -226,6 +243,6 @@ out_tc:  out:  	if (nstoken)  		close_netns(nstoken); -	system("ip netns del testns"); +	SYS_NOFAIL("ip netns del testns");  	test_xdp_do_redirect__destroy(skel);  } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c index aa4beae99f4f..626c461fa34d 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_metadata.c @@ -34,11 +34,6 @@  #define PREFIX_LEN "8"  #define FAMILY AF_INET -#define SYS(cmd) ({ \ -	if (!ASSERT_OK(system(cmd), (cmd))) \ -		goto out; \ -}) -  struct xsk {  	void *umem_area;  	struct xsk_umem *umem; @@ -273,6 +268,8 @@ static int verify_xsk_metadata(struct xsk *xsk)  	if (!ASSERT_NEQ(meta->rx_hash, 0, "rx_hash"))  		return -1; +	ASSERT_EQ(meta->rx_hash_type, 0, "rx_hash_type"); +  	xsk_ring_cons__release(&xsk->rx, 1);  	refill_rx(xsk, comp_addr); @@ -298,16 +295,16 @@ void test_xdp_metadata(void)  	/* Setup new networking namespace, with a veth pair. */ -	SYS("ip netns add xdp_metadata"); +	SYS(out, "ip netns add xdp_metadata");  	tok = open_netns("xdp_metadata"); -	SYS("ip link add numtxqueues 1 numrxqueues 1 " TX_NAME +	SYS(out, "ip link add numtxqueues 1 numrxqueues 1 " TX_NAME  	    " type veth peer " RX_NAME " numtxqueues 1 numrxqueues 1"); -	SYS("ip link set dev " TX_NAME " address 00:00:00:00:00:01"); -	SYS("ip link set dev " RX_NAME " address 00:00:00:00:00:02"); -	SYS("ip link set dev " TX_NAME " up"); -	SYS("ip link set dev " RX_NAME " up"); -	SYS("ip addr add " TX_ADDR "/" PREFIX_LEN " dev " TX_NAME); -	SYS("ip addr add " RX_ADDR "/" PREFIX_LEN " dev " RX_NAME); +	SYS(out, "ip link set dev " TX_NAME " address 00:00:00:00:00:01"); +	SYS(out, "ip link set dev " RX_NAME " address 00:00:00:00:00:02"); +	SYS(out, "ip link set dev " TX_NAME " up"); +	SYS(out, "ip link set dev " RX_NAME " up"); +	SYS(out, "ip addr add " TX_ADDR "/" PREFIX_LEN " dev " TX_NAME); +	SYS(out, "ip addr add " RX_ADDR "/" PREFIX_LEN " dev " RX_NAME);  	rx_ifindex = if_nametoindex(RX_NAME);  	tx_ifindex = if_nametoindex(TX_NAME); @@ -405,5 +402,5 @@ out:  	xdp_metadata__destroy(bpf_obj);  	if (tok)  		close_netns(tok); -	system("ip netns del xdp_metadata"); +	SYS_NOFAIL("ip netns del xdp_metadata");  } diff --git a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c index c72083885b6d..8b50a992d233 100644 --- a/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c +++ b/tools/testing/selftests/bpf/prog_tests/xdp_synproxy.c @@ -8,11 +8,6 @@  #define CMD_OUT_BUF_SIZE 1023 -#define SYS(cmd) ({ \ -	if (!ASSERT_OK(system(cmd), (cmd))) \ -		goto out; \ -}) -  #define SYS_OUT(cmd, ...) ({ \  	char buf[1024]; \  	snprintf(buf, sizeof(buf), (cmd), ##__VA_ARGS__); \ @@ -69,37 +64,37 @@ static void test_synproxy(bool xdp)  	char buf[CMD_OUT_BUF_SIZE];  	size_t size; -	SYS("ip netns add synproxy"); +	SYS(out, "ip netns add synproxy"); -	SYS("ip link add tmp0 type veth peer name tmp1"); -	SYS("ip link set tmp1 netns synproxy"); -	SYS("ip link set tmp0 up"); -	SYS("ip addr replace 198.18.0.1/24 dev tmp0"); +	SYS(out, "ip link add tmp0 type veth peer name tmp1"); +	SYS(out, "ip link set tmp1 netns synproxy"); +	SYS(out, "ip link set tmp0 up"); +	SYS(out, "ip addr replace 198.18.0.1/24 dev tmp0");  	/* When checksum offload is enabled, the XDP program sees wrong  	 * checksums and drops packets.  	 */ -	SYS("ethtool -K tmp0 tx off"); +	SYS(out, "ethtool -K tmp0 tx off");  	if (xdp)  		/* Workaround required for veth. */ -		SYS("ip link set tmp0 xdp object xdp_dummy.bpf.o section xdp 2> /dev/null"); +		SYS(out, "ip link set tmp0 xdp object xdp_dummy.bpf.o section xdp 2> /dev/null");  	ns = open_netns("synproxy");  	if (!ASSERT_OK_PTR(ns, "setns"))  		goto out; -	SYS("ip link set lo up"); -	SYS("ip link set tmp1 up"); -	SYS("ip addr replace 198.18.0.2/24 dev tmp1"); -	SYS("sysctl -w net.ipv4.tcp_syncookies=2"); -	SYS("sysctl -w net.ipv4.tcp_timestamps=1"); -	SYS("sysctl -w net.netfilter.nf_conntrack_tcp_loose=0"); -	SYS("iptables-legacy -t raw -I PREROUTING \ +	SYS(out, "ip link set lo up"); +	SYS(out, "ip link set tmp1 up"); +	SYS(out, "ip addr replace 198.18.0.2/24 dev tmp1"); +	SYS(out, "sysctl -w net.ipv4.tcp_syncookies=2"); +	SYS(out, "sysctl -w net.ipv4.tcp_timestamps=1"); +	SYS(out, "sysctl -w net.netfilter.nf_conntrack_tcp_loose=0"); +	SYS(out, "iptables-legacy -t raw -I PREROUTING \  	    -i tmp1 -p tcp -m tcp --syn --dport 8080 -j CT --notrack"); -	SYS("iptables-legacy -t filter -A INPUT \ +	SYS(out, "iptables-legacy -t filter -A INPUT \  	    -i tmp1 -p tcp -m tcp --dport 8080 -m state --state INVALID,UNTRACKED \  	    -j SYNPROXY --sack-perm --timestamp --wscale 7 --mss 1460"); -	SYS("iptables-legacy -t filter -A INPUT \ +	SYS(out, "iptables-legacy -t filter -A INPUT \  	    -i tmp1 -m state --state INVALID -j DROP");  	ctrl_file = SYS_OUT("./xdp_synproxy --iface tmp1 --ports 8080 \ @@ -170,8 +165,8 @@ out:  	if (ns)  		close_netns(ns); -	system("ip link del tmp0"); -	system("ip netns del synproxy"); +	SYS_NOFAIL("ip link del tmp0"); +	SYS_NOFAIL("ip netns del synproxy");  }  void test_xdp_synproxy(void) diff --git a/tools/testing/selftests/bpf/prog_tests/xfrm_info.c b/tools/testing/selftests/bpf/prog_tests/xfrm_info.c index 8b03c9bb4862..d37f5394e199 100644 --- a/tools/testing/selftests/bpf/prog_tests/xfrm_info.c +++ b/tools/testing/selftests/bpf/prog_tests/xfrm_info.c @@ -69,21 +69,6 @@      "proto esp aead 'rfc4106(gcm(aes))' " \      "0xe4d8f4b4da1df18a3510b3781496daa82488b713 128 mode tunnel " -#define SYS(fmt, ...)						\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		if (!ASSERT_OK(system(cmd), cmd))		\ -			goto fail;				\ -	}) - -#define SYS_NOFAIL(fmt, ...)					\ -	({							\ -		char cmd[1024];					\ -		snprintf(cmd, sizeof(cmd), fmt, ##__VA_ARGS__);	\ -		system(cmd);					\ -	}) -  static int attach_tc_prog(struct bpf_tc_hook *hook, int igr_fd, int egr_fd)  {  	LIBBPF_OPTS(bpf_tc_opts, opts1, .handle = 1, .priority = 1, @@ -126,23 +111,23 @@ static void cleanup(void)  static int config_underlay(void)  { -	SYS("ip netns add " NS0); -	SYS("ip netns add " NS1); -	SYS("ip netns add " NS2); +	SYS(fail, "ip netns add " NS0); +	SYS(fail, "ip netns add " NS1); +	SYS(fail, "ip netns add " NS2);  	/* NS0 <-> NS1 [veth01 <-> veth10] */ -	SYS("ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1); -	SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01"); -	SYS("ip -net " NS0 " link set dev veth01 up"); -	SYS("ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10"); -	SYS("ip -net " NS1 " link set dev veth10 up"); +	SYS(fail, "ip link add veth01 netns " NS0 " type veth peer name veth10 netns " NS1); +	SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH01 "/24 dev veth01"); +	SYS(fail, "ip -net " NS0 " link set dev veth01 up"); +	SYS(fail, "ip -net " NS1 " addr add " IP4_ADDR_VETH10 "/24 dev veth10"); +	SYS(fail, "ip -net " NS1 " link set dev veth10 up");  	/* NS0 <-> NS2 [veth02 <-> veth20] */ -	SYS("ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2); -	SYS("ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02"); -	SYS("ip -net " NS0 " link set dev veth02 up"); -	SYS("ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20"); -	SYS("ip -net " NS2 " link set dev veth20 up"); +	SYS(fail, "ip link add veth02 netns " NS0 " type veth peer name veth20 netns " NS2); +	SYS(fail, "ip -net " NS0 " addr add " IP4_ADDR_VETH02 "/24 dev veth02"); +	SYS(fail, "ip -net " NS0 " link set dev veth02 up"); +	SYS(fail, "ip -net " NS2 " addr add " IP4_ADDR_VETH20 "/24 dev veth20"); +	SYS(fail, "ip -net " NS2 " link set dev veth20 up");  	return 0;  fail: @@ -153,20 +138,20 @@ static int setup_xfrm_tunnel_ns(const char *ns, const char *ipv4_local,  				const char *ipv4_remote, int if_id)  {  	/* State: local -> remote */ -	SYS("ip -net %s xfrm state add src %s dst %s spi 1 " +	SYS(fail, "ip -net %s xfrm state add src %s dst %s spi 1 "  	    ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_local, ipv4_remote, if_id);  	/* State: local <- remote */ -	SYS("ip -net %s xfrm state add src %s dst %s spi 1 " +	SYS(fail, "ip -net %s xfrm state add src %s dst %s spi 1 "  	    ESP_DUMMY_PARAMS "if_id %d", ns, ipv4_remote, ipv4_local, if_id);  	/* Policy: local -> remote */ -	SYS("ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 " +	SYS(fail, "ip -net %s xfrm policy add dir out src 0.0.0.0/0 dst 0.0.0.0/0 "  	    "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,  	    if_id, ipv4_local, ipv4_remote, if_id);  	/* Policy: local <- remote */ -	SYS("ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 " +	SYS(fail, "ip -net %s xfrm policy add dir in src 0.0.0.0/0 dst 0.0.0.0/0 "  	    "if_id %d tmpl src %s dst %s proto esp mode tunnel if_id %d", ns,  	    if_id, ipv4_remote, ipv4_local, if_id); @@ -274,16 +259,16 @@ static int config_overlay(void)  	if (!ASSERT_OK(setup_xfrmi_external_dev(NS0), "xfrmi"))  		goto fail; -	SYS("ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0"); -	SYS("ip -net " NS0 " link set dev ipsec0 up"); +	SYS(fail, "ip -net " NS0 " addr add 192.168.1.100/24 dev ipsec0"); +	SYS(fail, "ip -net " NS0 " link set dev ipsec0 up"); -	SYS("ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1); -	SYS("ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0"); -	SYS("ip -net " NS1 " link set dev ipsec0 up"); +	SYS(fail, "ip -net " NS1 " link add ipsec0 type xfrm if_id %d", IF_ID_1); +	SYS(fail, "ip -net " NS1 " addr add 192.168.1.200/24 dev ipsec0"); +	SYS(fail, "ip -net " NS1 " link set dev ipsec0 up"); -	SYS("ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2); -	SYS("ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0"); -	SYS("ip -net " NS2 " link set dev ipsec0 up"); +	SYS(fail, "ip -net " NS2 " link add ipsec0 type xfrm if_id %d", IF_ID_2); +	SYS(fail, "ip -net " NS2 " addr add 192.168.1.200/24 dev ipsec0"); +	SYS(fail, "ip -net " NS2 " link set dev ipsec0 up");  	return 0;  fail: @@ -294,7 +279,7 @@ static int test_xfrm_ping(struct xfrm_info *skel, u32 if_id)  {  	skel->bss->req_if_id = if_id; -	SYS("ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null"); +	SYS(fail, "ping -i 0.01 -c 3 -w 10 -q 192.168.1.200 > /dev/null");  	if (!ASSERT_EQ(skel->bss->resp_if_id, if_id, "if_id"))  		goto fail; |