diff options
Diffstat (limited to 'tools/testing')
93 files changed, 5085 insertions, 1005 deletions
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index 153c3a181a4c..f4b23d697448 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -15,9 +15,9 @@ TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test  	test_align  TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o test_obj_id.o \ -	test_pkt_md_access.o +	test_pkt_md_access.o test_xdp_redirect.o sockmap_parse_prog.o sockmap_verdict_prog.o -TEST_PROGS := test_kmod.sh +TEST_PROGS := test_kmod.sh test_xdp_redirect.sh  include ../lib.mk diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h index d50ac342dc92..36fb9161b34a 100644 --- a/tools/testing/selftests/bpf/bpf_helpers.h +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -38,6 +38,8 @@ static int (*bpf_clone_redirect)(void *ctx, int ifindex, int flags) =  	(void *) BPF_FUNC_clone_redirect;  static int (*bpf_redirect)(int ifindex, int flags) =  	(void *) BPF_FUNC_redirect; +static int (*bpf_redirect_map)(void *map, int key, int flags) = +	(void *) BPF_FUNC_redirect_map;  static int (*bpf_perf_event_output)(void *ctx, void *map,  				    unsigned long long flags, void *data,  				    int size) = @@ -63,6 +65,12 @@ static int (*bpf_xdp_adjust_head)(void *ctx, int offset) =  static int (*bpf_setsockopt)(void *ctx, int level, int optname, void *optval,  			     int optlen) =  	(void *) BPF_FUNC_setsockopt; +static int (*bpf_sk_redirect_map)(void *map, int key, int flags) = +	(void *) BPF_FUNC_sk_redirect_map; +static int (*bpf_sock_map_update)(void *map, void *key, void *value, +				  unsigned long long flags) = +	(void *) BPF_FUNC_sock_map_update; +  /* llvm builtin functions that eBPF C program may use to   * emit BPF_LD_ABS and BPF_LD_IND instructions @@ -85,6 +93,7 @@ struct bpf_map_def {  	unsigned int max_entries;  	unsigned int map_flags;  	unsigned int inner_map_idx; +	unsigned int numa_node;  };  static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = diff --git a/tools/testing/selftests/bpf/sockmap_parse_prog.c b/tools/testing/selftests/bpf/sockmap_parse_prog.c new file mode 100644 index 000000000000..fae3b96c3aa4 --- /dev/null +++ b/tools/testing/selftests/bpf/sockmap_parse_prog.c @@ -0,0 +1,38 @@ +#include <linux/bpf.h> +#include "bpf_helpers.h" +#include "bpf_util.h" +#include "bpf_endian.h" + +int _version SEC("version") = 1; + +#define bpf_printk(fmt, ...)					\ +({								\ +	       char ____fmt[] = fmt;				\ +	       bpf_trace_printk(____fmt, sizeof(____fmt),	\ +				##__VA_ARGS__);			\ +}) + +SEC("sk_skb1") +int bpf_prog1(struct __sk_buff *skb) +{ +	void *data_end = (void *)(long) skb->data_end; +	void *data = (void *)(long) skb->data; +	__u32 lport = skb->local_port; +	__u32 rport = skb->remote_port; +	__u8 *d = data; + +	if (data + 10 > data_end) +		return skb->len; + +	/* This write/read is a bit pointless but tests the verifier and +	 * strparser handler for read/write pkt data and access into sk +	 * fields. +	 */ +	d[7] = 1; + +	bpf_printk("parse: data[0] = (%u): local_port %i remote %i\n", +		   d[0], lport, bpf_ntohl(rport)); +	return skb->len; +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/sockmap_verdict_prog.c b/tools/testing/selftests/bpf/sockmap_verdict_prog.c new file mode 100644 index 000000000000..9b99bd10807d --- /dev/null +++ b/tools/testing/selftests/bpf/sockmap_verdict_prog.c @@ -0,0 +1,68 @@ +#include <linux/bpf.h> +#include "bpf_helpers.h" +#include "bpf_util.h" +#include "bpf_endian.h" + +int _version SEC("version") = 1; + +#define bpf_printk(fmt, ...)					\ +({								\ +	       char ____fmt[] = fmt;				\ +	       bpf_trace_printk(____fmt, sizeof(____fmt),	\ +				##__VA_ARGS__);			\ +}) + +struct bpf_map_def SEC("maps") sock_map_rx = { +	.type = BPF_MAP_TYPE_SOCKMAP, +	.key_size = sizeof(int), +	.value_size = sizeof(int), +	.max_entries = 20, +}; + +struct bpf_map_def SEC("maps") sock_map_tx = { +	.type = BPF_MAP_TYPE_SOCKMAP, +	.key_size = sizeof(int), +	.value_size = sizeof(int), +	.max_entries = 20, +}; + +struct bpf_map_def SEC("maps") sock_map_break = { +	.type = BPF_MAP_TYPE_ARRAY, +	.key_size = sizeof(int), +	.value_size = sizeof(int), +	.max_entries = 20, +}; + +SEC("sk_skb2") +int bpf_prog2(struct __sk_buff *skb) +{ +	void *data_end = (void *)(long) skb->data_end; +	void *data = (void *)(long) skb->data; +	__u32 lport = skb->local_port; +	__u32 rport = skb->remote_port; +	__u8 *d = data; +	__u8 sk, map; + +	if (data + 8 > data_end) +		return SK_DROP; + +	map = d[0]; +	sk = d[1]; + +	d[0] = 0xd; +	d[1] = 0xe; +	d[2] = 0xa; +	d[3] = 0xd; +	d[4] = 0xb; +	d[5] = 0xe; +	d[6] = 0xe; +	d[7] = 0xf; + +	bpf_printk("verdict: data[0] = redir(%u:%u)\n", map, sk); + +	if (!map) +		return bpf_sk_redirect_map(&sock_map_rx, sk, 0); +	return bpf_sk_redirect_map(&sock_map_tx, sk, 0); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_align.c b/tools/testing/selftests/bpf/test_align.c index 29793694cbc7..8591c89c0828 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -27,6 +27,11 @@  #define MAX_INSNS	512  #define MAX_MATCHES	16 +struct bpf_reg_match { +	unsigned int line; +	const char *match; +}; +  struct bpf_align_test {  	const char *descr;  	struct bpf_insn	insns[MAX_INSNS]; @@ -36,10 +41,14 @@ struct bpf_align_test {  		REJECT  	} result;  	enum bpf_prog_type prog_type; -	const char *matches[MAX_MATCHES]; +	/* Matches must be in order of increasing line */ +	struct bpf_reg_match matches[MAX_MATCHES];  };  static struct bpf_align_test tests[] = { +	/* Four tests of known constants.  These aren't staggeringly +	 * interesting since we track exact values now. +	 */  	{  		.descr = "mov",  		.insns = { @@ -53,11 +62,13 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"1: R1=ctx R3=imm2,min_value=2,max_value=2,min_align=2 R10=fp", -			"2: R1=ctx R3=imm4,min_value=4,max_value=4,min_align=4 R10=fp", -			"3: R1=ctx R3=imm8,min_value=8,max_value=8,min_align=8 R10=fp", -			"4: R1=ctx R3=imm16,min_value=16,max_value=16,min_align=16 R10=fp", -			"5: R1=ctx R3=imm32,min_value=32,max_value=32,min_align=32 R10=fp", +			{1, "R1=ctx(id=0,off=0,imm=0)"}, +			{1, "R10=fp0"}, +			{1, "R3=inv2"}, +			{2, "R3=inv4"}, +			{3, "R3=inv8"}, +			{4, "R3=inv16"}, +			{5, "R3=inv32"},  		},  	},  	{ @@ -79,17 +90,19 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"1: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R10=fp", -			"2: R1=ctx R3=imm2,min_value=2,max_value=2,min_align=2 R10=fp", -			"3: R1=ctx R3=imm4,min_value=4,max_value=4,min_align=4 R10=fp", -			"4: R1=ctx R3=imm8,min_value=8,max_value=8,min_align=8 R10=fp", -			"5: R1=ctx R3=imm16,min_value=16,max_value=16,min_align=16 R10=fp", -			"6: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R10=fp", -			"7: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R4=imm32,min_value=32,max_value=32,min_align=32 R10=fp", -			"8: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R4=imm16,min_value=16,max_value=16,min_align=16 R10=fp", -			"9: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R4=imm8,min_value=8,max_value=8,min_align=8 R10=fp", -			"10: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R4=imm4,min_value=4,max_value=4,min_align=4 R10=fp", -			"11: R1=ctx R3=imm1,min_value=1,max_value=1,min_align=1 R4=imm2,min_value=2,max_value=2,min_align=2 R10=fp", +			{1, "R1=ctx(id=0,off=0,imm=0)"}, +			{1, "R10=fp0"}, +			{1, "R3=inv1"}, +			{2, "R3=inv2"}, +			{3, "R3=inv4"}, +			{4, "R3=inv8"}, +			{5, "R3=inv16"}, +			{6, "R3=inv1"}, +			{7, "R4=inv32"}, +			{8, "R4=inv16"}, +			{9, "R4=inv8"}, +			{10, "R4=inv4"}, +			{11, "R4=inv2"},  		},  	},  	{ @@ -106,12 +119,14 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"1: R1=ctx R3=imm4,min_value=4,max_value=4,min_align=4 R10=fp", -			"2: R1=ctx R3=imm8,min_value=8,max_value=8,min_align=4 R10=fp", -			"3: R1=ctx R3=imm10,min_value=10,max_value=10,min_align=2 R10=fp", -			"4: R1=ctx R3=imm10,min_value=10,max_value=10,min_align=2 R4=imm8,min_value=8,max_value=8,min_align=8 R10=fp", -			"5: R1=ctx R3=imm10,min_value=10,max_value=10,min_align=2 R4=imm12,min_value=12,max_value=12,min_align=4 R10=fp", -			"6: R1=ctx R3=imm10,min_value=10,max_value=10,min_align=2 R4=imm14,min_value=14,max_value=14,min_align=2 R10=fp", +			{1, "R1=ctx(id=0,off=0,imm=0)"}, +			{1, "R10=fp0"}, +			{1, "R3=inv4"}, +			{2, "R3=inv8"}, +			{3, "R3=inv10"}, +			{4, "R4=inv8"}, +			{5, "R4=inv12"}, +			{6, "R4=inv14"},  		},  	},  	{ @@ -126,13 +141,16 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"1: R1=ctx R3=imm7,min_value=7,max_value=7,min_align=1 R10=fp", -			"2: R1=ctx R3=imm7,min_value=7,max_value=7,min_align=1 R10=fp", -			"3: R1=ctx R3=imm14,min_value=14,max_value=14,min_align=2 R10=fp", -			"4: R1=ctx R3=imm56,min_value=56,max_value=56,min_align=4 R10=fp", +			{1, "R1=ctx(id=0,off=0,imm=0)"}, +			{1, "R10=fp0"}, +			{1, "R3=inv7"}, +			{2, "R3=inv7"}, +			{3, "R3=inv14"}, +			{4, "R3=inv56"},  		},  	}, +	/* Tests using unknown values */  #define PREP_PKT_POINTERS \  	BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, \  		    offsetof(struct __sk_buff, data)), \ @@ -166,17 +184,19 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"7: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R10=fp", -			"8: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv55,min_align=2 R10=fp", -			"9: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv54,min_align=4 R10=fp", -			"10: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv53,min_align=8 R10=fp", -			"11: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv52,min_align=16 R10=fp", -			"18: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv56 R10=fp", -			"19: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv51,min_align=32 R10=fp", -			"20: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv52,min_align=16 R10=fp", -			"21: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv53,min_align=8 R10=fp", -			"22: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv54,min_align=4 R10=fp", -			"23: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv55,min_align=2 R10=fp", +			{7, "R0=pkt(id=0,off=8,r=8,imm=0)"}, +			{7, "R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{8, "R3=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, +			{9, "R3=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{10, "R3=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, +			{11, "R3=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, +			{18, "R3=pkt_end(id=0,off=0,imm=0)"}, +			{18, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{19, "R4=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"}, +			{20, "R4=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"}, +			{21, "R4=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, +			{22, "R4=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{23, "R4=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},  		},  	},  	{ @@ -197,16 +217,16 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"7: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R10=fp", -			"8: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv56 R10=fp", -			"9: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv55,min_align=1 R10=fp", -			"10: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv56 R10=fp", -			"11: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv54,min_align=2 R10=fp", -			"12: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv56 R10=fp", -			"13: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv53,min_align=4 R10=fp", -			"14: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv56 R10=fp", -			"15: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv52,min_align=8 R10=fp", -			"16: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=inv56 R4=inv50,min_align=8 R10=fp" +			{7, "R3=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{8, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{9, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{10, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{11, "R4=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"}, +			{12, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{13, "R4=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{14, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{15, "R4=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"}, +			{16, "R4=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},  		},  	},  	{ @@ -237,12 +257,14 @@ static struct bpf_align_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.matches = { -			"4: R0=imm0,min_value=0,max_value=0,min_align=2147483648 R1=ctx R2=pkt(id=0,off=0,r=0) R3=pkt_end R5=pkt(id=0,off=0,r=0) R10=fp", -			"5: R0=imm0,min_value=0,max_value=0,min_align=2147483648 R1=ctx R2=pkt(id=0,off=0,r=0) R3=pkt_end R5=pkt(id=0,off=14,r=0) R10=fp", -			"6: R0=imm0,min_value=0,max_value=0,min_align=2147483648 R1=ctx R2=pkt(id=0,off=0,r=0) R3=pkt_end R4=pkt(id=0,off=14,r=0) R5=pkt(id=0,off=14,r=0) R10=fp", -			"10: R0=imm0,min_value=0,max_value=0,min_align=2147483648 R1=ctx R2=pkt(id=0,off=0,r=18) R3=pkt_end R4=inv56 R5=pkt(id=0,off=14,r=18) R10=fp", -			"14: R0=imm0,min_value=0,max_value=0,min_align=2147483648 R1=ctx R2=pkt(id=0,off=0,r=18) R3=pkt_end R4=inv48 R5=pkt(id=0,off=14,r=18) R10=fp", -			"15: R0=imm0,min_value=0,max_value=0,min_align=2147483648 R1=ctx R2=pkt(id=0,off=0,r=18) R3=pkt_end R4=inv48 R5=pkt(id=0,off=14,r=18) R10=fp", +			{4, "R5=pkt(id=0,off=0,r=0,imm=0)"}, +			{5, "R5=pkt(id=0,off=14,r=0,imm=0)"}, +			{6, "R4=pkt(id=0,off=14,r=0,imm=0)"}, +			{10, "R2=pkt(id=0,off=0,r=18,imm=0)"}, +			{10, "R5=pkt(id=0,off=14,r=18,imm=0)"}, +			{10, "R4=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"}, +			{14, "R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"}, +			{15, "R4=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},  		},  	},  	{ @@ -297,62 +319,286 @@ static struct bpf_align_test tests[] = {  			/* Calculated offset in R6 has unknown value, but known  			 * alignment of 4.  			 */ -			"8: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R6=inv54,min_align=4 R10=fp", - -			/* Offset is added to packet pointer R5, resulting in known -			 * auxiliary alignment and offset. +			{8, "R2=pkt(id=0,off=0,r=8,imm=0)"}, +			{8, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* Offset is added to packet pointer R5, resulting in +			 * known fixed offset, and variable offset from R6.  			 */ -			"11: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R5=pkt(id=1,off=0,r=0),aux_off=14,aux_off_align=4 R6=inv54,min_align=4 R10=fp", - +			{11, "R5=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* At the time the word size load is performed from R5,  			 * it's total offset is NET_IP_ALIGN + reg->off (0) +  			 * reg->aux_off (14) which is 16.  Then the variable  			 * offset is considered using reg->aux_off_align which  			 * is 4 and meets the load's requirements.  			 */ -			"15: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=pkt(id=1,off=4,r=4),aux_off=14,aux_off_align=4 R5=pkt(id=1,off=0,r=4),aux_off=14,aux_off_align=4 R6=inv54,min_align=4 R10=fp", - - +			{15, "R4=pkt(id=1,off=18,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{15, "R5=pkt(id=1,off=14,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Variable offset is added to R5 packet pointer,  			 * resulting in auxiliary alignment of 4.  			 */ -			"18: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv,aux_off=14,aux_off_align=4 R5=pkt(id=2,off=0,r=0),aux_off_align=4 R6=inv54,min_align=4 R10=fp", - +			{18, "R5=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Constant offset is added to R5, resulting in  			 * reg->off of 14.  			 */ -			"19: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv,aux_off=14,aux_off_align=4 R5=pkt(id=2,off=14,r=0),aux_off_align=4 R6=inv54,min_align=4 R10=fp", - +			{19, "R5=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* At the time the word size load is performed from R5, -			 * it's total offset is NET_IP_ALIGN + reg->off (14) which -			 * is 16.  Then the variable offset is considered using -			 * reg->aux_off_align which is 4 and meets the load's -			 * requirements. +			 * its total fixed offset is NET_IP_ALIGN + reg->off +			 * (14) which is 16.  Then the variable offset is 4-byte +			 * aligned, so the total offset is 4-byte aligned and +			 * meets the load's requirements.  			 */ -			"23: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=pkt(id=2,off=18,r=18),aux_off_align=4 R5=pkt(id=2,off=14,r=18),aux_off_align=4 R6=inv54,min_align=4 R10=fp", - +			{23, "R4=pkt(id=2,off=18,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			{23, "R5=pkt(id=2,off=14,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"},  			/* Constant offset is added to R5 packet pointer,  			 * resulting in reg->off value of 14.  			 */ -			"26: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv,aux_off_align=4 R5=pkt(id=0,off=14,r=8) R6=inv54,min_align=4 R10=fp", -			/* Variable offset is added to R5, resulting in an -			 * auxiliary offset of 14, and an auxiliary alignment of 4. +			{26, "R5=pkt(id=0,off=14,r=8"}, +			/* Variable offset is added to R5, resulting in a +			 * variable offset of (4n).  			 */ -			"27: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv,aux_off_align=4 R5=pkt(id=3,off=0,r=0),aux_off=14,aux_off_align=4 R6=inv54,min_align=4 R10=fp", -			/* Constant is added to R5 again, setting reg->off to 4. */ -			"28: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv,aux_off_align=4 R5=pkt(id=3,off=4,r=0),aux_off=14,aux_off_align=4 R6=inv54,min_align=4 R10=fp", -			/* And once more we add a variable, which causes an accumulation -			 * of reg->off into reg->aux_off_align, with resulting value of -			 * 18.  The auxiliary alignment stays at 4. +			{27, "R5=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* Constant is added to R5 again, setting reg->off to 18. */ +			{28, "R5=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* And once more we add a variable; resulting var_off +			 * is still (4n), fixed offset is not changed. +			 * Also, we create a new reg->id.  			 */ -			"29: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=inv,aux_off_align=4 R5=pkt(id=4,off=0,r=0),aux_off=18,aux_off_align=4 R6=inv54,min_align=4 R10=fp", +			{29, "R5=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc))"},  			/* At the time the word size load is performed from R5, -			 * it's total offset is NET_IP_ALIGN + reg->off (0) + -			 * reg->aux_off (18) which is 20.  Then the variable offset -			 * is considered using reg->aux_off_align which is 4 and meets -			 * the load's requirements. +			 * its total fixed offset is NET_IP_ALIGN + reg->off (18) +			 * which is 20.  Then the variable offset is (4n), so +			 * the total offset is 4-byte aligned and meets the +			 * load's requirements. +			 */ +			{33, "R4=pkt(id=4,off=22,r=22,umax_value=2040,var_off=(0x0; 0x7fc))"}, +			{33, "R5=pkt(id=4,off=18,r=22,umax_value=2040,var_off=(0x0; 0x7fc))"}, +		}, +	}, +	{ +		.descr = "packet variable offset 2", +		.insns = { +			/* Create an unknown offset, (4n+2)-aligned */ +			LOAD_UNKNOWN(BPF_REG_6), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14), +			/* Add it to the packet pointer */ +			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6), +			/* Check bounds and perform a read */ +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), +			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0), +			/* Make a (4n) offset from the value we just read */ +			BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xff), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2), +			/* Add it to the packet pointer */ +			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6), +			/* Check bounds and perform a read */ +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), +			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.matches = { +			/* Calculated offset in R6 has unknown value, but known +			 * alignment of 4. +			 */ +			{8, "R2=pkt(id=0,off=0,r=8,imm=0)"}, +			{8, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* Adding 14 makes R6 be (4n+2) */ +			{9, "R6=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			/* Packet pointer has (4n+2) offset */ +			{11, "R5=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			{13, "R4=pkt(id=1,off=4,r=0,umin_value=14,umax_value=1034,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. +			 */ +			{15, "R5=pkt(id=1,off=0,r=4,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			/* Newly read value in R6 was shifted left by 2, so has +			 * known alignment of 4. +			 */ +			{18, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* Added (4n) to packet pointer's (4n+2) var_off, giving +			 * another (4n+2). +			 */ +			{19, "R5=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"}, +			{21, "R4=pkt(id=2,off=4,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"}, +			/* 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. +			 */ +			{23, "R5=pkt(id=2,off=0,r=4,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"}, +		}, +	}, +	{ +		.descr = "dubious pointer arithmetic", +		.insns = { +			PREP_PKT_POINTERS, +			BPF_MOV64_IMM(BPF_REG_0, 0), +			/* ptr & const => unknown & const */ +			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), +			BPF_ALU64_IMM(BPF_AND, BPF_REG_5, 0x40), +			/* ptr << const => unknown << const */ +			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2), +			/* We have a (4n) value.  Let's make a packet offset +			 * out of it.  First add 14, to make it a (4n+2) +			 */ +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14), +			/* Then make sure it's nonnegative */ +			BPF_JMP_IMM(BPF_JSGE, BPF_REG_5, 0, 1), +			BPF_EXIT_INSN(), +			/* Add it to packet pointer */ +			BPF_MOV64_REG(BPF_REG_6, BPF_REG_2), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5), +			/* Check bounds and perform a read */ +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_6), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), +			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_6, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.result = REJECT, +		.matches = { +			{4, "R5=pkt(id=0,off=0,r=0,imm=0)"}, +			/* ptr & 0x40 == either 0 or 0x40 */ +			{5, "R5=inv(id=0,umax_value=64,var_off=(0x0; 0x40))"}, +			/* ptr << 2 == unknown, (4n) */ +			{7, "R5=inv(id=0,smax_value=9223372036854775804,umax_value=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc))"}, +			/* (4n) + 14 == (4n+2).  We blow our bounds, because +			 * the add could overflow. +			 */ +			{8, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"}, +			/* Checked s>=0 */ +			{10, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +			/* packet pointer + nonnegative (4n+2) */ +			{12, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +			{14, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +			/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine. +			 * We checked the bounds, but it might have been able +			 * to overflow if the packet pointer started in the +			 * upper half of the address space. +			 * So we did not get a 'range' on R6, and the access +			 * attempt will fail. +			 */ +			{16, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"}, +		} +	}, +	{ +		.descr = "variable subtraction", +		.insns = { +			/* Create an unknown offset, (4n+2)-aligned */ +			LOAD_UNKNOWN(BPF_REG_6), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14), +			/* Create another unknown, (4n)-aligned, and subtract +			 * it from the first one +			 */ +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2), +			BPF_ALU64_REG(BPF_SUB, BPF_REG_6, BPF_REG_7), +			/* Bounds-check the result */ +			BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 1), +			BPF_EXIT_INSN(), +			/* Add it to the packet pointer */ +			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6), +			/* Check bounds and perform a read */ +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), +			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.matches = { +			/* Calculated offset in R6 has unknown value, but known +			 * alignment of 4. +			 */ +			{7, "R2=pkt(id=0,off=0,r=8,imm=0)"}, +			{9, "R6=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* Adding 14 makes R6 be (4n+2) */ +			{10, "R6=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"}, +			/* New unknown value in R7 is (4n) */ +			{11, "R7=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"}, +			/* Subtracting it from R6 blows our unsigned bounds */ +			{12, "R6=inv(id=0,smin_value=-1006,smax_value=1034,var_off=(0x2; 0xfffffffffffffffc))"}, +			/* Checked s>= 0 */ +			{14, "R6=inv(id=0,umin_value=2,umax_value=1034,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=1,off=0,r=4,umin_value=2,umax_value=1034,var_off=(0x2; 0x7fc))"}, +		}, +	}, +	{ +		.descr = "pointer variable subtraction", +		.insns = { +			/* Create an unknown offset, (4n+2)-aligned and bounded +			 * to [14,74] +			 */ +			LOAD_UNKNOWN(BPF_REG_6), +			BPF_MOV64_REG(BPF_REG_7, BPF_REG_6), +			BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xf), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14), +			/* Subtract it from the packet pointer */ +			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2), +			BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_6), +			/* Create another unknown, (4n)-aligned and >= 74. +			 * That in fact means >= 76, since 74 % 4 == 2  			 */ -			"33: R0=pkt(id=0,off=8,r=8) R1=ctx R2=pkt(id=0,off=0,r=8) R3=pkt_end R4=pkt(id=4,off=4,r=4),aux_off=18,aux_off_align=4 R5=pkt(id=4,off=0,r=4),aux_off=18,aux_off_align=4 R6=inv54,min_align=4 R10=fp", +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 76), +			/* Add it to the packet pointer */ +			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_7), +			/* Check bounds and perform a read */ +			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4), +			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0), +			BPF_EXIT_INSN(), +		}, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +		.matches = { +			/* Calculated offset in R6 has unknown value, but known +			 * alignment of 4. +			 */ +			{7, "R2=pkt(id=0,off=0,r=8,imm=0)"}, +			{10, "R6=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"}, +			/* Adding 14 makes R6 be (4n+2) */ +			{11, "R6=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"}, +			/* Subtracting from packet pointer overflows ubounds */ +			{13, "R5=pkt(id=1,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c))"}, +			/* New unknown value in R7 is (4n), >= 76 */ +			{15, "R7=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"}, +			/* Adding it to packet pointer gives nice bounds again */ +			{16, "R5=pkt(id=2,off=0,r=0,umin_value=2,umax_value=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=2,off=0,r=4,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"},  		},  	},  }; @@ -373,6 +619,9 @@ static int do_test_single(struct bpf_align_test *test)  {  	struct bpf_insn *prog = test->insns;  	int prog_type = test->prog_type; +	char bpf_vlog_copy[32768]; +	const char *line_ptr; +	int cur_line = -1;  	int prog_len, i;  	int fd_prog;  	int ret; @@ -381,26 +630,49 @@ static int do_test_single(struct bpf_align_test *test)  	fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,  				     prog, prog_len, 1, "GPL", 0,  				     bpf_vlog, sizeof(bpf_vlog), 2); -	if (fd_prog < 0) { +	if (fd_prog < 0 && test->result != REJECT) {  		printf("Failed to load program.\n");  		printf("%s", bpf_vlog);  		ret = 1; +	} else if (fd_prog >= 0 && test->result == REJECT) { +		printf("Unexpected success to load!\n"); +		printf("%s", bpf_vlog); +		ret = 1; +		close(fd_prog);  	} else {  		ret = 0; +		/* We make a local copy so that we can strtok() it */ +		strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy)); +		line_ptr = strtok(bpf_vlog_copy, "\n");  		for (i = 0; i < MAX_MATCHES; i++) { -			const char *t, *m = test->matches[i]; +			struct bpf_reg_match m = test->matches[i]; -			if (!m) +			if (!m.match)  				break; -			t = strstr(bpf_vlog, m); -			if (!t) { -				printf("Failed to find match: %s\n", m); +			while (line_ptr) { +				cur_line = -1; +				sscanf(line_ptr, "%u: ", &cur_line); +				if (cur_line == m.line) +					break; +				line_ptr = strtok(NULL, "\n"); +			} +			if (!line_ptr) { +				printf("Failed to find line %u for match: %s\n", +				       m.line, m.match); +				ret = 1; +				printf("%s", bpf_vlog); +				break; +			} +			if (!strstr(line_ptr, m.match)) { +				printf("Failed to find match %u: %s\n", +				       m.line, m.match);  				ret = 1;  				printf("%s", bpf_vlog);  				break;  			}  		} -		close(fd_prog); +		if (fd_prog >= 0) +			close(fd_prog);  	}  	return ret;  } diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 79601c81e169..4acc772a28c0 100644 --- a/tools/testing/selftests/bpf/test_maps.c +++ b/tools/testing/selftests/bpf/test_maps.c @@ -22,6 +22,7 @@  #include <linux/bpf.h>  #include <bpf/bpf.h> +#include <bpf/libbpf.h>  #include "bpf_util.h"  static int map_flags; @@ -438,6 +439,395 @@ static void test_arraymap_percpu_many_keys(void)  	close(fd);  } +static void test_devmap(int task, void *data) +{ +	int fd; +	__u32 key, value; + +	fd = bpf_create_map(BPF_MAP_TYPE_DEVMAP, sizeof(key), sizeof(value), +			    2, 0); +	if (fd < 0) { +		printf("Failed to create arraymap '%s'!\n", strerror(errno)); +		exit(1); +	} + +	close(fd); +} + +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <sys/select.h> +#include <linux/err.h> +#define SOCKMAP_PARSE_PROG "./sockmap_parse_prog.o" +#define SOCKMAP_VERDICT_PROG "./sockmap_verdict_prog.o" +static void test_sockmap(int tasks, void *data) +{ +	int one = 1, map_fd_rx, map_fd_tx, map_fd_break, s, sc, rc; +	struct bpf_map *bpf_map_rx, *bpf_map_tx, *bpf_map_break; +	int ports[] = {50200, 50201, 50202, 50204}; +	int err, i, fd, sfd[6] = {0xdeadbeef}; +	u8 buf[20] = {0x0, 0x5, 0x3, 0x2, 0x1, 0x0}; +	int parse_prog, verdict_prog; +	struct sockaddr_in addr; +	struct bpf_object *obj; +	struct timeval to; +	__u32 key, value; +	pid_t pid[tasks]; +	fd_set w; + +	/* Create some sockets to use with sockmap */ +	for (i = 0; i < 2; i++) { +		sfd[i] = socket(AF_INET, SOCK_STREAM, 0); +		if (sfd[i] < 0) +			goto out; +		err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR, +				 (char *)&one, sizeof(one)); +		if (err) { +			printf("failed to setsockopt\n"); +			goto out; +		} +		err = ioctl(sfd[i], FIONBIO, (char *)&one); +		if (err < 0) { +			printf("failed to ioctl\n"); +			goto out; +		} +		memset(&addr, 0, sizeof(struct sockaddr_in)); +		addr.sin_family = AF_INET; +		addr.sin_addr.s_addr = inet_addr("127.0.0.1"); +		addr.sin_port = htons(ports[i]); +		err = bind(sfd[i], (struct sockaddr *)&addr, sizeof(addr)); +		if (err < 0) { +			printf("failed to bind: err %i: %i:%i\n", +			       err, i, sfd[i]); +			goto out; +		} +		err = listen(sfd[i], 32); +		if (err < 0) { +			printf("failed to listen\n"); +			goto out; +		} +	} + +	for (i = 2; i < 4; i++) { +		sfd[i] = socket(AF_INET, SOCK_STREAM, 0); +		if (sfd[i] < 0) +			goto out; +		err = setsockopt(sfd[i], SOL_SOCKET, SO_REUSEADDR, +				 (char *)&one, sizeof(one)); +		if (err) { +			printf("set sock opt\n"); +			goto out; +		} +		memset(&addr, 0, sizeof(struct sockaddr_in)); +		addr.sin_family = AF_INET; +		addr.sin_addr.s_addr = inet_addr("127.0.0.1"); +		addr.sin_port = htons(ports[i - 2]); +		err = connect(sfd[i], (struct sockaddr *)&addr, sizeof(addr)); +		if (err) { +			printf("failed to connect\n"); +			goto out; +		} +	} + + +	for (i = 4; i < 6; i++) { +		sfd[i] = accept(sfd[i - 4], NULL, NULL); +		if (sfd[i] < 0) { +			printf("accept failed\n"); +			goto out; +		} +	} + +	/* Test sockmap with connected sockets */ +	fd = bpf_create_map(BPF_MAP_TYPE_SOCKMAP, +			    sizeof(key), sizeof(value), +			    6, 0); +	if (fd < 0) { +		printf("Failed to create sockmap %i\n", fd); +		goto out_sockmap; +	} + +	/* Test update without programs */ +	for (i = 0; i < 6; i++) { +		err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); +		if (err) { +			printf("Failed noprog update sockmap '%i:%i'\n", +			       i, sfd[i]); +			goto out_sockmap; +		} +	} + +	/* Test attaching bad fds */ +	err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_PARSER, 0); +	if (!err) { +		printf("Failed invalid parser prog attach\n"); +		goto out_sockmap; +	} + +	err = bpf_prog_attach(-1, fd, BPF_SK_SKB_STREAM_VERDICT, 0); +	if (!err) { +		printf("Failed invalid verdict prog attach\n"); +		goto out_sockmap; +	} + +	/* Load SK_SKB program and Attach */ +	err = bpf_prog_load(SOCKMAP_PARSE_PROG, +			    BPF_PROG_TYPE_SK_SKB, &obj, &parse_prog); +	if (err) { +		printf("Failed to load SK_SKB parse prog\n"); +		goto out_sockmap; +	} + +	err = bpf_prog_load(SOCKMAP_VERDICT_PROG, +			    BPF_PROG_TYPE_SK_SKB, &obj, &verdict_prog); +	if (err) { +		printf("Failed to load SK_SKB verdict prog\n"); +		goto out_sockmap; +	} + +	bpf_map_rx = bpf_object__find_map_by_name(obj, "sock_map_rx"); +	if (IS_ERR(bpf_map_rx)) { +		printf("Failed to load map rx from verdict prog\n"); +		goto out_sockmap; +	} + +	map_fd_rx = bpf_map__fd(bpf_map_rx); +	if (map_fd_rx < 0) { +		printf("Failed to get map fd\n"); +		goto out_sockmap; +	} + +	bpf_map_tx = bpf_object__find_map_by_name(obj, "sock_map_tx"); +	if (IS_ERR(bpf_map_tx)) { +		printf("Failed to load map tx from verdict prog\n"); +		goto out_sockmap; +	} + +	map_fd_tx = bpf_map__fd(bpf_map_tx); +	if (map_fd_tx < 0) { +		printf("Failed to get map tx fd\n"); +		goto out_sockmap; +	} + +	bpf_map_break = bpf_object__find_map_by_name(obj, "sock_map_break"); +	if (IS_ERR(bpf_map_break)) { +		printf("Failed to load map tx from verdict prog\n"); +		goto out_sockmap; +	} + +	map_fd_break = bpf_map__fd(bpf_map_break); +	if (map_fd_break < 0) { +		printf("Failed to get map tx fd\n"); +		goto out_sockmap; +	} + +	err = bpf_prog_attach(parse_prog, map_fd_break, +			      BPF_SK_SKB_STREAM_PARSER, 0); +	if (!err) { +		printf("Allowed attaching SK_SKB program to invalid map\n"); +		goto out_sockmap; +	} + +	err = bpf_prog_attach(parse_prog, map_fd_rx, +		      BPF_SK_SKB_STREAM_PARSER, 0); +	if (err) { +		printf("Failed stream parser bpf prog attach\n"); +		goto out_sockmap; +	} + +	err = bpf_prog_attach(verdict_prog, map_fd_rx, +			      BPF_SK_SKB_STREAM_VERDICT, 0); +	if (err) { +		printf("Failed stream verdict bpf prog attach\n"); +		goto out_sockmap; +	} + +	/* Test map update elem afterwards fd lives in fd and map_fd */ +	for (i = 0; i < 6; i++) { +		err = bpf_map_update_elem(map_fd_rx, &i, &sfd[i], BPF_ANY); +		if (err) { +			printf("Failed map_fd_rx update sockmap %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +		err = bpf_map_update_elem(map_fd_tx, &i, &sfd[i], BPF_ANY); +		if (err) { +			printf("Failed map_fd_tx update sockmap %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +	} + +	/* Test map delete elem and remove send/recv sockets */ +	for (i = 2; i < 4; i++) { +		err = bpf_map_delete_elem(map_fd_rx, &i); +		if (err) { +			printf("Failed delete sockmap rx %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +		err = bpf_map_delete_elem(map_fd_tx, &i); +		if (err) { +			printf("Failed delete sockmap tx %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +	} + +	/* Test map send/recv */ +	for (i = 0; i < 2; i++) { +		buf[0] = i; +		buf[1] = 0x5; +		sc = send(sfd[2], buf, 20, 0); +		if (sc < 0) { +			printf("Failed sockmap send\n"); +			goto out_sockmap; +		} + +		FD_ZERO(&w); +		FD_SET(sfd[3], &w); +		to.tv_sec = 1; +		to.tv_usec = 0; +		s = select(sfd[3] + 1, &w, NULL, NULL, &to); +		if (s == -1) { +			perror("Failed sockmap select()"); +			goto out_sockmap; +		} else if (!s) { +			printf("Failed sockmap unexpected timeout\n"); +			goto out_sockmap; +		} + +		if (!FD_ISSET(sfd[3], &w)) { +			printf("Failed sockmap select/recv\n"); +			goto out_sockmap; +		} + +		rc = recv(sfd[3], buf, sizeof(buf), 0); +		if (rc < 0) { +			printf("Failed sockmap recv\n"); +			goto out_sockmap; +		} +	} + +	/* Negative null entry lookup from datapath should be dropped */ +	buf[0] = 1; +	buf[1] = 12; +	sc = send(sfd[2], buf, 20, 0); +	if (sc < 0) { +		printf("Failed sockmap send\n"); +		goto out_sockmap; +	} + +	/* Push fd into same slot */ +	i = 2; +	err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST); +	if (!err) { +		printf("Failed allowed sockmap dup slot BPF_NOEXIST\n"); +		goto out_sockmap; +	} + +	err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); +	if (err) { +		printf("Failed sockmap update new slot BPF_ANY\n"); +		goto out_sockmap; +	} + +	err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST); +	if (err) { +		printf("Failed sockmap update new slot BPF_EXIST\n"); +		goto out_sockmap; +	} + +	/* Delete the elems without programs */ +	for (i = 0; i < 6; i++) { +		err = bpf_map_delete_elem(fd, &i); +		if (err) { +			printf("Failed delete sockmap %i '%i:%i'\n", +			       err, i, sfd[i]); +		} +	} + +	/* Test having multiple maps open and set with programs on same fds */ +	err = bpf_prog_attach(parse_prog, fd, +			      BPF_SK_SKB_STREAM_PARSER, 0); +	if (err) { +		printf("Failed fd bpf parse prog attach\n"); +		goto out_sockmap; +	} +	err = bpf_prog_attach(verdict_prog, fd, +			      BPF_SK_SKB_STREAM_VERDICT, 0); +	if (err) { +		printf("Failed fd bpf verdict prog attach\n"); +		goto out_sockmap; +	} + +	for (i = 4; i < 6; i++) { +		err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_ANY); +		if (!err) { +			printf("Failed allowed duplicate programs in update ANY sockmap %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +		err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_NOEXIST); +		if (!err) { +			printf("Failed allowed duplicate program in update NOEXIST sockmap  %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +		err = bpf_map_update_elem(fd, &i, &sfd[i], BPF_EXIST); +		if (!err) { +			printf("Failed allowed duplicate program in update EXIST sockmap  %i '%i:%i'\n", +			       err, i, sfd[i]); +			goto out_sockmap; +		} +	} + +	/* Test tasks number of forked operations */ +	for (i = 0; i < tasks; i++) { +		pid[i] = fork(); +		if (pid[i] == 0) { +			for (i = 0; i < 6; i++) { +				bpf_map_delete_elem(map_fd_tx, &i); +				bpf_map_delete_elem(map_fd_rx, &i); +				bpf_map_update_elem(map_fd_tx, &i, +						    &sfd[i], BPF_ANY); +				bpf_map_update_elem(map_fd_rx, &i, +						    &sfd[i], BPF_ANY); +			} +			exit(0); +		} else if (pid[i] == -1) { +			printf("Couldn't spawn #%d process!\n", i); +			exit(1); +		} +	} + +	for (i = 0; i < tasks; i++) { +		int status; + +		assert(waitpid(pid[i], &status, 0) == pid[i]); +		assert(status == 0); +	} + +	/* Test map close sockets */ +	for (i = 0; i < 6; i++) +		close(sfd[i]); +	close(fd); +	close(map_fd_rx); +	bpf_object__close(obj); +	return; +out: +	for (i = 0; i < 6; i++) +		close(sfd[i]); +	printf("Failed to create sockmap '%i:%s'!\n", i, strerror(errno)); +	exit(1); +out_sockmap: +	for (i = 0; i < 6; i++) +		close(sfd[i]); +	close(fd); +	exit(1); +} +  #define MAP_SIZE (32 * 1024)  static void test_map_large(void) @@ -605,6 +995,9 @@ static void run_all_tests(void)  	test_arraymap_percpu_many_keys(); +	test_devmap(0, NULL); +	test_sockmap(0, NULL); +  	test_map_large();  	test_map_parallel();  	test_map_stress(); diff --git a/tools/testing/selftests/bpf/test_pkt_md_access.c b/tools/testing/selftests/bpf/test_pkt_md_access.c index 71729d47eb85..7956302ecdf2 100644 --- a/tools/testing/selftests/bpf/test_pkt_md_access.c +++ b/tools/testing/selftests/bpf/test_pkt_md_access.c @@ -12,12 +12,23 @@  int _version SEC("version") = 1; +#if  __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__  #define TEST_FIELD(TYPE, FIELD, MASK)					\  	{								\  		TYPE tmp = *(volatile TYPE *)&skb->FIELD;		\  		if (tmp != ((*(volatile __u32 *)&skb->FIELD) & MASK))	\  			return TC_ACT_SHOT;				\  	} +#else +#define TEST_FIELD_OFFSET(a, b)	((sizeof(a) - sizeof(b)) / sizeof(b)) +#define TEST_FIELD(TYPE, FIELD, MASK)					\ +	{								\ +		TYPE tmp = *((volatile TYPE *)&skb->FIELD +		\ +			      TEST_FIELD_OFFSET(skb->FIELD, TYPE));	\ +		if (tmp != ((*(volatile __u32 *)&skb->FIELD) & MASK))	\ +			return TC_ACT_SHOT;				\ +	} +#endif  SEC("test1")  int process(struct __sk_buff *skb) diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index 5855cd3d3d45..11ee25cea227 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -75,39 +75,6 @@ static struct {  	__ret;								\  }) -static int bpf_prog_load(const char *file, enum bpf_prog_type type, -			 struct bpf_object **pobj, int *prog_fd) -{ -	struct bpf_program *prog; -	struct bpf_object *obj; -	int err; - -	obj = bpf_object__open(file); -	if (IS_ERR(obj)) { -		error_cnt++; -		return -ENOENT; -	} - -	prog = bpf_program__next(NULL, obj); -	if (!prog) { -		bpf_object__close(obj); -		error_cnt++; -		return -ENOENT; -	} - -	bpf_program__set_type(prog, type); -	err = bpf_object__load(obj); -	if (err) { -		bpf_object__close(obj); -		error_cnt++; -		return -EINVAL; -	} - -	*pobj = obj; -	*prog_fd = bpf_program__fd(prog); -	return 0; -} -  static int bpf_find_map(const char *test, struct bpf_object *obj,  			const char *name)  { @@ -130,8 +97,10 @@ static void test_pkt_access(void)  	int err, prog_fd;  	err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); -	if (err) +	if (err) { +		error_cnt++;  		return; +	}  	err = bpf_prog_test_run(prog_fd, 100000, &pkt_v4, sizeof(pkt_v4),  				NULL, NULL, &retval, &duration); @@ -162,8 +131,10 @@ static void test_xdp(void)  	int err, prog_fd, map_fd;  	err = bpf_prog_load(file, BPF_PROG_TYPE_XDP, &obj, &prog_fd); -	if (err) +	if (err) { +		error_cnt++;  		return; +	}  	map_fd = bpf_find_map(__func__, obj, "vip2tnl");  	if (map_fd < 0) @@ -223,8 +194,10 @@ static void test_l4lb(void)  	u32 *magic = (u32 *)buf;  	err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); -	if (err) +	if (err) { +		error_cnt++;  		return; +	}  	map_fd = bpf_find_map(__func__, obj, "vip_map");  	if (map_fd < 0) @@ -280,8 +253,10 @@ static void test_tcp_estats(void)  	err = bpf_prog_load(file, BPF_PROG_TYPE_TRACEPOINT, &obj, &prog_fd);  	CHECK(err, "", "err %d errno %d\n", err, errno); -	if (err) +	if (err) { +		error_cnt++;  		return; +	}  	bpf_object__close(obj);  } @@ -304,7 +279,7 @@ static void test_bpf_obj_id(void)  	/* +1 to test for the info_len returned by kernel */  	struct bpf_prog_info prog_infos[nr_iters + 1];  	struct bpf_map_info map_infos[nr_iters + 1]; -	char jited_insns[128], xlated_insns[128]; +	char jited_insns[128], xlated_insns[128], zeros[128];  	__u32 i, next_id, info_len, nr_id_found, duration = 0;  	int sysctl_fd, jit_enabled = 0, err = 0;  	__u64 array_value; @@ -330,16 +305,22 @@ static void test_bpf_obj_id(void)  		objs[i] = NULL;  	/* Check bpf_obj_get_info_by_fd() */ +	bzero(zeros, sizeof(zeros));  	for (i = 0; i < nr_iters; i++) {  		err = bpf_prog_load(file, BPF_PROG_TYPE_SOCKET_FILTER,  				    &objs[i], &prog_fds[i]);  		/* test_obj_id.o is a dumb prog. It should never fail  		 * to load.  		 */ +		if (err) +			error_cnt++;  		assert(!err);  		/* Check getting prog info */  		info_len = sizeof(struct bpf_prog_info) * 2; +		bzero(&prog_infos[i], info_len); +		bzero(jited_insns, sizeof(jited_insns)); +		bzero(xlated_insns, sizeof(xlated_insns));  		prog_infos[i].jited_prog_insns = ptr_to_u64(jited_insns);  		prog_infos[i].jited_prog_len = sizeof(jited_insns);  		prog_infos[i].xlated_prog_insns = ptr_to_u64(xlated_insns); @@ -350,15 +331,20 @@ static void test_bpf_obj_id(void)  			  prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER ||  			  info_len != sizeof(struct bpf_prog_info) ||  			  (jit_enabled && !prog_infos[i].jited_prog_len) || -			  !prog_infos[i].xlated_prog_len, +			  (jit_enabled && +			   !memcmp(jited_insns, zeros, sizeof(zeros))) || +			  !prog_infos[i].xlated_prog_len || +			  !memcmp(xlated_insns, zeros, sizeof(zeros)),  			  "get-prog-info(fd)", -			  "err %d errno %d i %d type %d(%d) info_len %u(%lu) jit_enabled %d jited_prog_len %u xlated_prog_len %u\n", +			  "err %d errno %d i %d type %d(%d) info_len %u(%lu) jit_enabled %d jited_prog_len %u xlated_prog_len %u jited_prog %d xlated_prog %d\n",  			  err, errno, i,  			  prog_infos[i].type, BPF_PROG_TYPE_SOCKET_FILTER,  			  info_len, sizeof(struct bpf_prog_info),  			  jit_enabled,  			  prog_infos[i].jited_prog_len, -			  prog_infos[i].xlated_prog_len)) +			  prog_infos[i].xlated_prog_len, +			  !!memcmp(jited_insns, zeros, sizeof(zeros)), +			  !!memcmp(xlated_insns, zeros, sizeof(zeros))))  			goto done;  		map_fds[i] = bpf_find_map(__func__, objs[i], "test_map_id"); @@ -369,6 +355,7 @@ static void test_bpf_obj_id(void)  		/* Check getting map info */  		info_len = sizeof(struct bpf_map_info) * 2; +		bzero(&map_infos[i], info_len);  		err = bpf_obj_get_info_by_fd(map_fds[i], &map_infos[i],  					     &info_len);  		if (CHECK(err || @@ -394,7 +381,7 @@ static void test_bpf_obj_id(void)  	nr_id_found = 0;  	next_id = 0;  	while (!bpf_prog_get_next_id(next_id, &next_id)) { -		struct bpf_prog_info prog_info; +		struct bpf_prog_info prog_info = {};  		int prog_fd;  		info_len = sizeof(prog_info); @@ -418,6 +405,8 @@ static void test_bpf_obj_id(void)  		nr_id_found++;  		err = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &info_len); +		prog_infos[i].jited_prog_insns = 0; +		prog_infos[i].xlated_prog_insns = 0;  		CHECK(err || info_len != sizeof(struct bpf_prog_info) ||  		      memcmp(&prog_info, &prog_infos[i], info_len),  		      "get-prog-info(next_id->fd)", @@ -436,7 +425,7 @@ static void test_bpf_obj_id(void)  	nr_id_found = 0;  	next_id = 0;  	while (!bpf_map_get_next_id(next_id, &next_id)) { -		struct bpf_map_info map_info; +		struct bpf_map_info map_info = {};  		int map_fd;  		info_len = sizeof(map_info); @@ -492,8 +481,10 @@ static void test_pkt_md_access(void)  	int err, prog_fd;  	err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); -	if (err) +	if (err) { +		error_cnt++;  		return; +	}  	err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4),  				NULL, NULL, &retval, &duration); diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index af7d173910f4..8eb09950258b 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -8,6 +8,7 @@   * License as published by the Free Software Foundation.   */ +#include <endian.h>  #include <asm/types.h>  #include <linux/types.h>  #include <stdint.h> @@ -421,7 +422,7 @@ static struct bpf_test tests[] = {  			BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),  			BPF_EXIT_INSN(),  		}, -		.errstr_unpriv = "R1 pointer arithmetic", +		.errstr_unpriv = "R1 subtraction from stack pointer",  		.result_unpriv = REJECT,  		.errstr = "R1 invalid mem access",  		.result = REJECT, @@ -603,8 +604,9 @@ static struct bpf_test tests[] = {  			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -4),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned stack access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"invalid map_fd for function call", @@ -650,8 +652,9 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr = "misaligned access", +		.errstr = "misaligned value access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"sometimes access memory with incorrect alignment", @@ -672,6 +675,7 @@ static struct bpf_test tests[] = {  		.errstr = "R0 invalid mem access",  		.errstr_unpriv = "R0 leaks addr",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"jump test 1", @@ -963,6 +967,256 @@ static struct bpf_test tests[] = {  		.result = REJECT,  	},  	{ +		"invalid access __sk_buff family", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, family)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"invalid access __sk_buff remote_ip4", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip4)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"invalid access __sk_buff local_ip4", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip4)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"invalid access __sk_buff remote_ip6", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip6)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"invalid access __sk_buff local_ip6", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip6)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"invalid access __sk_buff remote_port", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_port)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"invalid access __sk_buff remote_port", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_port)), +			BPF_EXIT_INSN(), +		}, +		.errstr = "invalid bpf_context access", +		.result = REJECT, +	}, +	{ +		"valid access __sk_buff family", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, family)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"valid access __sk_buff remote_ip4", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip4)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"valid access __sk_buff local_ip4", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip4)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"valid access __sk_buff remote_ip6", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip6[0])), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip6[1])), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip6[2])), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_ip6[3])), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"valid access __sk_buff local_ip6", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip6[0])), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip6[1])), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip6[2])), +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_ip6[3])), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"valid access __sk_buff remote_port", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, remote_port)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"valid access __sk_buff remote_port", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, local_port)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"invalid access of tc_classid for SK_SKB", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, +				    offsetof(struct __sk_buff, tc_classid)), +			BPF_EXIT_INSN(), +		}, +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +		.errstr = "invalid bpf_context access", +	}, +	{ +		"check skb->mark is writeable by SK_SKB", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, +				    offsetof(struct __sk_buff, mark)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"check skb->tc_index is writeable by SK_SKB", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, +				    offsetof(struct __sk_buff, tc_index)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"check skb->priority is writeable by SK_SKB", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, +				    offsetof(struct __sk_buff, priority)), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"direct packet read for SK_SKB", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"direct packet write for SK_SKB", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1), +			BPF_STX_MEM(BPF_B, BPF_REG_2, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{ +		"overlapping checks for direct packet access SK_SKB", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 6), +			BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 1), +			BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_2, 6), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SK_SKB, +	}, +	{  		"check skb->mark is not writeable by sockets",  		.insns = {  			BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, @@ -1098,7 +1352,7 @@ static struct bpf_test tests[] = {  		"check skb->hash byte load permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,  				    offsetof(struct __sk_buff, hash)),  #else @@ -1135,7 +1389,7 @@ static struct bpf_test tests[] = {  		"check skb->hash byte load not permitted 3",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,  				    offsetof(struct __sk_buff, hash) + 3),  #else @@ -1215,8 +1469,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[0]) + 1),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check __sk_buff->hash, offset 0, half store not permitted", @@ -1244,7 +1499,7 @@ static struct bpf_test tests[] = {  		"check skb->hash half load permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,  				    offsetof(struct __sk_buff, hash)),  #else @@ -1259,7 +1514,7 @@ static struct bpf_test tests[] = {  		"check skb->hash half load not permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,  				    offsetof(struct __sk_buff, hash) + 2),  #else @@ -1319,8 +1574,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[0]) + 2),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check cb access: word, unaligned 2", @@ -1330,8 +1586,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[4]) + 1),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check cb access: word, unaligned 3", @@ -1341,8 +1598,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[4]) + 2),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check cb access: word, unaligned 4", @@ -1352,8 +1610,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[4]) + 3),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check cb access: double", @@ -1379,8 +1638,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[1])),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check cb access: double, unaligned 2", @@ -1390,8 +1650,9 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, cb[3])),  			BPF_EXIT_INSN(),  		}, -		.errstr = "misaligned access", +		.errstr = "misaligned context access",  		.result = REJECT, +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"check cb access: double, oob 1", @@ -1523,7 +1784,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "misaligned access off -6 size 8", +		.errstr = "misaligned stack access off (0x0; 0x0)+-8+2 size 8", +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"PTR_TO_STACK store/load - bad alignment on reg", @@ -1535,7 +1797,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "misaligned access off -2 size 8", +		.errstr = "misaligned stack access off (0x0; 0x0)+-10+8 size 8", +		.flags = F_LOAD_WITH_STRICT_ALIGNMENT,  	},  	{  		"PTR_TO_STACK store/load - out of bounds low", @@ -1579,8 +1842,6 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = ACCEPT, -		.result_unpriv = REJECT, -		.errstr_unpriv = "R1 pointer arithmetic",  	},  	{  		"unpriv: add pointer to pointer", @@ -1591,7 +1852,7 @@ static struct bpf_test tests[] = {  		},  		.result = ACCEPT,  		.result_unpriv = REJECT, -		.errstr_unpriv = "R1 pointer arithmetic", +		.errstr_unpriv = "R1 pointer += pointer",  	},  	{  		"unpriv: neg pointer", @@ -1932,10 +2193,7 @@ static struct bpf_test tests[] = {  			BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, -8),  			BPF_EXIT_INSN(),  		}, -		.errstr_unpriv = "pointer arithmetic prohibited", -		.result_unpriv = REJECT, -		.errstr = "R1 invalid mem access", -		.result = REJECT, +		.result = ACCEPT,  	},  	{  		"unpriv: cmp of stack pointer", @@ -1999,7 +2257,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid stack type R3", +		.errstr = "R4 min value is negative",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ @@ -2016,7 +2274,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid stack type R3", +		.errstr = "R4 min value is negative",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ @@ -2218,7 +2476,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid stack type R3 off=-1 access_size=-1", +		.errstr = "R4 min value is negative",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ @@ -2235,7 +2493,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid stack type R3 off=-1 access_size=2147483647", +		.errstr = "R4 unbounded memory access, use 'var &= const' or 'if (var < const)'",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ @@ -2252,7 +2510,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid stack type R3 off=-512 access_size=2147483647", +		.errstr = "R4 unbounded memory access, use 'var &= const' or 'if (var < const)'",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ @@ -2323,8 +2581,8 @@ static struct bpf_test tests[] = {  				    offsetof(struct __sk_buff, data)),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_4),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), -			BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 48), -			BPF_ALU64_IMM(BPF_RSH, BPF_REG_2, 48), +			BPF_ALU64_IMM(BPF_LSH, BPF_REG_2, 49), +			BPF_ALU64_IMM(BPF_RSH, BPF_REG_2, 49),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_3, BPF_REG_2),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_3),  			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, 8), @@ -2652,7 +2910,7 @@ static struct bpf_test tests[] = {  			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 1),  			BPF_JMP_A(-6),  		}, -		.errstr = "misaligned packet access off 2+15+-4 size 4", +		.errstr = "misaligned packet access off 2+(0x0; 0x0)+15+-4 size 4",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.flags = F_LOAD_WITH_STRICT_ALIGNMENT, @@ -2703,11 +2961,11 @@ static struct bpf_test tests[] = {  			BPF_MOV64_IMM(BPF_REG_0, 0xffffffff),  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_0, -8),  			BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8), -			BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0xffff), +			BPF_ALU64_IMM(BPF_AND, BPF_REG_0, 0x7fff),  			BPF_MOV64_REG(BPF_REG_4, BPF_REG_0),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),  			BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), -			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0xffff - 1), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0x7fff - 1),  			BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 1),  			BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_4, 0),  			BPF_MOV64_IMM(BPF_REG_0, 0), @@ -2729,10 +2987,10 @@ static struct bpf_test tests[] = {  			BPF_MOV64_IMM(BPF_REG_4, 0xffffffff),  			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_4, -8),  			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), -			BPF_ALU64_IMM(BPF_AND, BPF_REG_4, 0xffff), +			BPF_ALU64_IMM(BPF_AND, BPF_REG_4, 0x7fff),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),  			BPF_MOV64_REG(BPF_REG_5, BPF_REG_4), -			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0xffff - 1), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 0x7fff - 1),  			BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_3, 1),  			BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_4, 0),  			BPF_MOV64_IMM(BPF_REG_0, 0), @@ -2758,7 +3016,7 @@ static struct bpf_test tests[] = {  			BPF_MOV64_IMM(BPF_REG_4, 0xffffffff),  			BPF_STX_XADD(BPF_DW, BPF_REG_10, BPF_REG_4, -8),  			BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -8), -			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 48), +			BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 49),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2),  			BPF_MOV64_REG(BPF_REG_0, BPF_REG_4),  			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2), @@ -2795,7 +3053,7 @@ static struct bpf_test tests[] = {  		},  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  		.result = REJECT, -		.errstr = "cannot add integer value with 47 upper zero bits to ptr_to_packet", +		.errstr = "invalid access to packet, off=0 size=8, R5(id=1,off=0,r=0)",  	},  	{  		"direct packet access: test24 (x += pkt_ptr, 5)", @@ -2813,7 +3071,7 @@ static struct bpf_test tests[] = {  			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_4),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2),  			BPF_MOV64_REG(BPF_REG_5, BPF_REG_0), -			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 0xffff - 1), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 0x7fff - 1),  			BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),  			BPF_STX_MEM(BPF_DW, BPF_REG_5, BPF_REG_0, 0),  			BPF_MOV64_IMM(BPF_REG_0, 0), @@ -2823,6 +3081,79 @@ static struct bpf_test tests[] = {  		.result = ACCEPT,  	},  	{ +		"direct packet access: test25 (marking on <, good access)", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JLT, BPF_REG_0, BPF_REG_3, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, -4), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +	}, +	{ +		"direct packet access: test26 (marking on <, bad access)", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JLT, BPF_REG_0, BPF_REG_3, 3), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JA, 0, 0, -3), +		}, +		.result = REJECT, +		.errstr = "invalid access to packet", +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +	}, +	{ +		"direct packet access: test27 (marking on <=, good access)", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JLE, BPF_REG_3, BPF_REG_0, 1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +	}, +	{ +		"direct packet access: test28 (marking on <=, bad access)", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), +			BPF_JMP_REG(BPF_JLE, BPF_REG_3, BPF_REG_0, 2), +			BPF_MOV64_IMM(BPF_REG_0, 1), +			BPF_EXIT_INSN(), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0), +			BPF_JMP_IMM(BPF_JA, 0, 0, -4), +		}, +		.result = REJECT, +		.errstr = "invalid access to packet", +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +	}, +	{  		"helper access to packet: test1, valid packet_ptr range",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, @@ -3112,7 +3443,7 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test14, cls helper fail sub", +		"helper access to packet: test14, cls helper ok sub",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3132,12 +3463,36 @@ static struct bpf_test tests[] = {  			BPF_MOV64_IMM(BPF_REG_0, 0),  			BPF_EXIT_INSN(),  		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +	}, +	{ +		"helper access to packet: test15, cls helper fail sub", +		.insns = { +			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, +				    offsetof(struct __sk_buff, data)), +			BPF_LDX_MEM(BPF_W, BPF_REG_7, BPF_REG_1, +				    offsetof(struct __sk_buff, data_end)), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 1), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_6), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 7), +			BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_7, 6), +			BPF_ALU64_IMM(BPF_SUB, BPF_REG_1, 12), +			BPF_MOV64_IMM(BPF_REG_2, 4), +			BPF_MOV64_IMM(BPF_REG_3, 0), +			BPF_MOV64_IMM(BPF_REG_4, 0), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_csum_diff), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		},  		.result = REJECT, -		.errstr = "type=inv expected=fp", +		.errstr = "invalid access to packet",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test15, cls helper fail range 1", +		"helper access to packet: test16, cls helper fail range 1",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3162,7 +3517,7 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test16, cls helper fail range 2", +		"helper access to packet: test17, cls helper fail range 2",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3183,11 +3538,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid access to packet", +		.errstr = "R2 min value is negative",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test17, cls helper fail range 3", +		"helper access to packet: test18, cls helper fail range 3",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3208,11 +3563,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.result = REJECT, -		.errstr = "invalid access to packet", +		.errstr = "R2 min value is negative",  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test18, cls helper fail range zero", +		"helper access to packet: test19, cls helper fail range zero",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3237,7 +3592,7 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test19, pkt end as input", +		"helper access to packet: test20, pkt end as input",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3262,7 +3617,7 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	},  	{ -		"helper access to packet: test20, wrong reg", +		"helper access to packet: test21, wrong reg",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1,  				    offsetof(struct __sk_buff, data)), @@ -3322,7 +3677,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result_unpriv = REJECT,  		.result = ACCEPT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -3346,7 +3701,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result_unpriv = REJECT,  		.result = ACCEPT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -3374,7 +3729,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result_unpriv = REJECT,  		.result = ACCEPT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -3415,9 +3770,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is outside of the array range", -		.result_unpriv = REJECT,  		.result = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,  	}, @@ -3439,9 +3792,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", -		.result_unpriv = REJECT, +		.errstr = "R0 unbounded memory access, make sure to bounds check any array access into a map",  		.result = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,  	}, @@ -3455,7 +3806,7 @@ static struct bpf_test tests[] = {  			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,  				     BPF_FUNC_map_lookup_elem),  			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), -			BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_0, 0), +			BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0),  			BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES),  			BPF_JMP_REG(BPF_JSGT, BPF_REG_2, BPF_REG_1, 1),  			BPF_MOV32_IMM(BPF_REG_1, 0), @@ -3466,8 +3817,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", +		.errstr_unpriv = "R0 leaks addr", +		.errstr = "R0 unbounded memory access",  		.result_unpriv = REJECT,  		.result = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -3493,7 +3844,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.errstr = "invalid access to map value, value_size=48 off=44 size=8",  		.result_unpriv = REJECT,  		.result = REJECT, @@ -3523,8 +3874,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3, 11 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", +		.errstr_unpriv = "R0 pointer += pointer", +		.errstr = "R0 invalid mem access 'inv'",  		.result_unpriv = REJECT,  		.result = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -3666,34 +4017,6 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_SCHED_CLS  	},  	{ -		"multiple registers share map_lookup_elem bad reg type", -		.insns = { -			BPF_MOV64_IMM(BPF_REG_1, 10), -			BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8), -			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), -			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), -			BPF_LD_MAP_FD(BPF_REG_1, 0), -			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, -				     BPF_FUNC_map_lookup_elem), -			BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), -			BPF_MOV64_REG(BPF_REG_3, BPF_REG_0), -			BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), -			BPF_MOV64_REG(BPF_REG_5, BPF_REG_0), -			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), -			BPF_MOV64_IMM(BPF_REG_1, 1), -			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), -			BPF_MOV64_IMM(BPF_REG_1, 2), -			BPF_JMP_IMM(BPF_JEQ, BPF_REG_3, 0, 1), -			BPF_ST_MEM(BPF_DW, BPF_REG_3, 0, 0), -			BPF_MOV64_IMM(BPF_REG_1, 3), -			BPF_EXIT_INSN(), -		}, -		.fixup_map1 = { 4 }, -		.result = REJECT, -		.errstr = "R3 invalid mem access 'inv'", -		.prog_type = BPF_PROG_TYPE_SCHED_CLS -	}, -	{  		"invalid map access from else condition",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), @@ -3711,9 +4034,9 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "R0 unbounded memory access, make sure to bounds check any array access into a map", +		.errstr = "R0 unbounded memory access",  		.result = REJECT, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result_unpriv = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,  	}, @@ -4091,7 +4414,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "invalid access to map value, value_size=48 off=0 size=-8", +		.errstr = "R2 min value is negative",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4157,7 +4480,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "R1 min value is outside of the array range", +		.errstr = "invalid access to map value, value_size=48 off=4 size=0",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4203,7 +4526,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "invalid access to map value, value_size=48 off=4 size=-8", +		.errstr = "R2 min value is negative",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4225,7 +4548,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "R1 min value is outside of the array range", +		.errstr = "R2 min value is negative",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4341,7 +4664,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "invalid access to map value, value_size=48 off=4 size=-8", +		.errstr = "R2 min value is negative",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4364,7 +4687,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "R1 min value is outside of the array range", +		.errstr = "R2 min value is negative",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4452,13 +4775,13 @@ static struct bpf_test tests[] = {  			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),  			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), -			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_2, 1),  			BPF_MOV64_IMM(BPF_REG_3, 0),  			BPF_EMIT_CALL(BPF_FUNC_probe_read),  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr = "R1 min value is negative, either use unsigned index or do a if (index >=0) check", +		.errstr = "R1 unbounded memory access",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -4489,6 +4812,246 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	},  	{ +		"helper access to map: bounds check using <, good access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JLT, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using <, bad access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JLT, BPF_REG_3, 32, 4), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = REJECT, +		.errstr = "R1 unbounded memory access", +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using <=, good access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JLE, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using <=, bad access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JLE, BPF_REG_3, 32, 4), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = REJECT, +		.errstr = "R1 unbounded memory access", +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using s<, good access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JSLT, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JSLT, BPF_REG_3, 0, -3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using s<, good access 2", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JSLT, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JSLT, BPF_REG_3, -3, -3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using s<, bad access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JSLT, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JSLT, BPF_REG_3, -3, -3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = REJECT, +		.errstr = "R1 min value is negative", +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using s<=, good access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JSLE, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JSLE, BPF_REG_3, 0, -3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using s<=, good access 2", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JSLE, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JSLE, BPF_REG_3, -3, -3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{ +		"helper access to map: bounds check using s<=, bad access", +		.insns = { +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), +			BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), +			BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JSLE, BPF_REG_3, 32, 2), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_JMP_IMM(BPF_JSLE, BPF_REG_3, -3, -3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), +			BPF_ST_MEM(BPF_B, BPF_REG_1, 0, 0), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.result = REJECT, +		.errstr = "R1 min value is negative", +		.prog_type = BPF_PROG_TYPE_TRACEPOINT, +	}, +	{  		"map element value is preserved across register spilling",  		.insns = {  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -4578,7 +5141,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result = ACCEPT,  		.result_unpriv = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -4606,7 +5169,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result = ACCEPT,  		.result_unpriv = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -4625,7 +5188,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 bitwise operator &= on pointer",  		.errstr = "invalid mem access 'inv'",  		.result = REJECT,  		.result_unpriv = REJECT, @@ -4644,7 +5207,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 32-bit pointer arithmetic prohibited",  		.errstr = "invalid mem access 'inv'",  		.result = REJECT,  		.result_unpriv = REJECT, @@ -4663,7 +5226,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 pointer arithmetic with /= operator",  		.errstr = "invalid mem access 'inv'",  		.result = REJECT,  		.result_unpriv = REJECT, @@ -4706,10 +5269,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 invalid mem access 'inv'",  		.errstr = "R0 invalid mem access 'inv'",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"map element value is preserved across register spilling", @@ -4731,7 +5292,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 leaks addr",  		.result = ACCEPT,  		.result_unpriv = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, @@ -4913,7 +5474,8 @@ static struct bpf_test tests[] = {  			BPF_MOV64_IMM(BPF_REG_0, 0),  			BPF_EXIT_INSN(),  		}, -		.errstr = "R2 unbounded memory access", +		/* because max wasn't checked, signed min is negative */ +		.errstr = "R2 min value is negative, either use unsigned or 'var &= const'",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	}, @@ -5062,6 +5624,20 @@ static struct bpf_test tests[] = {  		.prog_type = BPF_PROG_TYPE_TRACEPOINT,  	},  	{ +		"helper access to variable memory: size = 0 allowed on NULL", +		.insns = { +			BPF_MOV64_IMM(BPF_REG_1, 0), +			BPF_MOV64_IMM(BPF_REG_2, 0), +			BPF_MOV64_IMM(BPF_REG_3, 0), +			BPF_MOV64_IMM(BPF_REG_4, 0), +			BPF_MOV64_IMM(BPF_REG_5, 0), +			BPF_EMIT_CALL(BPF_FUNC_csum_diff), +			BPF_EXIT_INSN(), +		}, +		.result = ACCEPT, +		.prog_type = BPF_PROG_TYPE_SCHED_CLS, +	}, +	{  		"helper access to variable memory: size > 0 not allowed on NULL",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_1, 0), @@ -5075,7 +5651,7 @@ static struct bpf_test tests[] = {  			BPF_EMIT_CALL(BPF_FUNC_csum_diff),  			BPF_EXIT_INSN(),  		}, -		.errstr = "R1 type=imm expected=fp", +		.errstr = "R1 type=inv expected=fp",  		.result = REJECT,  		.prog_type = BPF_PROG_TYPE_SCHED_CLS,  	}, @@ -5160,7 +5736,7 @@ static struct bpf_test tests[] = {  			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,  				     BPF_FUNC_map_lookup_elem),  			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), -			BPF_MOV64_IMM(BPF_REG_1, 6), +			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0),  			BPF_ALU64_IMM(BPF_AND, BPF_REG_1, -4),  			BPF_ALU64_IMM(BPF_LSH, BPF_REG_1, 2),  			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), @@ -5169,10 +5745,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", +		.errstr = "R0 max value is outside of the array range",  		.result = REJECT, -		.result_unpriv = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,  	},  	{ @@ -5201,10 +5775,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map2 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", +		.errstr = "R0 max value is outside of the array range",  		.result = REJECT, -		.result_unpriv = REJECT,  		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS,  	},  	{ @@ -5251,7 +5823,7 @@ static struct bpf_test tests[] = {  		},  		.fixup_map_in_map = { 3 },  		.errstr = "R1 type=inv expected=map_ptr", -		.errstr_unpriv = "R1 pointer arithmetic prohibited", +		.errstr_unpriv = "R1 pointer arithmetic on CONST_PTR_TO_MAP prohibited",  		.result = REJECT,  	},  	{ @@ -5422,7 +5994,7 @@ static struct bpf_test tests[] = {  		"check bpf_perf_event_data->sample_period byte load permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1,  				    offsetof(struct bpf_perf_event_data, sample_period)),  #else @@ -5438,7 +6010,7 @@ static struct bpf_test tests[] = {  		"check bpf_perf_event_data->sample_period half load permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,  				    offsetof(struct bpf_perf_event_data, sample_period)),  #else @@ -5454,7 +6026,7 @@ static struct bpf_test tests[] = {  		"check bpf_perf_event_data->sample_period word load permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,  				    offsetof(struct bpf_perf_event_data, sample_period)),  #else @@ -5481,7 +6053,7 @@ static struct bpf_test tests[] = {  		"check skb->data half load not permitted",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,  				    offsetof(struct __sk_buff, data)),  #else @@ -5497,7 +6069,7 @@ static struct bpf_test tests[] = {  		"check skb->tc_classid half load not permitted for lwt prog",  		.insns = {  			BPF_MOV64_IMM(BPF_REG_0, 0), -#ifdef __LITTLE_ENDIAN +#if __BYTE_ORDER == __LITTLE_ENDIAN  			BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1,  				    offsetof(struct __sk_buff, tc_classid)),  #else @@ -5531,10 +6103,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"bounds checks mixing signed and unsigned", @@ -5557,10 +6127,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 2", @@ -5585,10 +6153,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R8 invalid mem access 'inv'",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 3", @@ -5612,10 +6178,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R8 invalid mem access 'inv'",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 4", @@ -5638,10 +6202,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative", -		.result = REJECT, -		.result_unpriv = REJECT, +		.result = ACCEPT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 5", @@ -5665,10 +6226,8 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 invalid mem access", +		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 6", @@ -5689,10 +6248,8 @@ static struct bpf_test tests[] = {  			BPF_MOV64_IMM(BPF_REG_0, 0),  			BPF_EXIT_INSN(),  		}, -		.errstr_unpriv = "R4 min value is negative, either use unsigned",  		.errstr = "R4 min value is negative, either use unsigned",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 7", @@ -5715,10 +6272,7 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative", -		.result = REJECT, -		.result_unpriv = REJECT, +		.result = ACCEPT,  	},  	{  		"bounds checks mixing signed and unsigned, variant 8", @@ -5729,32 +6283,6 @@ static struct bpf_test tests[] = {  			BPF_LD_MAP_FD(BPF_REG_1, 0),  			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,  				     BPF_FUNC_map_lookup_elem), -			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), -			BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), -			BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), -			BPF_MOV64_IMM(BPF_REG_2, 1024 * 1024 * 1024 + 1), -			BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 3), -			BPF_JMP_IMM(BPF_JSGT, BPF_REG_1, 1, 2), -			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), -			BPF_ST_MEM(BPF_B, BPF_REG_0, 0, 0), -			BPF_MOV64_IMM(BPF_REG_0, 0), -			BPF_EXIT_INSN(), -		}, -		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative", -		.result = REJECT, -		.result_unpriv = REJECT, -	}, -	{ -		"bounds checks mixing signed and unsigned, variant 9", -		.insns = { -			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), -			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), -			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), -			BPF_LD_MAP_FD(BPF_REG_1, 0), -			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, -				     BPF_FUNC_map_lookup_elem),  			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9),  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8),  			BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), @@ -5769,13 +6297,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 10", +		"bounds checks mixing signed and unsigned, variant 9",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5797,13 +6323,10 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", -		.errstr = "R0 min value is negative", -		.result = REJECT, -		.result_unpriv = REJECT, +		.result = ACCEPT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 11", +		"bounds checks mixing signed and unsigned, variant 10",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5825,13 +6348,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 12", +		"bounds checks mixing signed and unsigned, variant 11",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5854,13 +6375,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 13", +		"bounds checks mixing signed and unsigned, variant 12",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5882,13 +6401,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 14", +		"bounds checks mixing signed and unsigned, variant 13",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5913,13 +6430,11 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 15", +		"bounds checks mixing signed and unsigned, variant 14",  		.insns = {  			BPF_LDX_MEM(BPF_W, BPF_REG_9, BPF_REG_1,  				    offsetof(struct __sk_buff, mark)), @@ -5945,13 +6460,11 @@ static struct bpf_test tests[] = {  			BPF_JMP_IMM(BPF_JA, 0, 0, -7),  		},  		.fixup_map1 = { 4 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT, -		.result_unpriv = REJECT,  	},  	{ -		"bounds checks mixing signed and unsigned, variant 16", +		"bounds checks mixing signed and unsigned, variant 15",  		.insns = {  			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),  			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5975,11 +6488,147 @@ static struct bpf_test tests[] = {  			BPF_EXIT_INSN(),  		},  		.fixup_map1 = { 3 }, -		.errstr_unpriv = "R0 pointer arithmetic prohibited", +		.errstr_unpriv = "R0 pointer comparison prohibited",  		.errstr = "R0 min value is negative",  		.result = REJECT,  		.result_unpriv = REJECT,  	}, +	{ +		"subtraction bounds (map value) variant 1", +		.insns = { +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 9), +			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 0xff, 7), +			BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_0, 1), +			BPF_JMP_IMM(BPF_JGT, BPF_REG_3, 0xff, 5), +			BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_3), +			BPF_ALU64_IMM(BPF_RSH, BPF_REG_1, 56), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map1 = { 3 }, +		.errstr = "R0 max value is outside of the array range", +		.result = REJECT, +	}, +	{ +		"subtraction bounds (map value) variant 2", +		.insns = { +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), +			BPF_LDX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JGT, BPF_REG_1, 0xff, 6), +			BPF_LDX_MEM(BPF_B, BPF_REG_3, BPF_REG_0, 1), +			BPF_JMP_IMM(BPF_JGT, BPF_REG_3, 0xff, 4), +			BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_3), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), +			BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_0, 0), +			BPF_EXIT_INSN(), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.fixup_map1 = { 3 }, +		.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", +		.result = REJECT, +	}, +	{ +		"variable-offset ctx access", +		.insns = { +			/* Get an unknown value */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0), +			/* Make it small and 4-byte aligned */ +			BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4), +			/* add it to skb.  We now have either &skb->len or +			 * &skb->pkt_type, but we don't know which +			 */ +			BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_2), +			/* dereference it */ +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, 0), +			BPF_EXIT_INSN(), +		}, +		.errstr = "variable ctx access var_off=(0x0; 0x4)", +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_LWT_IN, +	}, +	{ +		"variable-offset stack access", +		.insns = { +			/* Fill the top 8 bytes of the stack */ +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			/* Get an unknown value */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0), +			/* Make it small and 4-byte aligned */ +			BPF_ALU64_IMM(BPF_AND, BPF_REG_2, 4), +			BPF_ALU64_IMM(BPF_SUB, BPF_REG_2, 8), +			/* add it to fp.  We now have either fp-4 or fp-8, but +			 * we don't know which +			 */ +			BPF_ALU64_REG(BPF_ADD, BPF_REG_2, BPF_REG_10), +			/* dereference it */ +			BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_2, 0), +			BPF_EXIT_INSN(), +		}, +		.errstr = "variable stack access var_off=(0xfffffffffffffff8; 0x4)", +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_LWT_IN, +	}, +	{ +		"liveness pruning and write screening", +		.insns = { +			/* Get an unknown value */ +			BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 0), +			/* branch conditions teach us nothing about R2 */ +			BPF_JMP_IMM(BPF_JGE, BPF_REG_2, 0, 1), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_JMP_IMM(BPF_JGE, BPF_REG_2, 0, 1), +			BPF_MOV64_IMM(BPF_REG_0, 0), +			BPF_EXIT_INSN(), +		}, +		.errstr = "R0 !read_ok", +		.result = REJECT, +		.prog_type = BPF_PROG_TYPE_LWT_IN, +	}, +	{ +		"varlen_map_value_access pruning", +		.insns = { +			BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), +			BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), +			BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), +			BPF_LD_MAP_FD(BPF_REG_1, 0), +			BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, +				     BPF_FUNC_map_lookup_elem), +			BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 8), +			BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, 0), +			BPF_MOV32_IMM(BPF_REG_2, MAX_ENTRIES), +			BPF_JMP_REG(BPF_JSGT, BPF_REG_2, BPF_REG_1, 1), +			BPF_MOV32_IMM(BPF_REG_1, 0), +			BPF_ALU32_IMM(BPF_LSH, BPF_REG_1, 2), +			BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), +			BPF_JMP_IMM(BPF_JA, 0, 0, 0), +			BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, +				   offsetof(struct test_val, foo)), +			BPF_EXIT_INSN(), +		}, +		.fixup_map2 = { 3 }, +		.errstr_unpriv = "R0 leaks addr", +		.errstr = "R0 unbounded memory access", +		.result_unpriv = REJECT, +		.result = REJECT, +		.flags = F_NEEDS_EFFICIENT_UNALIGNED_ACCESS, +	},  };  static int probe_filter_length(const struct bpf_insn *fp) diff --git a/tools/testing/selftests/bpf/test_xdp_redirect.c b/tools/testing/selftests/bpf/test_xdp_redirect.c new file mode 100644 index 000000000000..ef9e704be140 --- /dev/null +++ b/tools/testing/selftests/bpf/test_xdp_redirect.c @@ -0,0 +1,28 @@ +/* Copyright (c) 2017 VMware + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#include <linux/bpf.h> +#include "bpf_helpers.h" + +int _version SEC("version") = 1; + +SEC("redirect_to_111") +int xdp_redirect_to_111(struct xdp_md *xdp) +{ +	return bpf_redirect(111, 0); +} +SEC("redirect_to_222") +int xdp_redirect_to_222(struct xdp_md *xdp) +{ +	return bpf_redirect(222, 0); +} + +char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_xdp_redirect.sh b/tools/testing/selftests/bpf/test_xdp_redirect.sh new file mode 100755 index 000000000000..344a3656dea6 --- /dev/null +++ b/tools/testing/selftests/bpf/test_xdp_redirect.sh @@ -0,0 +1,59 @@ +#!/bin/sh +# Create 2 namespaces with two veth peers, and +# forward packets in-between using generic XDP +# +# NS1(veth11)     NS2(veth22) +#     |               | +#     |               | +#   (veth1, ------ (veth2, +#   id:111)         id:222) +#     | xdp forwarding | +#     ------------------ + +cleanup() +{ +	if [ "$?" = "0" ]; then +		echo "selftests: test_xdp_redirect [PASS]"; +	else +		echo "selftests: test_xdp_redirect [FAILED]"; +	fi + +	set +e +	ip netns del ns1 2> /dev/null +	ip netns del ns2 2> /dev/null +} + +ip link set dev lo xdpgeneric off 2>/dev/null > /dev/null +if [ $? -ne 0 ];then +	echo "selftests: [SKIP] Could not run test without the ip xdpgeneric support" +	exit 0 +fi +set -e + +ip netns add ns1 +ip netns add ns2 + +trap cleanup 0 2 3 6 9 + +ip link add veth1 index 111 type veth peer name veth11 +ip link add veth2 index 222 type veth peer name veth22 + +ip link set veth11 netns ns1 +ip link set veth22 netns ns2 + +ip link set veth1 up +ip link set veth2 up + +ip netns exec ns1 ip addr add 10.1.1.11/24 dev veth11 +ip netns exec ns2 ip addr add 10.1.1.22/24 dev veth22 + +ip netns exec ns1 ip link set dev veth11 up +ip netns exec ns2 ip link set dev veth22 up + +ip link set dev veth1 xdpgeneric obj test_xdp_redirect.o sec redirect_to_222 +ip link set dev veth2 xdpgeneric obj test_xdp_redirect.o sec redirect_to_111 + +ip netns exec ns1 ping -c 1 10.1.1.22 +ip netns exec ns2 ping -c 1 10.1.1.11 + +exit 0 diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c index f63356151ad4..901b85ea6a59 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test.c @@ -367,11 +367,11 @@ static void launch_tests(void)  	/* Icebp traps */  	ptrace(PTRACE_CONT, child_pid, NULL, 0); -	check_success("Test icebp"); +	check_success("Test icebp\n");  	/* Int 3 traps */  	ptrace(PTRACE_CONT, child_pid, NULL, 0); -	check_success("Test int 3 trap"); +	check_success("Test int 3 trap\n");  	ptrace(PTRACE_CONT, child_pid, NULL, 0);  } diff --git a/tools/testing/selftests/capabilities/test_execve.c b/tools/testing/selftests/capabilities/test_execve.c index 763f37fecfb8..cf6778441381 100644 --- a/tools/testing/selftests/capabilities/test_execve.c +++ b/tools/testing/selftests/capabilities/test_execve.c @@ -1,7 +1,6 @@  #define _GNU_SOURCE  #include <cap-ng.h> -#include <err.h>  #include <linux/capability.h>  #include <stdbool.h>  #include <string.h> @@ -18,6 +17,8 @@  #include <sys/prctl.h>  #include <sys/stat.h> +#include "../kselftest.h" +  #ifndef PR_CAP_AMBIENT  #define PR_CAP_AMBIENT			47  # define PR_CAP_AMBIENT_IS_SET		1 @@ -27,6 +28,7 @@  #endif  static int nerrs; +static pid_t mpid;	/*  main() pid is used to avoid duplicate test counts */  static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list ap)  { @@ -36,29 +38,32 @@ static void vmaybe_write_file(bool enoent_ok, char *filename, char *fmt, va_list  	int buf_len;  	buf_len = vsnprintf(buf, sizeof(buf), fmt, ap); -	if (buf_len < 0) { -		err(1, "vsnprintf failed"); -	} -	if (buf_len >= sizeof(buf)) { -		errx(1, "vsnprintf output truncated"); -	} +	if (buf_len < 0) +		ksft_exit_fail_msg("vsnprintf failed - %s\n", strerror(errno)); + +	if (buf_len >= sizeof(buf)) +		ksft_exit_fail_msg("vsnprintf output truncated\n"); +  	fd = open(filename, O_WRONLY);  	if (fd < 0) {  		if ((errno == ENOENT) && enoent_ok)  			return; -		err(1, "open of %s failed", filename); +		ksft_exit_fail_msg("open of %s failed - %s\n", +					filename, strerror(errno));  	}  	written = write(fd, buf, buf_len);  	if (written != buf_len) {  		if (written >= 0) { -			errx(1, "short write to %s", filename); +			ksft_exit_fail_msg("short write to %s\n", filename);  		} else { -			err(1, "write to %s failed", filename); +			ksft_exit_fail_msg("write to %s failed - %s\n", +						filename, strerror(errno));  		}  	}  	if (close(fd) != 0) { -		err(1, "close of %s failed", filename); +		ksft_exit_fail_msg("close of %s failed - %s\n", +					filename, strerror(errno));  	}  } @@ -95,11 +100,12 @@ static bool create_and_enter_ns(uid_t inner_uid)  	 */  	if (unshare(CLONE_NEWNS) == 0) { -		printf("[NOTE]\tUsing global UIDs for tests\n"); +		ksft_print_msg("[NOTE]\tUsing global UIDs for tests\n");  		if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) != 0) -			err(1, "PR_SET_KEEPCAPS"); +			ksft_exit_fail_msg("PR_SET_KEEPCAPS - %s\n", +						strerror(errno));  		if (setresuid(inner_uid, inner_uid, -1) != 0) -			err(1, "setresuid"); +			ksft_exit_fail_msg("setresuid - %s\n", strerror(errno));  		// Re-enable effective caps  		capng_get_caps_process(); @@ -107,22 +113,24 @@ static bool create_and_enter_ns(uid_t inner_uid)  			if (capng_have_capability(CAPNG_PERMITTED, i))  				capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, i);  		if (capng_apply(CAPNG_SELECT_CAPS) != 0) -			err(1, "capng_apply"); +			ksft_exit_fail_msg( +					"capng_apply - %s\n", strerror(errno));  		have_outer_privilege = true;  	} else if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0) { -		printf("[NOTE]\tUsing a user namespace for tests\n"); +		ksft_print_msg("[NOTE]\tUsing a user namespace for tests\n");  		maybe_write_file("/proc/self/setgroups", "deny");  		write_file("/proc/self/uid_map", "%d %d 1", inner_uid, outer_uid);  		write_file("/proc/self/gid_map", "0 %d 1", outer_gid);  		have_outer_privilege = false;  	} else { -		errx(1, "must be root or be able to create a userns"); +		ksft_exit_skip("must be root or be able to create a userns\n");  	}  	if (mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL) != 0) -		err(1, "remount everything private"); +		ksft_exit_fail_msg("remount everything private - %s\n", +					strerror(errno));  	return have_outer_privilege;  } @@ -131,20 +139,22 @@ static void chdir_to_tmpfs(void)  {  	char cwd[PATH_MAX];  	if (getcwd(cwd, sizeof(cwd)) != cwd) -		err(1, "getcwd"); +		ksft_exit_fail_msg("getcwd - %s\n", strerror(errno));  	if (mount("private_tmp", ".", "tmpfs", 0, "mode=0777") != 0) -		err(1, "mount private tmpfs"); +		ksft_exit_fail_msg("mount private tmpfs - %s\n", +					strerror(errno));  	if (chdir(cwd) != 0) -		err(1, "chdir to private tmpfs"); +		ksft_exit_fail_msg("chdir to private tmpfs - %s\n", +					strerror(errno));  }  static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)  {  	int from = openat(fromfd, fromname, O_RDONLY);  	if (from == -1) -		err(1, "open copy source"); +		ksft_exit_fail_msg("open copy source - %s\n", strerror(errno));  	int to = open(toname, O_CREAT | O_WRONLY | O_EXCL, 0700); @@ -154,10 +164,11 @@ static void copy_fromat_to(int fromfd, const char *fromname, const char *toname)  		if (sz == 0)  			break;  		if (sz < 0) -			err(1, "read"); +			ksft_exit_fail_msg("read - %s\n", strerror(errno));  		if (write(to, buf, sz) != sz) -			err(1, "write");	/* no short writes on tmpfs */ +			/* no short writes on tmpfs */ +			ksft_exit_fail_msg("write - %s\n", strerror(errno));  	}  	close(from); @@ -174,18 +185,20 @@ static bool fork_wait(void)  		int status;  		if (waitpid(child, &status, 0) != child ||  		    !WIFEXITED(status)) { -			printf("[FAIL]\tChild died\n"); +			ksft_print_msg("Child died\n");  			nerrs++;  		} else if (WEXITSTATUS(status) != 0) { -			printf("[FAIL]\tChild failed\n"); +			ksft_print_msg("Child failed\n");  			nerrs++;  		} else { -			printf("[OK]\tChild succeeded\n"); +			/* don't print this message for mpid */ +			if (getpid() != mpid) +				ksft_test_result_pass("Passed\n");  		} -  		return false;  	} else { -		err(1, "fork"); +		ksft_exit_fail_msg("fork - %s\n", strerror(errno)); +		return false;  	}  } @@ -195,7 +208,7 @@ static void exec_other_validate_cap(const char *name,  	execl(name, name, (eff ? "1" : "0"),  	      (perm ? "1" : "0"), (inh ? "1" : "0"), (ambient ? "1" : "0"),  	      NULL); -	err(1, "execl"); +	ksft_exit_fail_msg("execl - %s\n", strerror(errno));  }  static void exec_validate_cap(bool eff, bool perm, bool inh, bool ambient) @@ -209,7 +222,8 @@ static int do_tests(int uid, const char *our_path)  	int ourpath_fd = open(our_path, O_RDONLY | O_DIRECTORY);  	if (ourpath_fd == -1) -		err(1, "open '%s'", our_path); +		ksft_exit_fail_msg("open '%s' - %s\n", +					our_path, strerror(errno));  	chdir_to_tmpfs(); @@ -221,30 +235,30 @@ static int do_tests(int uid, const char *our_path)  		copy_fromat_to(ourpath_fd, "validate_cap",  			       "validate_cap_suidroot");  		if (chown("validate_cap_suidroot", 0, -1) != 0) -			err(1, "chown"); +			ksft_exit_fail_msg("chown - %s\n", strerror(errno));  		if (chmod("validate_cap_suidroot", S_ISUID | 0700) != 0) -			err(1, "chmod"); +			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));  		copy_fromat_to(ourpath_fd, "validate_cap",  			       "validate_cap_suidnonroot");  		if (chown("validate_cap_suidnonroot", uid + 1, -1) != 0) -			err(1, "chown"); +			ksft_exit_fail_msg("chown - %s\n", strerror(errno));  		if (chmod("validate_cap_suidnonroot", S_ISUID | 0700) != 0) -			err(1, "chmod"); +			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));  		copy_fromat_to(ourpath_fd, "validate_cap",  			       "validate_cap_sgidroot");  		if (chown("validate_cap_sgidroot", -1, 0) != 0) -			err(1, "chown"); +			ksft_exit_fail_msg("chown - %s\n", strerror(errno));  		if (chmod("validate_cap_sgidroot", S_ISGID | 0710) != 0) -			err(1, "chmod"); +			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));  		copy_fromat_to(ourpath_fd, "validate_cap",  			       "validate_cap_sgidnonroot");  		if (chown("validate_cap_sgidnonroot", -1, gid + 1) != 0) -			err(1, "chown"); +			ksft_exit_fail_msg("chown - %s\n", strerror(errno));  		if (chmod("validate_cap_sgidnonroot", S_ISGID | 0710) != 0) -			err(1, "chmod"); +			ksft_exit_fail_msg("chmod - %s\n", strerror(errno));  	}  	capng_get_caps_process(); @@ -252,147 +266,162 @@ static int do_tests(int uid, const char *our_path)  	/* Make sure that i starts out clear */  	capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);  	if (capng_apply(CAPNG_SELECT_CAPS) != 0) -		err(1, "capng_apply"); +		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));  	if (uid == 0) { -		printf("[RUN]\tRoot => ep\n"); +		ksft_print_msg("[RUN]\tRoot => ep\n");  		if (fork_wait())  			exec_validate_cap(true, true, false, false);  	} else { -		printf("[RUN]\tNon-root => no caps\n"); +		ksft_print_msg("[RUN]\tNon-root => no caps\n");  		if (fork_wait())  			exec_validate_cap(false, false, false, false);  	} -	printf("[OK]\tCheck cap_ambient manipulation rules\n"); +	ksft_print_msg("Check cap_ambient manipulation rules\n");  	/* We should not be able to add ambient caps yet. */  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != -1 || errno != EPERM) {  		if (errno == EINVAL) -			printf("[FAIL]\tPR_CAP_AMBIENT_RAISE isn't supported\n"); +			ksft_test_result_fail( +				"PR_CAP_AMBIENT_RAISE isn't supported\n");  		else -			printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have failed eith EPERM on a non-inheritable cap\n"); +			ksft_test_result_fail( +				"PR_CAP_AMBIENT_RAISE should have failed eith EPERM on a non-inheritable cap\n");  		return 1;  	} -	printf("[OK]\tPR_CAP_AMBIENT_RAISE failed on non-inheritable cap\n"); +	ksft_test_result_pass( +		"PR_CAP_AMBIENT_RAISE failed on non-inheritable cap\n");  	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_RAW);  	capng_update(CAPNG_DROP, CAPNG_PERMITTED, CAP_NET_RAW);  	capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_NET_RAW);  	if (capng_apply(CAPNG_SELECT_CAPS) != 0) -		err(1, "capng_apply"); +		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_RAW, 0, 0, 0) != -1 || errno != EPERM) { -		printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have failed on a non-permitted cap\n"); +		ksft_test_result_fail( +			"PR_CAP_AMBIENT_RAISE should have failed on a non-permitted cap\n");  		return 1;  	} -	printf("[OK]\tPR_CAP_AMBIENT_RAISE failed on non-permitted cap\n"); +	ksft_test_result_pass( +		"PR_CAP_AMBIENT_RAISE failed on non-permitted cap\n");  	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);  	if (capng_apply(CAPNG_SELECT_CAPS) != 0) -		err(1, "capng_apply"); +		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) { -		printf("[FAIL]\tPR_CAP_AMBIENT_RAISE should have succeeded\n"); +		ksft_test_result_fail( +			"PR_CAP_AMBIENT_RAISE should have succeeded\n");  		return 1;  	} -	printf("[OK]\tPR_CAP_AMBIENT_RAISE worked\n"); +	ksft_test_result_pass("PR_CAP_AMBIENT_RAISE worked\n");  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 1) { -		printf("[FAIL]\tPR_CAP_AMBIENT_IS_SET is broken\n"); +		ksft_test_result_fail("PR_CAP_AMBIENT_IS_SET is broken\n");  		return 1;  	}  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0, 0) != 0) -		err(1, "PR_CAP_AMBIENT_CLEAR_ALL"); +		ksft_exit_fail_msg("PR_CAP_AMBIENT_CLEAR_ALL - %s\n", +					strerror(errno));  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) { -		printf("[FAIL]\tPR_CAP_AMBIENT_CLEAR_ALL didn't work\n"); +		ksft_test_result_fail( +			"PR_CAP_AMBIENT_CLEAR_ALL didn't work\n");  		return 1;  	}  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) -		err(1, "PR_CAP_AMBIENT_RAISE"); +		ksft_exit_fail_msg("PR_CAP_AMBIENT_RAISE - %s\n", +					strerror(errno));  	capng_update(CAPNG_DROP, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);  	if (capng_apply(CAPNG_SELECT_CAPS) != 0) -		err(1, "capng_apply"); +		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) { -		printf("[FAIL]\tDropping I should have dropped A\n"); +		ksft_test_result_fail("Dropping I should have dropped A\n");  		return 1;  	} -	printf("[OK]\tBasic manipulation appears to work\n"); +	ksft_test_result_pass("Basic manipulation appears to work\n");  	capng_update(CAPNG_ADD, CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE);  	if (capng_apply(CAPNG_SELECT_CAPS) != 0) -		err(1, "capng_apply"); +		ksft_exit_fail_msg("capng_apply - %s\n", strerror(errno));  	if (uid == 0) { -		printf("[RUN]\tRoot +i => eip\n"); +		ksft_print_msg("[RUN]\tRoot +i => eip\n");  		if (fork_wait())  			exec_validate_cap(true, true, true, false);  	} else { -		printf("[RUN]\tNon-root +i => i\n"); +		ksft_print_msg("[RUN]\tNon-root +i => i\n");  		if (fork_wait())  			exec_validate_cap(false, false, true, false);  	}  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, CAP_NET_BIND_SERVICE, 0, 0, 0) != 0) -		err(1, "PR_CAP_AMBIENT_RAISE"); +		ksft_exit_fail_msg("PR_CAP_AMBIENT_RAISE - %s\n", +					strerror(errno)); -	printf("[RUN]\tUID %d +ia => eipa\n", uid); +	ksft_print_msg("[RUN]\tUID %d +ia => eipa\n", uid);  	if (fork_wait())  		exec_validate_cap(true, true, true, true);  	/* The remaining tests need real privilege */  	if (!have_outer_privilege) { -		printf("[SKIP]\tSUID/SGID tests (needs privilege)\n"); +		ksft_test_result_skip("SUID/SGID tests (needs privilege)\n");  		goto done;  	}  	if (uid == 0) { -		printf("[RUN]\tRoot +ia, suidroot => eipa\n"); +		ksft_print_msg("[RUN]\tRoot +ia, suidroot => eipa\n");  		if (fork_wait())  			exec_other_validate_cap("./validate_cap_suidroot",  						true, true, true, true); -		printf("[RUN]\tRoot +ia, suidnonroot => ip\n"); +		ksft_print_msg("[RUN]\tRoot +ia, suidnonroot => ip\n");  		if (fork_wait())  			exec_other_validate_cap("./validate_cap_suidnonroot",  						false, true, true, false); -		printf("[RUN]\tRoot +ia, sgidroot => eipa\n"); +		ksft_print_msg("[RUN]\tRoot +ia, sgidroot => eipa\n");  		if (fork_wait())  			exec_other_validate_cap("./validate_cap_sgidroot",  						true, true, true, true);  		if (fork_wait()) { -			printf("[RUN]\tRoot, gid != 0, +ia, sgidroot => eip\n"); +			ksft_print_msg( +				"[RUN]\tRoot, gid != 0, +ia, sgidroot => eip\n");  			if (setresgid(1, 1, 1) != 0) -				err(1, "setresgid"); +				ksft_exit_fail_msg("setresgid - %s\n", +							strerror(errno));  			exec_other_validate_cap("./validate_cap_sgidroot",  						true, true, true, false);  		} -		printf("[RUN]\tRoot +ia, sgidnonroot => eip\n"); +		ksft_print_msg("[RUN]\tRoot +ia, sgidnonroot => eip\n");  		if (fork_wait())  			exec_other_validate_cap("./validate_cap_sgidnonroot",  						true, true, true, false);  	} else { -		printf("[RUN]\tNon-root +ia, sgidnonroot => i\n"); -		exec_other_validate_cap("./validate_cap_sgidnonroot", +		ksft_print_msg("[RUN]\tNon-root +ia, sgidnonroot => i\n"); +		if (fork_wait()) +			exec_other_validate_cap("./validate_cap_sgidnonroot",  					false, false, true, false);  		if (fork_wait()) { -			printf("[RUN]\tNon-root +ia, sgidroot => i\n"); +			ksft_print_msg("[RUN]\tNon-root +ia, sgidroot => i\n");  			if (setresgid(1, 1, 1) != 0) -				err(1, "setresgid"); +				ksft_exit_fail_msg("setresgid - %s\n", +							strerror(errno));  			exec_other_validate_cap("./validate_cap_sgidroot",  						false, false, true, false);  		}  	}  done: +	ksft_print_cnts();  	return nerrs ? 1 : 0;  } @@ -400,23 +429,29 @@ int main(int argc, char **argv)  {  	char *tmp1, *tmp2, *our_path; +	ksft_print_header(); +  	/* Find our path */  	tmp1 = strdup(argv[0]);  	if (!tmp1) -		err(1, "strdup"); +		ksft_exit_fail_msg("strdup - %s\n", strerror(errno));  	tmp2 = dirname(tmp1);  	our_path = strdup(tmp2);  	if (!our_path) -		err(1, "strdup"); +		ksft_exit_fail_msg("strdup - %s\n", strerror(errno));  	free(tmp1); +	mpid = getpid(); +  	if (fork_wait()) { -		printf("[RUN]\t+++ Tests with uid == 0 +++\n"); +		ksft_print_msg("[RUN]\t+++ Tests with uid == 0 +++\n");  		return do_tests(0, our_path);  	} +	ksft_print_msg("==================================================\n"); +  	if (fork_wait()) { -		printf("[RUN]\t+++ Tests with uid != 0 +++\n"); +		ksft_print_msg("[RUN]\t+++ Tests with uid != 0 +++\n");  		return do_tests(1, our_path);  	} diff --git a/tools/testing/selftests/capabilities/validate_cap.c b/tools/testing/selftests/capabilities/validate_cap.c index dd3c45f7b23c..694cd73d4493 100644 --- a/tools/testing/selftests/capabilities/validate_cap.c +++ b/tools/testing/selftests/capabilities/validate_cap.c @@ -1,5 +1,4 @@  #include <cap-ng.h> -#include <err.h>  #include <linux/capability.h>  #include <stdbool.h>  #include <string.h> @@ -7,6 +6,8 @@  #include <sys/prctl.h>  #include <sys/auxv.h> +#include "../kselftest.h" +  #ifndef PR_CAP_AMBIENT  #define PR_CAP_AMBIENT			47  # define PR_CAP_AMBIENT_IS_SET		1 @@ -25,8 +26,10 @@ static bool bool_arg(char **argv, int i)  		return false;  	else if (!strcmp(argv[i], "1"))  		return true; -	else -		errx(1, "wrong argv[%d]", i); +	else { +		ksft_exit_fail_msg("wrong argv[%d]\n", i); +		return false; +	}  }  int main(int argc, char **argv) @@ -39,7 +42,7 @@ int main(int argc, char **argv)  	 */  	if (argc != 5) -		errx(1, "wrong argc"); +		ksft_exit_fail_msg("wrong argc\n");  #ifdef HAVE_GETAUXVAL  	if (getauxval(AT_SECURE)) @@ -51,23 +54,26 @@ int main(int argc, char **argv)  	capng_get_caps_process();  	if (capng_have_capability(CAPNG_EFFECTIVE, CAP_NET_BIND_SERVICE) != bool_arg(argv, 1)) { -		printf("[FAIL]\tWrong effective state%s\n", atsec); +		ksft_print_msg("Wrong effective state%s\n", atsec);  		return 1;  	} +  	if (capng_have_capability(CAPNG_PERMITTED, CAP_NET_BIND_SERVICE) != bool_arg(argv, 2)) { -		printf("[FAIL]\tWrong permitted state%s\n", atsec); +		ksft_print_msg("Wrong permitted state%s\n", atsec);  		return 1;  	} +  	if (capng_have_capability(CAPNG_INHERITABLE, CAP_NET_BIND_SERVICE) != bool_arg(argv, 3)) { -		printf("[FAIL]\tWrong inheritable state%s\n", atsec); +		ksft_print_msg("Wrong inheritable state%s\n", atsec);  		return 1;  	}  	if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_NET_BIND_SERVICE, 0, 0, 0) != bool_arg(argv, 4)) { -		printf("[FAIL]\tWrong ambient state%s\n", atsec); +		ksft_print_msg("Wrong ambient state%s\n", atsec);  		return 1;  	} -	printf("[OK]\tCapabilities after execve were correct\n"); +	ksft_print_msg("%s: Capabilities after execve were correct\n", +			"validate_cap:");  	return 0;  } diff --git a/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh b/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh index 98b1d6565f2c..b18b253d7bfb 100755 --- a/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh +++ b/tools/testing/selftests/cpu-hotplug/cpu-on-off-test.sh @@ -28,6 +28,12 @@ prerequisite()  	echo "CPU online/offline summary:"  	online_cpus=`cat $SYSFS/devices/system/cpu/online`  	online_max=${online_cpus##*-} + +	if [[ "$online_cpus" = "$online_max" ]]; then +		echo "$msg: since there is only one cpu: $online_cpus" +		exit 0 +	fi +  	echo -e "\t Cpus in online state: $online_cpus"  	offline_cpus=`cat $SYSFS/devices/system/cpu/offline` @@ -89,8 +95,10 @@ online_cpu_expect_success()  	if ! online_cpu $cpu; then  		echo $FUNCNAME $cpu: unexpected fail >&2 +		exit 1  	elif ! cpu_is_online $cpu; then  		echo $FUNCNAME $cpu: unexpected offline >&2 +		exit 1  	fi  } @@ -100,8 +108,10 @@ online_cpu_expect_fail()  	if online_cpu $cpu 2> /dev/null; then  		echo $FUNCNAME $cpu: unexpected success >&2 +		exit 1  	elif ! cpu_is_offline $cpu; then  		echo $FUNCNAME $cpu: unexpected online >&2 +		exit 1  	fi  } @@ -111,8 +121,10 @@ offline_cpu_expect_success()  	if ! offline_cpu $cpu; then  		echo $FUNCNAME $cpu: unexpected fail >&2 +		exit 1  	elif ! cpu_is_offline $cpu; then  		echo $FUNCNAME $cpu: unexpected offline >&2 +		exit 1  	fi  } @@ -122,8 +134,10 @@ offline_cpu_expect_fail()  	if offline_cpu $cpu 2> /dev/null; then  		echo $FUNCNAME $cpu: unexpected success >&2 +		exit 1  	elif ! cpu_is_online $cpu; then  		echo $FUNCNAME $cpu: unexpected offline >&2 +		exit 1  	fi  } diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh index 2e4c22d5abf7..8f511035f783 100755 --- a/tools/testing/selftests/firmware/fw_fallback.sh +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -134,6 +134,27 @@ load_fw_custom_cancel()  	wait  } +load_fw_fallback_with_child() +{ +	local name="$1" +	local file="$2" + +	# This is the value already set but we want to be explicit +	echo 4 >/sys/class/firmware/timeout + +	sleep 1 & +	SECONDS_BEFORE=$(date +%s) +	echo -n "$name" >"$DIR"/trigger_request 2>/dev/null +	SECONDS_AFTER=$(date +%s) +	SECONDS_DELTA=$(($SECONDS_AFTER - $SECONDS_BEFORE)) +	if [ "$SECONDS_DELTA" -lt 4 ]; then +		RET=1 +	else +		RET=0 +	fi +	wait +	return $RET +}  trap "test_finish" EXIT @@ -221,4 +242,14 @@ else  	echo "$0: cancelling custom fallback mechanism works"  fi +set +e +load_fw_fallback_with_child "nope-signal-$NAME" "$FW" +if [ "$?" -eq 0 ]; then +	echo "$0: SIGCHLD on sync ignored as expected" >&2 +else +	echo "$0: error - sync firmware request cancelled due to SIGCHLD" >&2 +	exit 1 +fi +set -e +  exit 0 diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index e35691239350..7d8fd2e3695a 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -25,8 +25,9 @@ if [ ! -d $DIR ]; then  fi  # CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ -# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that -# as an indicator for CONFIG_FW_LOADER_USER_HELPER. +# These days most distros enable CONFIG_FW_LOADER_USER_HELPER but disable +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK. We use /sys/class/firmware/ as an +# indicator for CONFIG_FW_LOADER_USER_HELPER.  HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi)  if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then @@ -116,4 +117,240 @@ else  	echo "$0: async filesystem loading works"  fi +### Batched requests tests +test_config_present() +{ +	if [ ! -f $DIR/reset ]; then +		echo "Configuration triggers not present, ignoring test" +		exit 0 +	fi +} + +# Defaults : +# +# send_uevent: 1 +# sync_direct: 0 +# name: test-firmware.bin +# num_requests: 4 +config_reset() +{ +	echo 1 >  $DIR/reset +} + +release_all_firmware() +{ +	echo 1 >  $DIR/release_all_firmware +} + +config_set_name() +{ +	echo -n $1 >  $DIR/config_name +} + +config_set_sync_direct() +{ +	echo 1 >  $DIR/config_sync_direct +} + +config_unset_sync_direct() +{ +	echo 0 >  $DIR/config_sync_direct +} + +config_set_uevent() +{ +	echo 1 >  $DIR/config_send_uevent +} + +config_unset_uevent() +{ +	echo 0 >  $DIR/config_send_uevent +} + +config_trigger_sync() +{ +	echo -n 1 > $DIR/trigger_batched_requests 2>/dev/null +} + +config_trigger_async() +{ +	echo -n 1 > $DIR/trigger_batched_requests_async 2> /dev/null +} + +config_set_read_fw_idx() +{ +	echo -n $1 > $DIR/config_read_fw_idx 2> /dev/null +} + +read_firmwares() +{ +	for i in $(seq 0 3); do +		config_set_read_fw_idx $i +		# Verify the contents are what we expect. +		# -Z required for now -- check for yourself, md5sum +		# on $FW and DIR/read_firmware will yield the same. Even +		# cmp agrees, so something is off. +		if ! diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then +			echo "request #$i: firmware was not loaded" >&2 +			exit 1 +		fi +	done +} + +read_firmwares_expect_nofile() +{ +	for i in $(seq 0 3); do +		config_set_read_fw_idx $i +		# Ensures contents differ +		if diff -q -Z "$FW" $DIR/read_firmware 2>/dev/null ; then +			echo "request $i: file was not expected to match" >&2 +			exit 1 +		fi +	done +} + +test_batched_request_firmware_nofile() +{ +	echo -n "Batched request_firmware() nofile try #$1: " +	config_reset +	config_set_name nope-test-firmware.bin +	config_trigger_sync +	read_firmwares_expect_nofile +	release_all_firmware +	echo "OK" +} + +test_batched_request_firmware_direct_nofile() +{ +	echo -n "Batched request_firmware_direct() nofile try #$1: " +	config_reset +	config_set_name nope-test-firmware.bin +	config_set_sync_direct +	config_trigger_sync +	release_all_firmware +	echo "OK" +} + +test_request_firmware_nowait_uevent_nofile() +{ +	echo -n "Batched request_firmware_nowait(uevent=true) nofile try #$1: " +	config_reset +	config_set_name nope-test-firmware.bin +	config_trigger_async +	release_all_firmware +	echo "OK" +} + +test_wait_and_cancel_custom_load() +{ +	if [ "$HAS_FW_LOADER_USER_HELPER" != "yes" ]; then +		return +	fi +	local timeout=10 +	name=$1 +	while [ ! -e "$DIR"/"$name"/loading ]; do +		sleep 0.1 +		timeout=$(( $timeout - 1 )) +		if [ "$timeout" -eq 0 ]; then +			echo "firmware interface never appeared:" >&2 +			echo "$DIR/$name/loading" >&2 +			exit 1 +		fi +	done +	echo -1 >"$DIR"/"$name"/loading +} + +test_request_firmware_nowait_custom_nofile() +{ +	echo -n "Batched request_firmware_nowait(uevent=false) nofile try #$1: " +	config_unset_uevent +	config_set_name nope-test-firmware.bin +	config_trigger_async & +	test_wait_and_cancel_custom_load nope-test-firmware.bin +	wait +	release_all_firmware +	echo "OK" +} + +test_batched_request_firmware() +{ +	echo -n "Batched request_firmware() try #$1: " +	config_reset +	config_trigger_sync +	read_firmwares +	release_all_firmware +	echo "OK" +} + +test_batched_request_firmware_direct() +{ +	echo -n "Batched request_firmware_direct() try #$1: " +	config_reset +	config_set_sync_direct +	config_trigger_sync +	release_all_firmware +	echo "OK" +} + +test_request_firmware_nowait_uevent() +{ +	echo -n "Batched request_firmware_nowait(uevent=true) try #$1: " +	config_reset +	config_trigger_async +	release_all_firmware +	echo "OK" +} + +test_request_firmware_nowait_custom() +{ +	echo -n "Batched request_firmware_nowait(uevent=false) try #$1: " +	config_unset_uevent +	config_trigger_async +	release_all_firmware +	echo "OK" +} + +# Only continue if batched request triggers are present on the +# test-firmware driver +test_config_present + +# test with the file present +echo +echo "Testing with the file present..." +for i in $(seq 1 5); do +	test_batched_request_firmware $i +done + +for i in $(seq 1 5); do +	test_batched_request_firmware_direct $i +done + +for i in $(seq 1 5); do +	test_request_firmware_nowait_uevent $i +done + +for i in $(seq 1 5); do +	test_request_firmware_nowait_custom $i +done + +# Test for file not found, errors are expected, the failure would be +# a hung task, which would require a hard reset. +echo +echo "Testing with the file missing..." +for i in $(seq 1 5); do +	test_batched_request_firmware_nofile $i +done + +for i in $(seq 1 5); do +	test_batched_request_firmware_direct_nofile $i +done + +for i in $(seq 1 5); do +	test_request_firmware_nowait_uevent_nofile $i +done + +for i in $(seq 1 5); do +	test_request_firmware_nowait_custom_nofile $i +done +  exit 0 diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 14a03ea1e21d..abc706cf7702 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -8,15 +8,18 @@  # Released under the terms of the GPL v2.  usage() { # errno [message] -[ "$2" ] && echo $2 +[ ! -z "$2" ] && echo $2  echo "Usage: ftracetest [options] [testcase(s)] [testcase-directory(s)]"  echo " Options:"  echo "		-h|--help  Show help message"  echo "		-k|--keep  Keep passed test logs"  echo "		-v|--verbose Increase verbosity of test messages"  echo "		-vv        Alias of -v -v (Show all results in stdout)" +echo "		-vvv       Alias of -v -v -v (Show all commands immediately)" +echo "		--fail-unsupported Treat UNSUPPORTED as a failure"  echo "		-d|--debug Debug mode (trace all shell commands)"  echo "		-l|--logdir <dir> Save logs on the <dir>" +echo "		            If <dir> is -, all logs output in console only"  exit $1  } @@ -47,7 +50,7 @@ parse_opts() { # opts    local OPT_TEST_CASES=    local OPT_TEST_DIR= -  while [ "$1" ]; do +  while [ ! -z "$1" ]; do      case "$1" in      --help|-h)        usage 0 @@ -56,15 +59,20 @@ parse_opts() { # opts        KEEP_LOG=1        shift 1      ;; -    --verbose|-v|-vv) +    --verbose|-v|-vv|-vvv)        VERBOSE=$((VERBOSE + 1))        [ $1 = '-vv' ] && VERBOSE=$((VERBOSE + 1)) +      [ $1 = '-vvv' ] && VERBOSE=$((VERBOSE + 2))        shift 1      ;;      --debug|-d)        DEBUG=1        shift 1      ;; +    --fail-unsupported) +      UNSUPPORTED_RESULT=1 +      shift 1 +    ;;      --logdir|-l)        LOG_DIR=$2        shift 2 @@ -88,7 +96,7 @@ parse_opts() { # opts      ;;      esac    done -  if [ "$OPT_TEST_CASES" ]; then +  if [ ! -z "$OPT_TEST_CASES" ]; then      TEST_CASES=$OPT_TEST_CASES    fi  } @@ -108,6 +116,7 @@ LOG_DIR=$TOP_DIR/logs/`date +%Y%m%d-%H%M%S`/  KEEP_LOG=0  DEBUG=0  VERBOSE=0 +UNSUPPORTED_RESULT=0  # Parse command-line options  parse_opts $* @@ -119,14 +128,20 @@ if [ -z "$TRACING_DIR" -o ! -d "$TRACING_DIR" ]; then  fi  # Preparing logs -LOG_FILE=$LOG_DIR/ftracetest.log -mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" -date > $LOG_FILE +if [ "x$LOG_DIR" = "x-" ]; then +  LOG_FILE= +  date +else +  LOG_FILE=$LOG_DIR/ftracetest.log +  mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" +  date > $LOG_FILE +fi +  prlog() { # messages -  echo "$@" | tee -a $LOG_FILE +  [ -z "$LOG_FILE" ] && echo "$@" || echo "$@" | tee -a $LOG_FILE  }  catlog() { #file -  cat $1 | tee -a $LOG_FILE +  [ -z "$LOG_FILE" ] && cat $1 || cat $1 | tee -a $LOG_FILE  }  prlog "=== Ftrace unit tests ===" @@ -187,7 +202,7 @@ eval_result() { # sigval      $UNSUPPORTED)        prlog "	[UNSUPPORTED]"        UNSUPPORTED_CASES="$UNSUPPORTED_CASES $CASENO" -      return 1 # this is not a bug, but the result should be reported. +      return $UNSUPPORTED_RESULT # depends on use case      ;;      $XFAIL)        prlog "	[XFAIL]" @@ -247,12 +262,20 @@ __run_test() { # testfile  # Run one test case  run_test() { # testfile    local testname=`basename $1` -  local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX` +  if [ ! -z "$LOG_FILE" ] ; then +    local testlog=`mktemp $LOG_DIR/${testname}-log.XXXXXX` +  else +    local testlog=/proc/self/fd/1 +  fi    export TMPDIR=`mktemp -d /tmp/ftracetest-dir.XXXXXX`    testcase $1    echo "execute$INSTANCE: "$1 > $testlog    SIG_RESULT=0 -  if [ $VERBOSE -ge 2 ]; then +  if [ -z "$LOG_FILE" ]; then +    __run_test $1 2>&1 +  elif [ $VERBOSE -ge 3 ]; then +    __run_test $1 | tee -a $testlog 2>&1 +  elif [ $VERBOSE -eq 2 ]; then      __run_test $1 2>> $testlog | tee -a $testlog    else      __run_test $1 >> $testlog 2>&1 @@ -260,9 +283,9 @@ run_test() { # testfile    eval_result $SIG_RESULT    if [ $? -eq 0 ]; then      # Remove test log if the test was done as it was expected. -    [ $KEEP_LOG -eq 0 ] && rm $testlog +    [ $KEEP_LOG -eq 0 -a ! -z "$LOG_FILE" ] && rm $testlog    else -    [ $VERBOSE -ge 1 ] && catlog $testlog +    [ $VERBOSE -eq 1 -o $VERBOSE -eq 2 ] && catlog $testlog      TOTAL_RESULT=1    fi    rm -rf $TMPDIR diff --git a/tools/testing/selftests/futex/Makefile b/tools/testing/selftests/futex/Makefile index e2fbb890aef9..7c647f619d63 100644 --- a/tools/testing/selftests/futex/Makefile +++ b/tools/testing/selftests/futex/Makefile @@ -14,7 +14,7 @@ all:  	done  override define RUN_TESTS -	@if [ `dirname $(OUTPUT)` = $(PWD) ]; then ./run.sh; fi +	$(OUTPUT)/run.sh  endef  override define INSTALL_RULE diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c index d24ab7421e73..54cd5c414e82 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c @@ -394,9 +394,11 @@ int main(int argc, char *argv[])  		}  	} -	printf("%s: Test requeue functionality\n", basename(argv[0])); -	printf("\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n", -	       broadcast, locked, owner, timeout_ns); +	ksft_print_header(); +	ksft_print_msg("%s: Test requeue functionality\n", basename(argv[0])); +	ksft_print_msg( +		"\tArguments: broadcast=%d locked=%d owner=%d timeout=%ldns\n", +		broadcast, locked, owner, timeout_ns);  	/*  	 * FIXME: unit_test is obsolete now that we parse options and the diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c index e0a798ad0d21..08187a16507f 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_mismatched_ops.c @@ -78,7 +78,8 @@ int main(int argc, char *argv[])  		}  	} -	printf("%s: Detect mismatched requeue_pi operations\n", +	ksft_print_header(); +	ksft_print_msg("%s: Detect mismatched requeue_pi operations\n",  	       basename(argv[0]));  	if (pthread_create(&child, NULL, blocking_child, NULL)) { diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c index 982f83577501..f0542a344d95 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi_signal_restart.c @@ -143,9 +143,10 @@ int main(int argc, char *argv[])  		}  	} -	printf("%s: Test signal handling during requeue_pi\n", +	ksft_print_header(); +	ksft_print_msg("%s: Test signal handling during requeue_pi\n",  	       basename(argv[0])); -	printf("\tArguments: <none>\n"); +	ksft_print_msg("\tArguments: <none>\n");  	sa.sa_handler = handle_signal;  	sigemptyset(&sa.sa_mask); diff --git a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c index bdc48dc047e5..6216de828093 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c +++ b/tools/testing/selftests/futex/functional/futex_wait_private_mapped_file.c @@ -97,8 +97,10 @@ int main(int argc, char **argv)  		}  	} -	printf("%s: Test the futex value of private file mappings in FUTEX_WAIT\n", -	       basename(argv[0])); +	ksft_print_header(); +	ksft_print_msg( +		"%s: Test the futex value of private file mappings in FUTEX_WAIT\n", +		basename(argv[0]));  	ret = pthread_create(&thr, NULL, thr_futex_wait, NULL);  	if (ret < 0) { diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c index 6aadd560366e..bab3dfe1787f 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c +++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c @@ -68,9 +68,10 @@ int main(int argc, char *argv[])  		}  	} -	printf("%s: Block on a futex and wait for timeout\n", +	ksft_print_header(); +	ksft_print_msg("%s: Block on a futex and wait for timeout\n",  	       basename(argv[0])); -	printf("\tArguments: timeout=%ldns\n", timeout_ns); +	ksft_print_msg("\tArguments: timeout=%ldns\n", timeout_ns);  	/* initialize timeout */  	to.tv_sec = 0; diff --git a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c index d237a8b702f0..26975322545b 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c +++ b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c @@ -99,7 +99,8 @@ int main(int argc, char **argv)  		exit(1);  	} -	printf("%s: Test the uninitialized futex value in FUTEX_WAIT\n", +	ksft_print_header(); +	ksft_print_msg("%s: Test the uninitialized futex value in FUTEX_WAIT\n",  	       basename(argv[0])); diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c index 9a2c56fa7305..da15a63269b4 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c +++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c @@ -64,7 +64,8 @@ int main(int argc, char *argv[])  		}  	} -	printf("%s: Test the unexpected futex value in FUTEX_WAIT\n", +	ksft_print_header(); +	ksft_print_msg("%s: Test the unexpected futex value in FUTEX_WAIT\n",  	       basename(argv[0]));  	info("Calling futex_wait on f1: %u @ %p with val=%u\n", f1, &f1, f1+1); diff --git a/tools/testing/selftests/futex/include/logging.h b/tools/testing/selftests/futex/include/logging.h index 4e7944984fbb..01989644e50a 100644 --- a/tools/testing/selftests/futex/include/logging.h +++ b/tools/testing/selftests/futex/include/logging.h @@ -109,22 +109,20 @@ void log_verbosity(int level)   */  void print_result(const char *test_name, int ret)  { -	const char *result = "Unknown return code"; -  	switch (ret) {  	case RET_PASS: -		ksft_inc_pass_cnt(); -		result = PASS; -		break; +		ksft_test_result_pass("%s\n", test_name); +		ksft_print_cnts(); +		return;  	case RET_ERROR: -		result = ERROR; -		break; +		ksft_test_result_error("%s\n", test_name); +		ksft_print_cnts(); +		return;  	case RET_FAIL: -		ksft_inc_fail_cnt(); -		result = FAIL; -		break; +		ksft_test_result_fail("%s\n", test_name); +		ksft_print_cnts(); +		return;  	} -	printf("selftests: %s [%s]\n", test_name, result);  }  /* log level macros */ diff --git a/tools/testing/selftests/kmod/kmod.sh b/tools/testing/selftests/kmod/kmod.sh index 8cecae9a8bca..7956ea3be667 100644..100755 --- a/tools/testing/selftests/kmod/kmod.sh +++ b/tools/testing/selftests/kmod/kmod.sh @@ -473,8 +473,8 @@ usage()  	echo "    all     Runs all tests (default)"  	echo "    -t      Run test ID the number amount of times is recommended"  	echo "    -w      Watch test ID run until it runs into an error" -	echo "    -c      Run test ID once" -	echo "    -s      Run test ID x test-count number of times" +	echo "    -s      Run test ID once" +	echo "    -c      Run test ID x test-count number of times"  	echo "    -l      List all test ID list"  	echo " -h|--help  Help"  	echo diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index 08e90c2cc5cb..1ae565ed9bf0 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -19,7 +19,8 @@  #define KSFT_FAIL  1  #define KSFT_XFAIL 2  #define KSFT_XPASS 3 -#define KSFT_SKIP  4 +/* Treat skip as pass */ +#define KSFT_SKIP  KSFT_PASS  /* counters */  struct ksft_count { @@ -28,6 +29,7 @@ struct ksft_count {  	unsigned int ksft_xfail;  	unsigned int ksft_xpass;  	unsigned int ksft_xskip; +	unsigned int ksft_error;  };  static struct ksft_count ksft_cnt; @@ -36,7 +38,7 @@ static inline int ksft_test_num(void)  {  	return ksft_cnt.ksft_pass + ksft_cnt.ksft_fail +  		ksft_cnt.ksft_xfail + ksft_cnt.ksft_xpass + -		ksft_cnt.ksft_xskip; +		ksft_cnt.ksft_xskip + ksft_cnt.ksft_error;  }  static inline void ksft_inc_pass_cnt(void) { ksft_cnt.ksft_pass++; } @@ -44,6 +46,14 @@ static inline void ksft_inc_fail_cnt(void) { ksft_cnt.ksft_fail++; }  static inline void ksft_inc_xfail_cnt(void) { ksft_cnt.ksft_xfail++; }  static inline void ksft_inc_xpass_cnt(void) { ksft_cnt.ksft_xpass++; }  static inline void ksft_inc_xskip_cnt(void) { ksft_cnt.ksft_xskip++; } +static inline void ksft_inc_error_cnt(void) { ksft_cnt.ksft_error++; } + +static inline int ksft_get_pass_cnt(void) { return ksft_cnt.ksft_pass; } +static inline int ksft_get_fail_cnt(void) { return ksft_cnt.ksft_fail; } +static inline int ksft_get_xfail_cnt(void) { return ksft_cnt.ksft_xfail; } +static inline int ksft_get_xpass_cnt(void) { return ksft_cnt.ksft_xpass; } +static inline int ksft_get_xskip_cnt(void) { return ksft_cnt.ksft_xskip; } +static inline int ksft_get_error_cnt(void) { return ksft_cnt.ksft_error; }  static inline void ksft_print_header(void)  { @@ -52,6 +62,10 @@ static inline void ksft_print_header(void)  static inline void ksft_print_cnts(void)  { +	printf("Pass %d Fail %d Xfail %d Xpass %d Skip %d Error %d\n", +		ksft_cnt.ksft_pass, ksft_cnt.ksft_fail, +		ksft_cnt.ksft_xfail, ksft_cnt.ksft_xpass, +		ksft_cnt.ksft_xskip, ksft_cnt.ksft_error);  	printf("1..%d\n", ksft_test_num());  } @@ -101,6 +115,18 @@ static inline void ksft_test_result_skip(const char *msg, ...)  	va_end(args);  } +static inline void ksft_test_result_error(const char *msg, ...) +{ +	va_list args; + +	ksft_cnt.ksft_error++; + +	va_start(args, msg); +	printf("not ok %d # error ", ksft_test_num()); +	vprintf(msg, args); +	va_end(args); +} +  static inline int ksft_exit_pass(void)  {  	ksft_print_cnts(); diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h index c56f72e07cd7..e81bd28bdd89 100644 --- a/tools/testing/selftests/kselftest_harness.h +++ b/tools/testing/selftests/kselftest_harness.h @@ -51,6 +51,9 @@  #define __KSELFTEST_HARNESS_H  #define _GNU_SOURCE +#include <asm/types.h> +#include <errno.h> +#include <stdbool.h>  #include <stdint.h>  #include <stdio.h>  #include <stdlib.h> @@ -84,6 +87,14 @@   * E.g., #define TH_LOG_ENABLED 1   *   * If no definition is provided, logging is enabled by default. + * + * If there is no way to print an error message for the process running the + * test (e.g. not allowed to write to stderr), it is still possible to get the + * ASSERT_* number for which the test failed.  This behavior can be enabled by + * writing `_metadata->no_print = true;` before the check sequence that is + * unable to print.  When an error occur, instead of printing an error message + * and calling `abort(3)`, the test process call `_exit(2)` with the assert + * number as argument, which is then printed by the parent process.   */  #define TH_LOG(fmt, ...) do { \  	if (TH_LOG_ENABLED) \ @@ -555,12 +566,18 @@   * return while still providing an optional block to the API consumer.   */  #define OPTIONAL_HANDLER(_assert) \ -	for (; _metadata->trigger;  _metadata->trigger = __bail(_assert)) +	for (; _metadata->trigger; _metadata->trigger = \ +			__bail(_assert, _metadata->no_print, _metadata->step)) + +#define __INC_STEP(_metadata) \ +	if (_metadata->passed && _metadata->step < 255) \ +		_metadata->step++;  #define __EXPECT(_expected, _seen, _t, _assert) do { \  	/* Avoid multiple evaluation of the cases */ \  	__typeof__(_expected) __exp = (_expected); \  	__typeof__(_seen) __seen = (_seen); \ +	if (_assert) __INC_STEP(_metadata); \  	if (!(__exp _t __seen)) { \  		unsigned long long __exp_print = (uintptr_t)__exp; \  		unsigned long long __seen_print = (uintptr_t)__seen; \ @@ -576,6 +593,7 @@  #define __EXPECT_STR(_expected, _seen, _t, _assert) do { \  	const char *__exp = (_expected); \  	const char *__seen = (_seen); \ +	if (_assert) __INC_STEP(_metadata); \  	if (!(strcmp(__exp, __seen) _t 0))  { \  		__TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \  		_metadata->passed = 0; \ @@ -590,6 +608,8 @@ struct __test_metadata {  	int termsig;  	int passed;  	int trigger; /* extra handler after the evaluation */ +	__u8 step; +	bool no_print; /* manual trigger when TH_LOG_STREAM is not available */  	struct __test_metadata *prev, *next;  }; @@ -634,10 +654,13 @@ static inline void __register_test(struct __test_metadata *t)  	}  } -static inline int __bail(int for_realz) +static inline int __bail(int for_realz, bool no_print, __u8 step)  { -	if (for_realz) +	if (for_realz) { +		if (no_print) +			_exit(step);  		abort(); +	}  	return 0;  } @@ -655,18 +678,24 @@ void __run_test(struct __test_metadata *t)  		t->passed = 0;  	} else if (child_pid == 0) {  		t->fn(t); -		_exit(t->passed); +		/* return the step that failed or 0 */ +		_exit(t->passed ? 0 : t->step);  	} else {  		/* TODO(wad) add timeout support. */  		waitpid(child_pid, &status, 0);  		if (WIFEXITED(status)) { -			t->passed = t->termsig == -1 ? WEXITSTATUS(status) : 0; +			t->passed = t->termsig == -1 ? !WEXITSTATUS(status) : 0;  			if (t->termsig != -1) {  				fprintf(TH_LOG_STREAM,  					"%s: Test exited normally "  					"instead of by signal (code: %d)\n",  					t->name,  					WEXITSTATUS(status)); +			} else if (!t->passed) { +				fprintf(TH_LOG_STREAM, +					"%s: Test failed at step #%d\n", +					t->name, +					WEXITSTATUS(status));  			}  		} else if (WIFSIGNALED(status)) {  			t->passed = 0; diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index 959273c3a52e..693616651da5 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -11,15 +11,26 @@ TEST_GEN_FILES := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_FILES))  all: $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) +.ONESHELL:  define RUN_TESTS -	@for TEST in $(TEST_GEN_PROGS) $(TEST_PROGS); do \ +	@test_num=`echo 0`; +	@echo "TAP version 13"; +	@for TEST in $(1); do				\  		BASENAME_TEST=`basename $$TEST`;	\ -		cd `dirname $$TEST`; (./$$BASENAME_TEST && echo "selftests: $$BASENAME_TEST [PASS]") || echo "selftests:  $$BASENAME_TEST [FAIL]"; cd -;\ +		test_num=`echo $$test_num+1 | bc`;	\ +		echo "selftests: $$BASENAME_TEST";	\ +		echo "========================================";	\ +		if [ ! -x $$BASENAME_TEST ]; then	\ +			echo "selftests: Warning: file $$BASENAME_TEST is not executable, correct this.";\ +			echo "not ok 1..$$test_num selftests: $$BASENAME_TEST [FAIL]"; \ +		else					\ +			cd `dirname $$TEST` > /dev/null; (./$$BASENAME_TEST && echo "ok 1..$$test_num selftests: $$BASENAME_TEST [PASS]") || echo "not ok 1..$$test_num selftests:  $$BASENAME_TEST [FAIL]"; cd - > /dev/null;\ +		fi;					\  	done;  endef  run_tests: all -	$(RUN_TESTS) +	$(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_PROGS))  define INSTALL_RULE  	@if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then					\ diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile index ad8a0897e47f..bc9d02d615da 100644 --- a/tools/testing/selftests/memfd/Makefile +++ b/tools/testing/selftests/memfd/Makefile @@ -3,7 +3,7 @@ CFLAGS += -I../../../../include/uapi/  CFLAGS += -I../../../../include/  CFLAGS += -I../../../../usr/include/ -TEST_PROGS := run_fuse_test.sh +TEST_PROGS := run_tests.sh  TEST_GEN_FILES := memfd_test fuse_mnt fuse_test  fuse_mnt.o: CFLAGS += $(shell pkg-config fuse --cflags) diff --git a/tools/testing/selftests/memfd/fuse_test.c b/tools/testing/selftests/memfd/fuse_test.c index 67908b18f035..7f3617274bf5 100644 --- a/tools/testing/selftests/memfd/fuse_test.c +++ b/tools/testing/selftests/memfd/fuse_test.c @@ -33,7 +33,7 @@  #include <unistd.h>  #define MFD_DEF_SIZE 8192 -#define STACK_SIZE 65535 +#define STACK_SIZE 65536  static int sys_memfd_create(const char *name,  			    unsigned int flags) diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 26546892cd54..f94c6d1fb46f 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -18,12 +18,48 @@  #include <sys/wait.h>  #include <unistd.h> +#define MEMFD_STR	"memfd:" +#define SHARED_FT_STR	"(shared file-table)" +  #define MFD_DEF_SIZE 8192  #define STACK_SIZE 65536 +/* + * Default is not to test hugetlbfs + */ +static int hugetlbfs_test; +static size_t mfd_def_size = MFD_DEF_SIZE; + +/* + * Copied from mlock2-tests.c + */ +static unsigned long default_huge_page_size(void) +{ +	unsigned long hps = 0; +	char *line = NULL; +	size_t linelen = 0; +	FILE *f = fopen("/proc/meminfo", "r"); + +	if (!f) +		return 0; +	while (getline(&line, &linelen, f) > 0) { +		if (sscanf(line, "Hugepagesize:       %lu kB", &hps) == 1) { +			hps <<= 10; +			break; +		} +	} + +	free(line); +	fclose(f); +	return hps; +} +  static int sys_memfd_create(const char *name,  			    unsigned int flags)  { +	if (hugetlbfs_test) +		flags |= MFD_HUGETLB; +  	return syscall(__NR_memfd_create, name, flags);  } @@ -150,7 +186,7 @@ static void *mfd_assert_mmap_shared(int fd)  	void *p;  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ | PROT_WRITE,  		 MAP_SHARED,  		 fd, @@ -168,7 +204,7 @@ static void *mfd_assert_mmap_private(int fd)  	void *p;  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ,  		 MAP_PRIVATE,  		 fd, @@ -223,7 +259,7 @@ static void mfd_assert_read(int fd)  	/* verify PROT_READ *is* allowed */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ,  		 MAP_PRIVATE,  		 fd, @@ -232,11 +268,11 @@ static void mfd_assert_read(int fd)  		printf("mmap() failed: %m\n");  		abort();  	} -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size);  	/* verify MAP_PRIVATE is *always* allowed (even writable) */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ | PROT_WRITE,  		 MAP_PRIVATE,  		 fd, @@ -245,7 +281,7 @@ static void mfd_assert_read(int fd)  		printf("mmap() failed: %m\n");  		abort();  	} -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size);  }  static void mfd_assert_write(int fd) @@ -254,16 +290,22 @@ static void mfd_assert_write(int fd)  	void *p;  	int r; -	/* verify write() succeeds */ -	l = write(fd, "\0\0\0\0", 4); -	if (l != 4) { -		printf("write() failed: %m\n"); -		abort(); +	/* +	 * huegtlbfs does not support write, but we want to +	 * verify everything else here. +	 */ +	if (!hugetlbfs_test) { +		/* verify write() succeeds */ +		l = write(fd, "\0\0\0\0", 4); +		if (l != 4) { +			printf("write() failed: %m\n"); +			abort(); +		}  	}  	/* verify PROT_READ | PROT_WRITE is allowed */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ | PROT_WRITE,  		 MAP_SHARED,  		 fd, @@ -273,11 +315,11 @@ static void mfd_assert_write(int fd)  		abort();  	}  	*(char *)p = 0; -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size);  	/* verify PROT_WRITE is allowed */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_WRITE,  		 MAP_SHARED,  		 fd, @@ -287,12 +329,12 @@ static void mfd_assert_write(int fd)  		abort();  	}  	*(char *)p = 0; -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size);  	/* verify PROT_READ with MAP_SHARED is allowed and a following  	 * mprotect(PROT_WRITE) allows writing */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ,  		 MAP_SHARED,  		 fd, @@ -302,20 +344,20 @@ static void mfd_assert_write(int fd)  		abort();  	} -	r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); +	r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE);  	if (r < 0) {  		printf("mprotect() failed: %m\n");  		abort();  	}  	*(char *)p = 0; -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size);  	/* verify PUNCH_HOLE works */  	r = fallocate(fd,  		      FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,  		      0, -		      MFD_DEF_SIZE); +		      mfd_def_size);  	if (r < 0) {  		printf("fallocate(PUNCH_HOLE) failed: %m\n");  		abort(); @@ -337,7 +379,7 @@ static void mfd_fail_write(int fd)  	/* verify PROT_READ | PROT_WRITE is not allowed */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ | PROT_WRITE,  		 MAP_SHARED,  		 fd, @@ -349,7 +391,7 @@ static void mfd_fail_write(int fd)  	/* verify PROT_WRITE is not allowed */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_WRITE,  		 MAP_SHARED,  		 fd, @@ -362,13 +404,13 @@ static void mfd_fail_write(int fd)  	/* Verify PROT_READ with MAP_SHARED with a following mprotect is not  	 * allowed. Note that for r/w the kernel already prevents the mmap. */  	p = mmap(NULL, -		 MFD_DEF_SIZE, +		 mfd_def_size,  		 PROT_READ,  		 MAP_SHARED,  		 fd,  		 0);  	if (p != MAP_FAILED) { -		r = mprotect(p, MFD_DEF_SIZE, PROT_READ | PROT_WRITE); +		r = mprotect(p, mfd_def_size, PROT_READ | PROT_WRITE);  		if (r >= 0) {  			printf("mmap()+mprotect() didn't fail as expected\n");  			abort(); @@ -379,7 +421,7 @@ static void mfd_fail_write(int fd)  	r = fallocate(fd,  		      FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,  		      0, -		      MFD_DEF_SIZE); +		      mfd_def_size);  	if (r >= 0) {  		printf("fallocate(PUNCH_HOLE) didn't fail as expected\n");  		abort(); @@ -390,13 +432,13 @@ static void mfd_assert_shrink(int fd)  {  	int r, fd2; -	r = ftruncate(fd, MFD_DEF_SIZE / 2); +	r = ftruncate(fd, mfd_def_size / 2);  	if (r < 0) {  		printf("ftruncate(SHRINK) failed: %m\n");  		abort();  	} -	mfd_assert_size(fd, MFD_DEF_SIZE / 2); +	mfd_assert_size(fd, mfd_def_size / 2);  	fd2 = mfd_assert_open(fd,  			      O_RDWR | O_CREAT | O_TRUNC, @@ -410,7 +452,7 @@ static void mfd_fail_shrink(int fd)  {  	int r; -	r = ftruncate(fd, MFD_DEF_SIZE / 2); +	r = ftruncate(fd, mfd_def_size / 2);  	if (r >= 0) {  		printf("ftruncate(SHRINK) didn't fail as expected\n");  		abort(); @@ -425,31 +467,31 @@ static void mfd_assert_grow(int fd)  {  	int r; -	r = ftruncate(fd, MFD_DEF_SIZE * 2); +	r = ftruncate(fd, mfd_def_size * 2);  	if (r < 0) {  		printf("ftruncate(GROW) failed: %m\n");  		abort();  	} -	mfd_assert_size(fd, MFD_DEF_SIZE * 2); +	mfd_assert_size(fd, mfd_def_size * 2);  	r = fallocate(fd,  		      0,  		      0, -		      MFD_DEF_SIZE * 4); +		      mfd_def_size * 4);  	if (r < 0) {  		printf("fallocate(ALLOC) failed: %m\n");  		abort();  	} -	mfd_assert_size(fd, MFD_DEF_SIZE * 4); +	mfd_assert_size(fd, mfd_def_size * 4);  }  static void mfd_fail_grow(int fd)  {  	int r; -	r = ftruncate(fd, MFD_DEF_SIZE * 2); +	r = ftruncate(fd, mfd_def_size * 2);  	if (r >= 0) {  		printf("ftruncate(GROW) didn't fail as expected\n");  		abort(); @@ -458,7 +500,7 @@ static void mfd_fail_grow(int fd)  	r = fallocate(fd,  		      0,  		      0, -		      MFD_DEF_SIZE * 4); +		      mfd_def_size * 4);  	if (r >= 0) {  		printf("fallocate(ALLOC) didn't fail as expected\n");  		abort(); @@ -467,25 +509,37 @@ static void mfd_fail_grow(int fd)  static void mfd_assert_grow_write(int fd)  { -	static char buf[MFD_DEF_SIZE * 8]; +	static char *buf;  	ssize_t l; -	l = pwrite(fd, buf, sizeof(buf), 0); -	if (l != sizeof(buf)) { +	buf = malloc(mfd_def_size * 8); +	if (!buf) { +		printf("malloc(%d) failed: %m\n", mfd_def_size * 8); +		abort(); +	} + +	l = pwrite(fd, buf, mfd_def_size * 8, 0); +	if (l != (mfd_def_size * 8)) {  		printf("pwrite() failed: %m\n");  		abort();  	} -	mfd_assert_size(fd, MFD_DEF_SIZE * 8); +	mfd_assert_size(fd, mfd_def_size * 8);  }  static void mfd_fail_grow_write(int fd)  { -	static char buf[MFD_DEF_SIZE * 8]; +	static char *buf;  	ssize_t l; -	l = pwrite(fd, buf, sizeof(buf), 0); -	if (l == sizeof(buf)) { +	buf = malloc(mfd_def_size * 8); +	if (!buf) { +		printf("malloc(%d) failed: %m\n", mfd_def_size * 8); +		abort(); +	} + +	l = pwrite(fd, buf, mfd_def_size * 8, 0); +	if (l == (mfd_def_size * 8)) {  		printf("pwrite() didn't fail as expected\n");  		abort();  	} @@ -543,6 +597,8 @@ static void test_create(void)  	char buf[2048];  	int fd; +	printf("%s CREATE\n", MEMFD_STR); +  	/* test NULL name */  	mfd_fail_new(NULL, 0); @@ -570,13 +626,18 @@ static void test_create(void)  	fd = mfd_assert_new("", 0, MFD_CLOEXEC);  	close(fd); -	/* verify MFD_ALLOW_SEALING is allowed */ -	fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING); -	close(fd); +	if (!hugetlbfs_test) { +		/* verify MFD_ALLOW_SEALING is allowed */ +		fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING); +		close(fd); -	/* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */ -	fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC); -	close(fd); +		/* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */ +		fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC); +		close(fd); +	} else { +		/* sealing is not supported on hugetlbfs */ +		mfd_fail_new("", MFD_ALLOW_SEALING); +	}  }  /* @@ -587,8 +648,14 @@ static void test_basic(void)  {  	int fd; +	/* hugetlbfs does not contain sealing support */ +	if (hugetlbfs_test) +		return; + +	printf("%s BASIC\n", MEMFD_STR); +  	fd = mfd_assert_new("kern_memfd_basic", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	/* add basic seals */ @@ -619,7 +686,7 @@ static void test_basic(void)  	/* verify sealing does not work without MFD_ALLOW_SEALING */  	fd = mfd_assert_new("kern_memfd_basic", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC);  	mfd_assert_has_seals(fd, F_SEAL_SEAL);  	mfd_fail_add_seals(fd, F_SEAL_SHRINK | @@ -630,6 +697,28 @@ static void test_basic(void)  }  /* + * hugetlbfs doesn't support seals or write, so just verify grow and shrink + * on a hugetlbfs file created via memfd_create. + */ +static void test_hugetlbfs_grow_shrink(void) +{ +	int fd; + +	printf("%s HUGETLBFS-GROW-SHRINK\n", MEMFD_STR); + +	fd = mfd_assert_new("kern_memfd_seal_write", +			    mfd_def_size, +			    MFD_CLOEXEC); + +	mfd_assert_read(fd); +	mfd_assert_write(fd); +	mfd_assert_shrink(fd); +	mfd_assert_grow(fd); + +	close(fd); +} + +/*   * Test SEAL_WRITE   * Test whether SEAL_WRITE actually prevents modifications.   */ @@ -637,8 +726,17 @@ static void test_seal_write(void)  {  	int fd; +	/* +	 * hugetlbfs does not contain sealing or write support.  Just test +	 * basic grow and shrink via test_hugetlbfs_grow_shrink. +	 */ +	if (hugetlbfs_test) +		return test_hugetlbfs_grow_shrink(); + +	printf("%s SEAL-WRITE\n", MEMFD_STR); +  	fd = mfd_assert_new("kern_memfd_seal_write", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0);  	mfd_assert_add_seals(fd, F_SEAL_WRITE); @@ -661,8 +759,14 @@ static void test_seal_shrink(void)  {  	int fd; +	/* hugetlbfs does not contain sealing support */ +	if (hugetlbfs_test) +		return; + +	printf("%s SEAL-SHRINK\n", MEMFD_STR); +  	fd = mfd_assert_new("kern_memfd_seal_shrink", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0);  	mfd_assert_add_seals(fd, F_SEAL_SHRINK); @@ -685,8 +789,14 @@ static void test_seal_grow(void)  {  	int fd; +	/* hugetlbfs does not contain sealing support */ +	if (hugetlbfs_test) +		return; + +	printf("%s SEAL-GROW\n", MEMFD_STR); +  	fd = mfd_assert_new("kern_memfd_seal_grow", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0);  	mfd_assert_add_seals(fd, F_SEAL_GROW); @@ -709,8 +819,14 @@ static void test_seal_resize(void)  {  	int fd; +	/* hugetlbfs does not contain sealing support */ +	if (hugetlbfs_test) +		return; + +	printf("%s SEAL-RESIZE\n", MEMFD_STR); +  	fd = mfd_assert_new("kern_memfd_seal_resize", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0);  	mfd_assert_add_seals(fd, F_SEAL_SHRINK | F_SEAL_GROW); @@ -726,15 +842,52 @@ static void test_seal_resize(void)  }  /* + * hugetlbfs does not support seals.  Basic test to dup the memfd created + * fd and perform some basic operations on it. + */ +static void hugetlbfs_dup(char *b_suffix) +{ +	int fd, fd2; + +	printf("%s HUGETLBFS-DUP %s\n", MEMFD_STR, b_suffix); + +	fd = mfd_assert_new("kern_memfd_share_dup", +			    mfd_def_size, +			    MFD_CLOEXEC); + +	fd2 = mfd_assert_dup(fd); + +	mfd_assert_read(fd); +	mfd_assert_write(fd); + +	mfd_assert_shrink(fd2); +	mfd_assert_grow(fd2); + +	close(fd2); +	close(fd); +} + +/*   * Test sharing via dup()   * Test that seals are shared between dupped FDs and they're all equal.   */ -static void test_share_dup(void) +static void test_share_dup(char *banner, char *b_suffix)  {  	int fd, fd2; +	/* +	 * hugetlbfs does not contain sealing support.  Perform some +	 * basic testing on dup'ed fd instead via hugetlbfs_dup. +	 */ +	if (hugetlbfs_test) { +		hugetlbfs_dup(b_suffix); +		return; +	} + +	printf("%s %s %s\n", MEMFD_STR, banner, b_suffix); +  	fd = mfd_assert_new("kern_memfd_share_dup", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0); @@ -768,13 +921,19 @@ static void test_share_dup(void)   * Test sealing with active mmap()s   * Modifying seals is only allowed if no other mmap() refs exist.   */ -static void test_share_mmap(void) +static void test_share_mmap(char *banner, char *b_suffix)  {  	int fd;  	void *p; +	/* hugetlbfs does not contain sealing support */ +	if (hugetlbfs_test) +		return; + +	printf("%s %s %s\n", MEMFD_STR,  banner, b_suffix); +  	fd = mfd_assert_new("kern_memfd_share_mmap", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0); @@ -784,14 +943,40 @@ static void test_share_mmap(void)  	mfd_assert_has_seals(fd, 0);  	mfd_assert_add_seals(fd, F_SEAL_SHRINK);  	mfd_assert_has_seals(fd, F_SEAL_SHRINK); -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size);  	/* readable ref allows sealing */  	p = mfd_assert_mmap_private(fd);  	mfd_assert_add_seals(fd, F_SEAL_WRITE);  	mfd_assert_has_seals(fd, F_SEAL_WRITE | F_SEAL_SHRINK); -	munmap(p, MFD_DEF_SIZE); +	munmap(p, mfd_def_size); + +	close(fd); +} + +/* + * Basic test to make sure we can open the hugetlbfs fd via /proc and + * perform some simple operations on it. + */ +static void hugetlbfs_proc_open(char *b_suffix) +{ +	int fd, fd2; + +	printf("%s HUGETLBFS-PROC-OPEN %s\n", MEMFD_STR, b_suffix); +	fd = mfd_assert_new("kern_memfd_share_open", +			    mfd_def_size, +			    MFD_CLOEXEC); + +	fd2 = mfd_assert_open(fd, O_RDWR, 0); + +	mfd_assert_read(fd); +	mfd_assert_write(fd); + +	mfd_assert_shrink(fd2); +	mfd_assert_grow(fd2); + +	close(fd2);  	close(fd);  } @@ -801,12 +986,23 @@ static void test_share_mmap(void)   * This is *not* like dup(), but like a real separate open(). Make sure the   * semantics are as expected and we correctly check for RDONLY / WRONLY / RDWR.   */ -static void test_share_open(void) +static void test_share_open(char *banner, char *b_suffix)  {  	int fd, fd2; +	/* +	 * hugetlbfs does not contain sealing support.  So test basic +	 * functionality of using /proc fd via hugetlbfs_proc_open +	 */ +	if (hugetlbfs_test) { +		hugetlbfs_proc_open(b_suffix); +		return; +	} + +	printf("%s %s %s\n", MEMFD_STR, banner, b_suffix); +  	fd = mfd_assert_new("kern_memfd_share_open", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0); @@ -841,13 +1037,19 @@ static void test_share_open(void)   * Test sharing via fork()   * Test whether seal-modifications work as expected with forked childs.   */ -static void test_share_fork(void) +static void test_share_fork(char *banner, char *b_suffix)  {  	int fd;  	pid_t pid; +	/* hugetlbfs does not contain sealing support */ +	if (hugetlbfs_test) +		return; + +	printf("%s %s %s\n", MEMFD_STR, banner, b_suffix); +  	fd = mfd_assert_new("kern_memfd_share_fork", -			    MFD_DEF_SIZE, +			    mfd_def_size,  			    MFD_CLOEXEC | MFD_ALLOW_SEALING);  	mfd_assert_has_seals(fd, 0); @@ -870,40 +1072,40 @@ int main(int argc, char **argv)  {  	pid_t pid; -	printf("memfd: CREATE\n"); +	if (argc == 2) { +		if (!strcmp(argv[1], "hugetlbfs")) { +			unsigned long hpage_size = default_huge_page_size(); + +			if (!hpage_size) { +				printf("Unable to determine huge page size\n"); +				abort(); +			} + +			hugetlbfs_test = 1; +			mfd_def_size = hpage_size * 2; +		} +	} +  	test_create(); -	printf("memfd: BASIC\n");  	test_basic(); -	printf("memfd: SEAL-WRITE\n");  	test_seal_write(); -	printf("memfd: SEAL-SHRINK\n");  	test_seal_shrink(); -	printf("memfd: SEAL-GROW\n");  	test_seal_grow(); -	printf("memfd: SEAL-RESIZE\n");  	test_seal_resize(); -	printf("memfd: SHARE-DUP\n"); -	test_share_dup(); -	printf("memfd: SHARE-MMAP\n"); -	test_share_mmap(); -	printf("memfd: SHARE-OPEN\n"); -	test_share_open(); -	printf("memfd: SHARE-FORK\n"); -	test_share_fork(); +	test_share_dup("SHARE-DUP", ""); +	test_share_mmap("SHARE-MMAP", ""); +	test_share_open("SHARE-OPEN", ""); +	test_share_fork("SHARE-FORK", "");  	/* Run test-suite in a multi-threaded environment with a shared  	 * file-table. */  	pid = spawn_idle_thread(CLONE_FILES | CLONE_FS | CLONE_VM); -	printf("memfd: SHARE-DUP (shared file-table)\n"); -	test_share_dup(); -	printf("memfd: SHARE-MMAP (shared file-table)\n"); -	test_share_mmap(); -	printf("memfd: SHARE-OPEN (shared file-table)\n"); -	test_share_open(); -	printf("memfd: SHARE-FORK (shared file-table)\n"); -	test_share_fork(); +	test_share_dup("SHARE-DUP", SHARED_FT_STR); +	test_share_mmap("SHARE-MMAP", SHARED_FT_STR); +	test_share_open("SHARE-OPEN", SHARED_FT_STR); +	test_share_fork("SHARE-FORK", SHARED_FT_STR);  	join_idle_thread(pid);  	printf("memfd: DONE\n"); diff --git a/tools/testing/selftests/memfd/run_tests.sh b/tools/testing/selftests/memfd/run_tests.sh new file mode 100644 index 000000000000..daabb350697c --- /dev/null +++ b/tools/testing/selftests/memfd/run_tests.sh @@ -0,0 +1,69 @@ +#!/bin/bash +# please run as root + +# +# Normal tests requiring no special resources +# +./run_fuse_test.sh +./memfd_test + +# +# To test memfd_create with hugetlbfs, there needs to be hpages_test +# huge pages free.  Attempt to allocate enough pages to test. +# +hpages_test=8 + +# +# Get count of free huge pages from /proc/meminfo +# +while read name size unit; do +        if [ "$name" = "HugePages_Free:" ]; then +                freepgs=$size +        fi +done < /proc/meminfo + +# +# If not enough free huge pages for test, attempt to increase +# +if [ -n "$freepgs" ] && [ $freepgs -lt $hpages_test ]; then +	nr_hugepgs=`cat /proc/sys/vm/nr_hugepages` +	hpages_needed=`expr $hpages_test - $freepgs` + +	echo 3 > /proc/sys/vm/drop_caches +	echo $(( $hpages_needed + $nr_hugepgs )) > /proc/sys/vm/nr_hugepages +	if [ $? -ne 0 ]; then +		echo "Please run this test as root" +		exit 1 +	fi +	while read name size unit; do +		if [ "$name" = "HugePages_Free:" ]; then +			freepgs=$size +		fi +	done < /proc/meminfo +fi + +# +# If still not enough huge pages available, exit.  But, give back any huge +# pages potentially allocated above. +# +if [ $freepgs -lt $hpages_test ]; then +	# nr_hugepgs non-zero only if we attempted to increase +	if [ -n "$nr_hugepgs" ]; then +		echo $nr_hugepgs > /proc/sys/vm/nr_hugepages +	fi +	printf "Not enough huge pages available (%d < %d)\n" \ +		$freepgs $needpgs +	exit 1 +fi + +# +# Run the hugetlbfs test +# +./memfd_test hugetlbfs + +# +# Give back any huge pages allocated for the test +# +if [ -n "$nr_hugepgs" ]; then +	echo $nr_hugepgs > /proc/sys/vm/nr_hugepages +fi diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index afe109e5508a..9801253e4802 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -1,3 +1,4 @@ +msg_zerocopy  socket  psock_fanout  psock_tpacket diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index f6c9dbf478f8..de1f5772b878 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -3,11 +3,11 @@  CFLAGS =  -Wall -Wl,--no-as-needed -O2 -g  CFLAGS += -I../../../../usr/include/ -TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh +TEST_PROGS := run_netsocktests run_afpackettests test_bpf.sh netdevice.sh rtnetlink.sh  TEST_GEN_FILES =  socket  TEST_GEN_FILES += psock_fanout psock_tpacket  TEST_GEN_FILES += reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa -TEST_GEN_FILES += reuseport_dualstack +TEST_GEN_FILES += reuseport_dualstack msg_zerocopy  include ../lib.mk diff --git a/tools/testing/selftests/net/msg_zerocopy.c b/tools/testing/selftests/net/msg_zerocopy.c new file mode 100644 index 000000000000..40232af5b023 --- /dev/null +++ b/tools/testing/selftests/net/msg_zerocopy.c @@ -0,0 +1,697 @@ +/* Evaluate MSG_ZEROCOPY + * + * Send traffic between two processes over one of the supported + * protocols and modes: + * + * PF_INET/PF_INET6 + * - SOCK_STREAM + * - SOCK_DGRAM + * - SOCK_DGRAM with UDP_CORK + * - SOCK_RAW + * - SOCK_RAW with IP_HDRINCL + * + * PF_PACKET + * - SOCK_DGRAM + * - SOCK_RAW + * + * Start this program on two connected hosts, one in send mode and + * the other with option '-r' to put it in receiver mode. + * + * If zerocopy mode ('-z') is enabled, the sender will verify that + * the kernel queues completions on the error queue for all zerocopy + * transfers. + */ + +#define _GNU_SOURCE + +#include <arpa/inet.h> +#include <error.h> +#include <errno.h> +#include <limits.h> +#include <linux/errqueue.h> +#include <linux/if_packet.h> +#include <linux/ipv6.h> +#include <linux/socket.h> +#include <linux/sockios.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <netinet/ip.h> +#include <netinet/ip6.h> +#include <netinet/tcp.h> +#include <netinet/udp.h> +#include <poll.h> +#include <sched.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + +#ifndef SO_EE_ORIGIN_ZEROCOPY +#define SO_EE_ORIGIN_ZEROCOPY		SO_EE_ORIGIN_UPAGE +#endif + +#ifndef SO_ZEROCOPY +#define SO_ZEROCOPY	60 +#endif + +#ifndef SO_EE_CODE_ZEROCOPY_COPIED +#define SO_EE_CODE_ZEROCOPY_COPIED	1 +#endif + +#ifndef MSG_ZEROCOPY +#define MSG_ZEROCOPY	0x4000000 +#endif + +static int  cfg_cork; +static bool cfg_cork_mixed; +static int  cfg_cpu		= -1;		/* default: pin to last cpu */ +static int  cfg_family		= PF_UNSPEC; +static int  cfg_ifindex		= 1; +static int  cfg_payload_len; +static int  cfg_port		= 8000; +static bool cfg_rx; +static int  cfg_runtime_ms	= 4200; +static int  cfg_verbose; +static int  cfg_waittime_ms	= 500; +static bool cfg_zerocopy; + +static socklen_t cfg_alen; +static struct sockaddr_storage cfg_dst_addr; +static struct sockaddr_storage cfg_src_addr; + +static char payload[IP_MAXPACKET]; +static long packets, bytes, completions, expected_completions; +static int  zerocopied = -1; +static uint32_t next_completion; + +static unsigned long gettimeofday_ms(void) +{ +	struct timeval tv; + +	gettimeofday(&tv, NULL); +	return (tv.tv_sec * 1000) + (tv.tv_usec / 1000); +} + +static uint16_t get_ip_csum(const uint16_t *start, int num_words) +{ +	unsigned long sum = 0; +	int i; + +	for (i = 0; i < num_words; i++) +		sum += start[i]; + +	while (sum >> 16) +		sum = (sum & 0xFFFF) + (sum >> 16); + +	return ~sum; +} + +static int do_setcpu(int cpu) +{ +	cpu_set_t mask; + +	CPU_ZERO(&mask); +	CPU_SET(cpu, &mask); +	if (sched_setaffinity(0, sizeof(mask), &mask)) +		error(1, 0, "setaffinity %d", cpu); + +	if (cfg_verbose) +		fprintf(stderr, "cpu: %u\n", cpu); + +	return 0; +} + +static void do_setsockopt(int fd, int level, int optname, int val) +{ +	if (setsockopt(fd, level, optname, &val, sizeof(val))) +		error(1, errno, "setsockopt %d.%d: %d", level, optname, val); +} + +static int do_poll(int fd, int events) +{ +	struct pollfd pfd; +	int ret; + +	pfd.events = events; +	pfd.revents = 0; +	pfd.fd = fd; + +	ret = poll(&pfd, 1, cfg_waittime_ms); +	if (ret == -1) +		error(1, errno, "poll"); + +	return ret && (pfd.revents & events); +} + +static int do_accept(int fd) +{ +	int fda = fd; + +	fd = accept(fda, NULL, NULL); +	if (fd == -1) +		error(1, errno, "accept"); +	if (close(fda)) +		error(1, errno, "close listen sock"); + +	return fd; +} + +static bool do_sendmsg(int fd, struct msghdr *msg, bool do_zerocopy) +{ +	int ret, len, i, flags; + +	len = 0; +	for (i = 0; i < msg->msg_iovlen; i++) +		len += msg->msg_iov[i].iov_len; + +	flags = MSG_DONTWAIT; +	if (do_zerocopy) +		flags |= MSG_ZEROCOPY; + +	ret = sendmsg(fd, msg, flags); +	if (ret == -1 && errno == EAGAIN) +		return false; +	if (ret == -1) +		error(1, errno, "send"); +	if (cfg_verbose && ret != len) +		fprintf(stderr, "send: ret=%u != %u\n", ret, len); + +	if (len) { +		packets++; +		bytes += ret; +		if (do_zerocopy && ret) +			expected_completions++; +	} + +	return true; +} + +static void do_sendmsg_corked(int fd, struct msghdr *msg) +{ +	bool do_zerocopy = cfg_zerocopy; +	int i, payload_len, extra_len; + +	/* split up the packet. for non-multiple, make first buffer longer */ +	payload_len = cfg_payload_len / cfg_cork; +	extra_len = cfg_payload_len - (cfg_cork * payload_len); + +	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 1); + +	for (i = 0; i < cfg_cork; i++) { + +		/* in mixed-frags mode, alternate zerocopy and copy frags +		 * start with non-zerocopy, to ensure attach later works +		 */ +		if (cfg_cork_mixed) +			do_zerocopy = (i & 1); + +		msg->msg_iov[0].iov_len = payload_len + extra_len; +		extra_len = 0; + +		do_sendmsg(fd, msg, do_zerocopy); +	} + +	do_setsockopt(fd, IPPROTO_UDP, UDP_CORK, 0); +} + +static int setup_iph(struct iphdr *iph, uint16_t payload_len) +{ +	struct sockaddr_in *daddr = (void *) &cfg_dst_addr; +	struct sockaddr_in *saddr = (void *) &cfg_src_addr; + +	memset(iph, 0, sizeof(*iph)); + +	iph->version	= 4; +	iph->tos	= 0; +	iph->ihl	= 5; +	iph->ttl	= 2; +	iph->saddr	= saddr->sin_addr.s_addr; +	iph->daddr	= daddr->sin_addr.s_addr; +	iph->protocol	= IPPROTO_EGP; +	iph->tot_len	= htons(sizeof(*iph) + payload_len); +	iph->check	= get_ip_csum((void *) iph, iph->ihl << 1); + +	return sizeof(*iph); +} + +static int setup_ip6h(struct ipv6hdr *ip6h, uint16_t payload_len) +{ +	struct sockaddr_in6 *daddr = (void *) &cfg_dst_addr; +	struct sockaddr_in6 *saddr = (void *) &cfg_src_addr; + +	memset(ip6h, 0, sizeof(*ip6h)); + +	ip6h->version		= 6; +	ip6h->payload_len	= htons(payload_len); +	ip6h->nexthdr		= IPPROTO_EGP; +	ip6h->hop_limit		= 2; +	ip6h->saddr		= saddr->sin6_addr; +	ip6h->daddr		= daddr->sin6_addr; + +	return sizeof(*ip6h); +} + +static void setup_sockaddr(int domain, const char *str_addr, void *sockaddr) +{ +	struct sockaddr_in6 *addr6 = (void *) sockaddr; +	struct sockaddr_in *addr4 = (void *) sockaddr; + +	switch (domain) { +	case PF_INET: +		addr4->sin_family = AF_INET; +		addr4->sin_port = htons(cfg_port); +		if (inet_pton(AF_INET, str_addr, &(addr4->sin_addr)) != 1) +			error(1, 0, "ipv4 parse error: %s", str_addr); +		break; +	case PF_INET6: +		addr6->sin6_family = AF_INET6; +		addr6->sin6_port = htons(cfg_port); +		if (inet_pton(AF_INET6, str_addr, &(addr6->sin6_addr)) != 1) +			error(1, 0, "ipv6 parse error: %s", str_addr); +		break; +	default: +		error(1, 0, "illegal domain"); +	} +} + +static int do_setup_tx(int domain, int type, int protocol) +{ +	int fd; + +	fd = socket(domain, type, protocol); +	if (fd == -1) +		error(1, errno, "socket t"); + +	do_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, 1 << 21); +	if (cfg_zerocopy) +		do_setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, 1); + +	if (domain != PF_PACKET) +		if (connect(fd, (void *) &cfg_dst_addr, cfg_alen)) +			error(1, errno, "connect"); + +	return fd; +} + +static bool do_recv_completion(int fd) +{ +	struct sock_extended_err *serr; +	struct msghdr msg = {}; +	struct cmsghdr *cm; +	uint32_t hi, lo, range; +	int ret, zerocopy; +	char control[100]; + +	msg.msg_control = control; +	msg.msg_controllen = sizeof(control); + +	ret = recvmsg(fd, &msg, MSG_ERRQUEUE); +	if (ret == -1 && errno == EAGAIN) +		return false; +	if (ret == -1) +		error(1, errno, "recvmsg notification"); +	if (msg.msg_flags & MSG_CTRUNC) +		error(1, errno, "recvmsg notification: truncated"); + +	cm = CMSG_FIRSTHDR(&msg); +	if (!cm) +		error(1, 0, "cmsg: no cmsg"); +	if (!((cm->cmsg_level == SOL_IP && cm->cmsg_type == IP_RECVERR) || +	      (cm->cmsg_level == SOL_IPV6 && cm->cmsg_type == IPV6_RECVERR) || +	      (cm->cmsg_level == SOL_PACKET && cm->cmsg_type == PACKET_TX_TIMESTAMP))) +		error(1, 0, "serr: wrong type: %d.%d", +		      cm->cmsg_level, cm->cmsg_type); + +	serr = (void *) CMSG_DATA(cm); +	if (serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) +		error(1, 0, "serr: wrong origin: %u", serr->ee_origin); +	if (serr->ee_errno != 0) +		error(1, 0, "serr: wrong error code: %u", serr->ee_errno); + +	hi = serr->ee_data; +	lo = serr->ee_info; +	range = hi - lo + 1; + +	/* Detect notification gaps. These should not happen often, if at all. +	 * Gaps can occur due to drops, reordering and retransmissions. +	 */ +	if (lo != next_completion) +		fprintf(stderr, "gap: %u..%u does not append to %u\n", +			lo, hi, next_completion); +	next_completion = hi + 1; + +	zerocopy = !(serr->ee_code & SO_EE_CODE_ZEROCOPY_COPIED); +	if (zerocopied == -1) +		zerocopied = zerocopy; +	else if (zerocopied != zerocopy) { +		fprintf(stderr, "serr: inconsistent\n"); +		zerocopied = zerocopy; +	} + +	if (cfg_verbose >= 2) +		fprintf(stderr, "completed: %u (h=%u l=%u)\n", +			range, hi, lo); + +	completions += range; +	return true; +} + +/* Read all outstanding messages on the errqueue */ +static void do_recv_completions(int fd) +{ +	while (do_recv_completion(fd)) {} +} + +/* Wait for all remaining completions on the errqueue */ +static void do_recv_remaining_completions(int fd) +{ +	int64_t tstop = gettimeofday_ms() + cfg_waittime_ms; + +	while (completions < expected_completions && +	       gettimeofday_ms() < tstop) { +		if (do_poll(fd, POLLERR)) +			do_recv_completions(fd); +	} + +	if (completions < expected_completions) +		fprintf(stderr, "missing notifications: %lu < %lu\n", +			completions, expected_completions); +} + +static void do_tx(int domain, int type, int protocol) +{ +	struct iovec iov[3] = { {0} }; +	struct sockaddr_ll laddr; +	struct msghdr msg = {0}; +	struct ethhdr eth; +	union { +		struct ipv6hdr ip6h; +		struct iphdr iph; +	} nh; +	uint64_t tstop; +	int fd; + +	fd = do_setup_tx(domain, type, protocol); + +	if (domain == PF_PACKET) { +		uint16_t proto = cfg_family == PF_INET ? ETH_P_IP : ETH_P_IPV6; + +		/* sock_raw passes ll header as data */ +		if (type == SOCK_RAW) { +			memset(eth.h_dest, 0x06, ETH_ALEN); +			memset(eth.h_source, 0x02, ETH_ALEN); +			eth.h_proto = htons(proto); +			iov[0].iov_base = ð +			iov[0].iov_len = sizeof(eth); +			msg.msg_iovlen++; +		} + +		/* both sock_raw and sock_dgram expect name */ +		memset(&laddr, 0, sizeof(laddr)); +		laddr.sll_family	= AF_PACKET; +		laddr.sll_ifindex	= cfg_ifindex; +		laddr.sll_protocol	= htons(proto); +		laddr.sll_halen		= ETH_ALEN; + +		memset(laddr.sll_addr, 0x06, ETH_ALEN); + +		msg.msg_name		= &laddr; +		msg.msg_namelen		= sizeof(laddr); +	} + +	/* packet and raw sockets with hdrincl must pass network header */ +	if (domain == PF_PACKET || protocol == IPPROTO_RAW) { +		if (cfg_family == PF_INET) +			iov[1].iov_len = setup_iph(&nh.iph, cfg_payload_len); +		else +			iov[1].iov_len = setup_ip6h(&nh.ip6h, cfg_payload_len); + +		iov[1].iov_base = (void *) &nh; +		msg.msg_iovlen++; +	} + +	iov[2].iov_base = payload; +	iov[2].iov_len = cfg_payload_len; +	msg.msg_iovlen++; +	msg.msg_iov = &iov[3 - msg.msg_iovlen]; + +	tstop = gettimeofday_ms() + cfg_runtime_ms; +	do { +		if (cfg_cork) +			do_sendmsg_corked(fd, &msg); +		else +			do_sendmsg(fd, &msg, cfg_zerocopy); + +		while (!do_poll(fd, POLLOUT)) { +			if (cfg_zerocopy) +				do_recv_completions(fd); +		} + +	} while (gettimeofday_ms() < tstop); + +	if (cfg_zerocopy) +		do_recv_remaining_completions(fd); + +	if (close(fd)) +		error(1, errno, "close"); + +	fprintf(stderr, "tx=%lu (%lu MB) txc=%lu zc=%c\n", +		packets, bytes >> 20, completions, +		zerocopied == 1 ? 'y' : 'n'); +} + +static int do_setup_rx(int domain, int type, int protocol) +{ +	int fd; + +	/* If tx over PF_PACKET, rx over PF_INET(6)/SOCK_RAW, +	 * to recv the only copy of the packet, not a clone +	 */ +	if (domain == PF_PACKET) +		error(1, 0, "Use PF_INET/SOCK_RAW to read"); + +	if (type == SOCK_RAW && protocol == IPPROTO_RAW) +		error(1, 0, "IPPROTO_RAW: not supported on Rx"); + +	fd = socket(domain, type, protocol); +	if (fd == -1) +		error(1, errno, "socket r"); + +	do_setsockopt(fd, SOL_SOCKET, SO_RCVBUF, 1 << 21); +	do_setsockopt(fd, SOL_SOCKET, SO_RCVLOWAT, 1 << 16); +	do_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, 1); + +	if (bind(fd, (void *) &cfg_dst_addr, cfg_alen)) +		error(1, errno, "bind"); + +	if (type == SOCK_STREAM) { +		if (listen(fd, 1)) +			error(1, errno, "listen"); +		fd = do_accept(fd); +	} + +	return fd; +} + +/* Flush all outstanding bytes for the tcp receive queue */ +static void do_flush_tcp(int fd) +{ +	int ret; + +	/* MSG_TRUNC flushes up to len bytes */ +	ret = recv(fd, NULL, 1 << 21, MSG_TRUNC | MSG_DONTWAIT); +	if (ret == -1 && errno == EAGAIN) +		return; +	if (ret == -1) +		error(1, errno, "flush"); +	if (!ret) +		return; + +	packets++; +	bytes += ret; +} + +/* Flush all outstanding datagrams. Verify first few bytes of each. */ +static void do_flush_datagram(int fd, int type) +{ +	int ret, off = 0; +	char buf[64]; + +	/* MSG_TRUNC will return full datagram length */ +	ret = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_TRUNC); +	if (ret == -1 && errno == EAGAIN) +		return; + +	/* raw ipv4 return with header, raw ipv6 without */ +	if (cfg_family == PF_INET && type == SOCK_RAW) { +		off += sizeof(struct iphdr); +		ret -= sizeof(struct iphdr); +	} + +	if (ret == -1) +		error(1, errno, "recv"); +	if (ret != cfg_payload_len) +		error(1, 0, "recv: ret=%u != %u", ret, cfg_payload_len); +	if (ret > sizeof(buf) - off) +		ret = sizeof(buf) - off; +	if (memcmp(buf + off, payload, ret)) +		error(1, 0, "recv: data mismatch"); + +	packets++; +	bytes += cfg_payload_len; +} + +static void do_rx(int domain, int type, int protocol) +{ +	uint64_t tstop; +	int fd; + +	fd = do_setup_rx(domain, type, protocol); + +	tstop = gettimeofday_ms() + cfg_runtime_ms; +	do { +		if (type == SOCK_STREAM) +			do_flush_tcp(fd); +		else +			do_flush_datagram(fd, type); + +		do_poll(fd, POLLIN); + +	} while (gettimeofday_ms() < tstop); + +	if (close(fd)) +		error(1, errno, "close"); + +	fprintf(stderr, "rx=%lu (%lu MB)\n", packets, bytes >> 20); +} + +static void do_test(int domain, int type, int protocol) +{ +	int i; + +	if (cfg_cork && (domain == PF_PACKET || type != SOCK_DGRAM)) +		error(1, 0, "can only cork udp sockets"); + +	do_setcpu(cfg_cpu); + +	for (i = 0; i < IP_MAXPACKET; i++) +		payload[i] = 'a' + (i % 26); + +	if (cfg_rx) +		do_rx(domain, type, protocol); +	else +		do_tx(domain, type, protocol); +} + +static void usage(const char *filepath) +{ +	error(1, 0, "Usage: %s [options] <test>", filepath); +} + +static void parse_opts(int argc, char **argv) +{ +	const int max_payload_len = sizeof(payload) - +				    sizeof(struct ipv6hdr) - +				    sizeof(struct tcphdr) - +				    40 /* max tcp options */; +	int c; + +	cfg_payload_len = max_payload_len; + +	while ((c = getopt(argc, argv, "46c:C:D:i:mp:rs:S:t:vz")) != -1) { +		switch (c) { +		case '4': +			if (cfg_family != PF_UNSPEC) +				error(1, 0, "Pass one of -4 or -6"); +			cfg_family = PF_INET; +			cfg_alen = sizeof(struct sockaddr_in); +			break; +		case '6': +			if (cfg_family != PF_UNSPEC) +				error(1, 0, "Pass one of -4 or -6"); +			cfg_family = PF_INET6; +			cfg_alen = sizeof(struct sockaddr_in6); +			break; +		case 'c': +			cfg_cork = strtol(optarg, NULL, 0); +			break; +		case 'C': +			cfg_cpu = strtol(optarg, NULL, 0); +			break; +		case 'D': +			setup_sockaddr(cfg_family, optarg, &cfg_dst_addr); +			break; +		case 'i': +			cfg_ifindex = if_nametoindex(optarg); +			if (cfg_ifindex == 0) +				error(1, errno, "invalid iface: %s", optarg); +			break; +		case 'm': +			cfg_cork_mixed = true; +			break; +		case 'p': +			cfg_port = htons(strtoul(optarg, NULL, 0)); +			break; +		case 'r': +			cfg_rx = true; +			break; +		case 's': +			cfg_payload_len = strtoul(optarg, NULL, 0); +			break; +		case 'S': +			setup_sockaddr(cfg_family, optarg, &cfg_src_addr); +			break; +		case 't': +			cfg_runtime_ms = 200 + strtoul(optarg, NULL, 10) * 1000; +			break; +		case 'v': +			cfg_verbose++; +			break; +		case 'z': +			cfg_zerocopy = true; +			break; +		} +	} + +	if (cfg_payload_len > max_payload_len) +		error(1, 0, "-s: payload exceeds max (%d)", max_payload_len); +	if (cfg_cork_mixed && (!cfg_zerocopy || !cfg_cork)) +		error(1, 0, "-m: cork_mixed requires corking and zerocopy"); + +	if (optind != argc - 1) +		usage(argv[0]); +} + +int main(int argc, char **argv) +{ +	const char *cfg_test; + +	parse_opts(argc, argv); + +	cfg_test = argv[argc - 1]; + +	if (!strcmp(cfg_test, "packet")) +		do_test(PF_PACKET, SOCK_RAW, 0); +	else if (!strcmp(cfg_test, "packet_dgram")) +		do_test(PF_PACKET, SOCK_DGRAM, 0); +	else if (!strcmp(cfg_test, "raw")) +		do_test(cfg_family, SOCK_RAW, IPPROTO_EGP); +	else if (!strcmp(cfg_test, "raw_hdrincl")) +		do_test(cfg_family, SOCK_RAW, IPPROTO_RAW); +	else if (!strcmp(cfg_test, "tcp")) +		do_test(cfg_family, SOCK_STREAM, 0); +	else if (!strcmp(cfg_test, "udp")) +		do_test(cfg_family, SOCK_DGRAM, 0); +	else +		error(1, 0, "unknown cfg_test %s", cfg_test); + +	return 0; +} diff --git a/tools/testing/selftests/net/msg_zerocopy.sh b/tools/testing/selftests/net/msg_zerocopy.sh new file mode 100755 index 000000000000..d571d213418d --- /dev/null +++ b/tools/testing/selftests/net/msg_zerocopy.sh @@ -0,0 +1,112 @@ +#!/bin/bash +# +# Send data between two processes across namespaces +# Run twice: once without and once with zerocopy + +set -e + +readonly DEV="veth0" +readonly DEV_MTU=65535 +readonly BIN="./msg_zerocopy" + +readonly RAND="$(mktemp -u XXXXXX)" +readonly NSPREFIX="ns-${RAND}" +readonly NS1="${NSPREFIX}1" +readonly NS2="${NSPREFIX}2" + +readonly SADDR4='192.168.1.1' +readonly DADDR4='192.168.1.2' +readonly SADDR6='fd::1' +readonly DADDR6='fd::2' + +readonly path_sysctl_mem="net.core.optmem_max" + +# Argument parsing +if [[ "$#" -lt "2" ]]; then +	echo "Usage: $0 [4|6] [tcp|udp|raw|raw_hdrincl|packet|packet_dgram] <args>" +	exit 1 +fi + +readonly IP="$1" +shift +readonly TXMODE="$1" +shift +readonly EXTRA_ARGS="$@" + +# Argument parsing: configure addresses +if [[ "${IP}" == "4" ]]; then +	readonly SADDR="${SADDR4}" +	readonly DADDR="${DADDR4}" +elif [[ "${IP}" == "6" ]]; then +	readonly SADDR="${SADDR6}" +	readonly DADDR="${DADDR6}" +else +	echo "Invalid IP version ${IP}" +	exit 1 +fi + +# Argument parsing: select receive mode +# +# This differs from send mode for +# - packet:	use raw recv, because packet receives skb clones +# - raw_hdrinc: use raw recv, because hdrincl is a tx-only option +case "${TXMODE}" in +'packet' | 'packet_dgram' | 'raw_hdrincl') +	RXMODE='raw' +	;; +*) +	RXMODE="${TXMODE}" +	;; +esac + +# Start of state changes: install cleanup handler +save_sysctl_mem="$(sysctl -n ${path_sysctl_mem})" + +cleanup() { +	ip netns del "${NS2}" +	ip netns del "${NS1}" +	sysctl -w -q "${path_sysctl_mem}=${save_sysctl_mem}" +} + +trap cleanup EXIT + +# Configure system settings +sysctl -w -q "${path_sysctl_mem}=1000000" + +# Create virtual ethernet pair between network namespaces +ip netns add "${NS1}" +ip netns add "${NS2}" + +ip link add "${DEV}" mtu "${DEV_MTU}" netns "${NS1}" type veth \ +  peer name "${DEV}" mtu "${DEV_MTU}" netns "${NS2}" + +# Bring the devices up +ip -netns "${NS1}" link set "${DEV}" up +ip -netns "${NS2}" link set "${DEV}" up + +# Set fixed MAC addresses on the devices +ip -netns "${NS1}" link set dev "${DEV}" address 02:02:02:02:02:02 +ip -netns "${NS2}" link set dev "${DEV}" address 06:06:06:06:06:06 + +# Add fixed IP addresses to the devices +ip -netns "${NS1}" addr add 192.168.1.1/24 dev "${DEV}" +ip -netns "${NS2}" addr add 192.168.1.2/24 dev "${DEV}" +ip -netns "${NS1}" addr add       fd::1/64 dev "${DEV}" nodad +ip -netns "${NS2}" addr add       fd::2/64 dev "${DEV}" nodad + +# Optionally disable sg or csum offload to test edge cases +# ip netns exec "${NS1}" ethtool -K "${DEV}" sg off + +do_test() { +	local readonly ARGS="$1" + +	echo "ipv${IP} ${TXMODE} ${ARGS}" +	ip netns exec "${NS2}" "${BIN}" "-${IP}" -i "${DEV}" -t 2 -C 2 -S "${SADDR}" -D "${DADDR}" ${ARGS} -r "${RXMODE}" & +	sleep 0.2 +	ip netns exec "${NS1}" "${BIN}" "-${IP}" -i "${DEV}" -t 1 -C 3 -S "${SADDR}" -D "${DADDR}" ${ARGS} "${TXMODE}" +	wait +} + +do_test "${EXTRA_ARGS}" +do_test "-z ${EXTRA_ARGS}" +echo ok diff --git a/tools/testing/selftests/net/rtnetlink.sh b/tools/testing/selftests/net/rtnetlink.sh new file mode 100755 index 000000000000..57b5ff576240 --- /dev/null +++ b/tools/testing/selftests/net/rtnetlink.sh @@ -0,0 +1,272 @@ +#!/bin/sh +# +# This test is for checking rtnetlink callpaths, and get as much coverage as possible. +# +# set -e + +devdummy="test-dummy0" +ret=0 + +# set global exit status, but never reset nonzero one. +check_err() +{ +	if [ $ret -eq 0 ]; then +		ret=$1 +	fi +} + +kci_add_dummy() +{ +	ip link add name "$devdummy" type dummy +	check_err $? +	ip link set "$devdummy" up +	check_err $? +} + +kci_del_dummy() +{ +	ip link del dev "$devdummy" +	check_err $? +} + +# add a bridge with vlans on top +kci_test_bridge() +{ +	devbr="test-br0" +	vlandev="testbr-vlan1" + +	ret=0 +	ip link add name "$devbr" type bridge +	check_err $? + +	ip link set dev "$devdummy" master "$devbr" +	check_err $? + +	ip link set "$devbr" up +	check_err $? + +	ip link add link "$devbr" name "$vlandev" type vlan id 1 +	check_err $? +	ip addr add dev "$vlandev" 10.200.7.23/30 +	check_err $? +	ip -6 addr add dev "$vlandev" dead:42::1234/64 +	check_err $? +	ip -d link > /dev/null +	check_err $? +	ip r s t all > /dev/null +	check_err $? +	ip -6 addr del dev "$vlandev" dead:42::1234/64 +	check_err $? + +	ip link del dev "$vlandev" +	check_err $? +	ip link del dev "$devbr" +	check_err $? + +	if [ $ret -ne 0 ];then +		echo "FAIL: bridge setup" +		return 1 +	fi +	echo "PASS: bridge setup" + +} + +kci_test_gre() +{ +	gredev=neta +	rem=10.42.42.1 +	loc=10.0.0.1 + +	ret=0 +	ip tunnel add $gredev mode gre remote $rem local $loc ttl 1 +	check_err $? +	ip link set $gredev up +	check_err $? +	ip addr add 10.23.7.10 dev $gredev +	check_err $? +	ip route add 10.23.8.0/30 dev $gredev +	check_err $? +	ip addr add dev "$devdummy" 10.23.7.11/24 +	check_err $? +	ip link > /dev/null +	check_err $? +	ip addr > /dev/null +	check_err $? +	ip addr del dev "$devdummy" 10.23.7.11/24 +	check_err $? + +	ip link del $gredev +	check_err $? + +	if [ $ret -ne 0 ];then +		echo "FAIL: gre tunnel endpoint" +		return 1 +	fi +	echo "PASS: gre tunnel endpoint" +} + +# tc uses rtnetlink too, for full tc testing +# please see tools/testing/selftests/tc-testing. +kci_test_tc() +{ +	dev=lo +	ret=0 + +	tc qdisc add dev "$dev" root handle 1: htb +	check_err $? +	tc class add dev "$dev" parent 1: classid 1:10 htb rate 1mbit +	check_err $? +	tc filter add dev "$dev" parent 1:0 prio 5 handle ffe: protocol ip u32 divisor 256 +	check_err $? +	tc filter add dev "$dev" parent 1:0 prio 5 handle ffd: protocol ip u32 divisor 256 +	check_err $? +	tc filter add dev "$dev" parent 1:0 prio 5 handle ffc: protocol ip u32 divisor 256 +	check_err $? +	tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 ht ffe:2: match ip src 10.0.0.3 flowid 1:10 +	check_err $? +	tc filter add dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:2 u32 ht ffe:2: match ip src 10.0.0.2 flowid 1:10 +	check_err $? +	tc filter show dev "$dev" parent  1:0 > /dev/null +	check_err $? +	tc filter del dev "$dev" protocol ip parent 1: prio 5 handle ffe:2:3 u32 +	check_err $? +	tc filter show dev "$dev" parent  1:0 > /dev/null +	check_err $? +	tc qdisc del dev "$dev" root handle 1: htb +	check_err $? + +	if [ $ret -ne 0 ];then +		echo "FAIL: tc htb hierarchy" +		return 1 +	fi +	echo "PASS: tc htb hierarchy" + +} + +kci_test_polrouting() +{ +	ret=0 +	ip rule add fwmark 1 lookup 100 +	check_err $? +	ip route add local 0.0.0.0/0 dev lo table 100 +	check_err $? +	ip r s t all > /dev/null +	check_err $? +	ip rule del fwmark 1 lookup 100 +	check_err $? +	ip route del local 0.0.0.0/0 dev lo table 100 +	check_err $? + +	if [ $ret -ne 0 ];then +		echo "FAIL: policy route test" +		return 1 +	fi +	echo "PASS: policy routing" +} + +kci_test_route_get() +{ +	ret=0 + +	ip route get 127.0.0.1 > /dev/null +	check_err $? +	ip route get 127.0.0.1 dev "$devdummy" > /dev/null +	check_err $? +	ip route get ::1 > /dev/null +	check_err $? +	ip route get fe80::1 dev "$devdummy" > /dev/null +	check_err $? +	ip route get 127.0.0.1 from 127.0.0.1 oif lo tos 0x1 mark 0x1 > /dev/null +	check_err $? +	ip route get ::1 from ::1 iif lo oif lo tos 0x1 mark 0x1 > /dev/null +	check_err $? +	ip addr add dev "$devdummy" 10.23.7.11/24 +	check_err $? +	ip route get 10.23.7.11 from 10.23.7.12 iif "$devdummy" > /dev/null +	check_err $? +	ip addr del dev "$devdummy" 10.23.7.11/24 +	check_err $? + +	if [ $ret -ne 0 ];then +		echo "FAIL: route get" +		return 1 +	fi + +	echo "PASS: route get" +} + +kci_test_addrlabel() +{ +	ret=0 + +	ip addrlabel add prefix dead::/64 dev lo label 1 +	check_err $? + +	ip addrlabel list |grep -q "prefix dead::/64 dev lo label 1" +	check_err $? + +	ip addrlabel del prefix dead::/64 dev lo label 1 2> /dev/null +	check_err $? + +	ip addrlabel add prefix dead::/64 label 1 2> /dev/null +	check_err $? + +	ip addrlabel del prefix dead::/64 label 1 2> /dev/null +	check_err $? + +	# concurrent add/delete +	for i in $(seq 1 1000); do +		ip addrlabel add prefix 1c3::/64 label 12345 2>/dev/null +	done & + +	for i in $(seq 1 1000); do +		ip addrlabel del prefix 1c3::/64 label 12345 2>/dev/null +	done + +	wait + +	ip addrlabel del prefix 1c3::/64 label 12345 2>/dev/null + +	if [ $ret -ne 0 ];then +		echo "FAIL: ipv6 addrlabel" +		return 1 +	fi + +	echo "PASS: ipv6 addrlabel" +} + +kci_test_rtnl() +{ +	kci_add_dummy +	if [ $ret -ne 0 ];then +		echo "FAIL: cannot add dummy interface" +		return 1 +	fi + +	kci_test_polrouting +	kci_test_route_get +	kci_test_tc +	kci_test_gre +	kci_test_bridge +	kci_test_addrlabel + +	kci_del_dummy +} + +#check for needed privileges +if [ "$(id -u)" -ne 0 ];then +	echo "SKIP: Need root privileges" +	exit 0 +fi + +for x in ip tc;do +	$x -Version 2>/dev/null >/dev/null +	if [ $? -ne 0 ];then +		echo "SKIP: Could not run test without the $x tool" +		exit 0 +	fi +done + +kci_test_rtnl + +exit $ret diff --git a/tools/testing/selftests/networking/timestamping/.gitignore b/tools/testing/selftests/networking/timestamping/.gitignore index 9e69e982fb38..d9355035e746 100644 --- a/tools/testing/selftests/networking/timestamping/.gitignore +++ b/tools/testing/selftests/networking/timestamping/.gitignore @@ -1,3 +1,4 @@  timestamping +rxtimestamp  txtimestamp  hwtstamp_config diff --git a/tools/testing/selftests/networking/timestamping/Makefile b/tools/testing/selftests/networking/timestamping/Makefile index ccbb9edbbbb9..92fb8ee917c5 100644 --- a/tools/testing/selftests/networking/timestamping/Makefile +++ b/tools/testing/selftests/networking/timestamping/Makefile @@ -1,4 +1,6 @@ -TEST_PROGS := hwtstamp_config timestamping txtimestamp +CFLAGS += -I../../../../../usr/include + +TEST_PROGS := hwtstamp_config rxtimestamp timestamping txtimestamp  all: $(TEST_PROGS) diff --git a/tools/testing/selftests/networking/timestamping/rxtimestamp.c b/tools/testing/selftests/networking/timestamping/rxtimestamp.c new file mode 100644 index 000000000000..00f286661dcd --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/rxtimestamp.c @@ -0,0 +1,389 @@ +#include <errno.h> +#include <error.h> +#include <getopt.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/select.h> +#include <sys/ioctl.h> +#include <arpa/inet.h> +#include <net/if.h> + +#include <asm/types.h> +#include <linux/net_tstamp.h> +#include <linux/errqueue.h> + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +struct options { +	int so_timestamp; +	int so_timestampns; +	int so_timestamping; +}; + +struct tstamps { +	bool tstamp; +	bool tstampns; +	bool swtstamp; +	bool hwtstamp; +}; + +struct socket_type { +	char *friendly_name; +	int type; +	int protocol; +	bool enabled; +}; + +struct test_case { +	struct options sockopt; +	struct tstamps expected; +	bool enabled; +}; + +struct sof_flag { +	int mask; +	char *name; +}; + +static struct sof_flag sof_flags[] = { +#define SOF_FLAG(f) { f, #f } +	SOF_FLAG(SOF_TIMESTAMPING_SOFTWARE), +	SOF_FLAG(SOF_TIMESTAMPING_RX_SOFTWARE), +	SOF_FLAG(SOF_TIMESTAMPING_RX_HARDWARE), +}; + +static struct socket_type socket_types[] = { +	{ "ip",		SOCK_RAW,	IPPROTO_EGP }, +	{ "udp",	SOCK_DGRAM,	IPPROTO_UDP }, +	{ "tcp",	SOCK_STREAM,	IPPROTO_TCP }, +}; + +static struct test_case test_cases[] = { +	{ {}, {} }, +	{ +		{ so_timestamp: 1 }, +		{ tstamp: true } +	}, +	{ +		{ so_timestampns: 1 }, +		{ tstampns: true } +	}, +	{ +		{ so_timestamp: 1, so_timestampns: 1 }, +		{ tstampns: true } +	}, +	{ +		{ so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE }, +		{} +	}, +	{ +		/* Loopback device does not support hw timestamps. */ +		{ so_timestamping: SOF_TIMESTAMPING_RX_HARDWARE }, +		{} +	}, +	{ +		{ so_timestamping: SOF_TIMESTAMPING_SOFTWARE }, +		{} +	}, +	{ +		{ so_timestamping: SOF_TIMESTAMPING_RX_SOFTWARE +			| SOF_TIMESTAMPING_RX_HARDWARE }, +		{} +	}, +	{ +		{ so_timestamping: SOF_TIMESTAMPING_SOFTWARE +			| SOF_TIMESTAMPING_RX_SOFTWARE }, +		{ swtstamp: true } +	}, +	{ +		{ so_timestamp: 1, so_timestamping: SOF_TIMESTAMPING_SOFTWARE +			| SOF_TIMESTAMPING_RX_SOFTWARE }, +		{ tstamp: true, swtstamp: true } +	}, +}; + +static struct option long_options[] = { +	{ "list_tests", no_argument, 0, 'l' }, +	{ "test_num", required_argument, 0, 'n' }, +	{ "op_size", required_argument, 0, 's' }, +	{ "tcp", no_argument, 0, 't' }, +	{ "udp", no_argument, 0, 'u' }, +	{ "ip", no_argument, 0, 'i' }, +}; + +static int next_port = 19999; +static int op_size = 10 * 1024; + +void print_test_case(struct test_case *t) +{ +	int f = 0; + +	printf("sockopts {"); +	if (t->sockopt.so_timestamp) +		printf(" SO_TIMESTAMP "); +	if (t->sockopt.so_timestampns) +		printf(" SO_TIMESTAMPNS "); +	if (t->sockopt.so_timestamping) { +		printf(" SO_TIMESTAMPING: {"); +		for (f = 0; f < ARRAY_SIZE(sof_flags); f++) +			if (t->sockopt.so_timestamping & sof_flags[f].mask) +				printf(" %s |", sof_flags[f].name); +		printf("}"); +	} +	printf("} expected cmsgs: {"); +	if (t->expected.tstamp) +		printf(" SCM_TIMESTAMP "); +	if (t->expected.tstampns) +		printf(" SCM_TIMESTAMPNS "); +	if (t->expected.swtstamp || t->expected.hwtstamp) { +		printf(" SCM_TIMESTAMPING {"); +		if (t->expected.swtstamp) +			printf("0"); +		if (t->expected.swtstamp && t->expected.hwtstamp) +			printf(","); +		if (t->expected.hwtstamp) +			printf("2"); +		printf("}"); +	} +	printf("}\n"); +} + +void do_send(int src) +{ +	int r; +	char *buf = malloc(op_size); + +	memset(buf, 'z', op_size); +	r = write(src, buf, op_size); +	if (r < 0) +		error(1, errno, "Failed to sendmsg"); + +	free(buf); +} + +bool do_recv(int rcv, int read_size, struct tstamps expected) +{ +	const int CMSG_SIZE = 1024; + +	struct scm_timestamping *ts; +	struct tstamps actual = {}; +	char cmsg_buf[CMSG_SIZE]; +	struct iovec recv_iov; +	struct cmsghdr *cmsg; +	bool failed = false; +	struct msghdr hdr; +	int flags = 0; +	int r; + +	memset(&hdr, 0, sizeof(hdr)); +	hdr.msg_iov = &recv_iov; +	hdr.msg_iovlen = 1; +	recv_iov.iov_base = malloc(read_size); +	recv_iov.iov_len = read_size; + +	hdr.msg_control = cmsg_buf; +	hdr.msg_controllen = sizeof(cmsg_buf); + +	r = recvmsg(rcv, &hdr, flags); +	if (r < 0) +		error(1, errno, "Failed to recvmsg"); +	if (r != read_size) +		error(1, 0, "Only received %d bytes of payload.", r); + +	if (hdr.msg_flags & (MSG_TRUNC | MSG_CTRUNC)) +		error(1, 0, "Message was truncated."); + +	for (cmsg = CMSG_FIRSTHDR(&hdr); cmsg != NULL; +	     cmsg = CMSG_NXTHDR(&hdr, cmsg)) { +		if (cmsg->cmsg_level != SOL_SOCKET) +			error(1, 0, "Unexpected cmsg_level %d", +			      cmsg->cmsg_level); +		switch (cmsg->cmsg_type) { +		case SCM_TIMESTAMP: +			actual.tstamp = true; +			break; +		case SCM_TIMESTAMPNS: +			actual.tstampns = true; +			break; +		case SCM_TIMESTAMPING: +			ts = (struct scm_timestamping *)CMSG_DATA(cmsg); +			actual.swtstamp = !!ts->ts[0].tv_sec; +			if (ts->ts[1].tv_sec != 0) +				error(0, 0, "ts[1] should not be set."); +			actual.hwtstamp = !!ts->ts[2].tv_sec; +			break; +		default: +			error(1, 0, "Unexpected cmsg_type %d", cmsg->cmsg_type); +		} +	} + +#define VALIDATE(field) \ +	do { \ +		if (expected.field != actual.field) { \ +			if (expected.field) \ +				error(0, 0, "Expected " #field " to be set."); \ +			else \ +				error(0, 0, \ +				      "Expected " #field " to not be set."); \ +			failed = true; \ +		} \ +	} while (0) + +	VALIDATE(tstamp); +	VALIDATE(tstampns); +	VALIDATE(swtstamp); +	VALIDATE(hwtstamp); +#undef VALIDATE + +	free(recv_iov.iov_base); + +	return failed; +} + +void config_so_flags(int rcv, struct options o) +{ +	int on = 1; + +	if (setsockopt(rcv, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) +		error(1, errno, "Failed to enable SO_REUSEADDR"); + +	if (o.so_timestamp && +	    setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMP, +		       &o.so_timestamp, sizeof(o.so_timestamp)) < 0) +		error(1, errno, "Failed to enable SO_TIMESTAMP"); + +	if (o.so_timestampns && +	    setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPNS, +		       &o.so_timestampns, sizeof(o.so_timestampns)) < 0) +		error(1, errno, "Failed to enable SO_TIMESTAMPNS"); + +	if (o.so_timestamping && +	    setsockopt(rcv, SOL_SOCKET, SO_TIMESTAMPING, +		       &o.so_timestamping, sizeof(o.so_timestamping)) < 0) +		error(1, errno, "Failed to set SO_TIMESTAMPING"); +} + +bool run_test_case(struct socket_type s, struct test_case t) +{ +	int port = (s.type == SOCK_RAW) ? 0 : next_port++; +	int read_size = op_size; +	struct sockaddr_in addr; +	bool failed = false; +	int src, dst, rcv; + +	src = socket(AF_INET, s.type, s.protocol); +	if (src < 0) +		error(1, errno, "Failed to open src socket"); + +	dst = socket(AF_INET, s.type, s.protocol); +	if (dst < 0) +		error(1, errno, "Failed to open dst socket"); + +	memset(&addr, 0, sizeof(addr)); +	addr.sin_family = AF_INET; +	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +	addr.sin_port = htons(port); + +	if (bind(dst, (struct sockaddr *)&addr, sizeof(addr)) < 0) +		error(1, errno, "Failed to bind to port %d", port); + +	if (s.type == SOCK_STREAM && (listen(dst, 1) < 0)) +		error(1, errno, "Failed to listen"); + +	if (connect(src, (struct sockaddr *)&addr, sizeof(addr)) < 0) +		error(1, errno, "Failed to connect"); + +	if (s.type == SOCK_STREAM) { +		rcv = accept(dst, NULL, NULL); +		if (rcv < 0) +			error(1, errno, "Failed to accept"); +		close(dst); +	} else { +		rcv = dst; +	} + +	config_so_flags(rcv, t.sockopt); +	usleep(20000); /* setsockopt for SO_TIMESTAMPING is asynchronous */ +	do_send(src); + +	if (s.type == SOCK_RAW) +		read_size += 20;  /* for IP header */ +	failed = do_recv(rcv, read_size, t.expected); + +	close(rcv); +	close(src); + +	return failed; +} + +int main(int argc, char **argv) +{ +	bool all_protocols = true; +	bool all_tests = true; +	int arg_index = 0; +	int failures = 0; +	int s, t; +	char opt; + +	while ((opt = getopt_long(argc, argv, "", long_options, +				  &arg_index)) != -1) { +		switch (opt) { +		case 'l': +			for (t = 0; t < ARRAY_SIZE(test_cases); t++) { +				printf("%d\t", t); +				print_test_case(&test_cases[t]); +			} +			return 0; +		case 'n': +			t = atoi(optarg); +			if (t > ARRAY_SIZE(test_cases)) +				error(1, 0, "Invalid test case: %d", t); +			all_tests = false; +			test_cases[t].enabled = true; +			break; +		case 's': +			op_size = atoi(optarg); +			break; +		case 't': +			all_protocols = false; +			socket_types[2].enabled = true; +			break; +		case 'u': +			all_protocols = false; +			socket_types[1].enabled = true; +			break; +		case 'i': +			all_protocols = false; +			socket_types[0].enabled = true; +			break; +		default: +			error(1, 0, "Failed to parse parameters."); +		} +	} + +	for (s = 0; s < ARRAY_SIZE(socket_types); s++) { +		if (!all_protocols && !socket_types[s].enabled) +			continue; + +		printf("Testing %s...\n", socket_types[s].friendly_name); +		for (t = 0; t < ARRAY_SIZE(test_cases); t++) { +			if (!all_tests && !test_cases[t].enabled) +				continue; + +			printf("Starting testcase %d...\n", t); +			if (run_test_case(socket_types[s], test_cases[t])) { +				failures++; +				printf("FAILURE in test case "); +				print_test_case(&test_cases[t]); +			} +		} +	} +	if (!failures) +		printf("PASSED.\n"); +	return failures; +} diff --git a/tools/testing/selftests/nsfs/config b/tools/testing/selftests/nsfs/config new file mode 100644 index 000000000000..598d0a225fc9 --- /dev/null +++ b/tools/testing/selftests/nsfs/config @@ -0,0 +1,3 @@ +CONFIG_USER_NS=y +CONFIG_UTS_NS=y +CONFIG_PID_NS=y diff --git a/tools/testing/selftests/ntb/ntb_test.sh b/tools/testing/selftests/ntb/ntb_test.sh index 1c12b5855e4f..5fc7ad359e21 100755 --- a/tools/testing/selftests/ntb/ntb_test.sh +++ b/tools/testing/selftests/ntb/ntb_test.sh @@ -333,6 +333,10 @@ function ntb_tool_tests()  	link_test $LOCAL_TOOL $REMOTE_TOOL  	link_test $REMOTE_TOOL $LOCAL_TOOL +	#Ensure the link is up on both sides before continuing +	write_file Y $LOCAL_TOOL/link_event +	write_file Y $REMOTE_TOOL/link_event +  	for PEER_TRANS in $(ls $LOCAL_TOOL/peer_trans*); do  		PT=$(basename $PEER_TRANS)  		write_file $MW_SIZE $LOCAL_TOOL/$PT diff --git a/tools/testing/selftests/powerpc/ptrace/Makefile b/tools/testing/selftests/powerpc/ptrace/Makefile index fe6bc60dfc60..8932263e5a74 100644 --- a/tools/testing/selftests/powerpc/ptrace/Makefile +++ b/tools/testing/selftests/powerpc/ptrace/Makefile @@ -6,7 +6,7 @@ include ../../lib.mk  all: $(TEST_PROGS) -CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm +CFLAGS += -m64 -I../../../../../usr/include -I../tm -mhtm -fno-pie  $(TEST_PROGS): ../harness.c ../utils.c ../lib/reg.S ptrace.h diff --git a/tools/testing/selftests/powerpc/switch_endian/switch_endian_test.S b/tools/testing/selftests/powerpc/switch_endian/switch_endian_test.S index ef7c971abb67..bceb53f57573 100644 --- a/tools/testing/selftests/powerpc/switch_endian/switch_endian_test.S +++ b/tools/testing/selftests/powerpc/switch_endian/switch_endian_test.S @@ -8,7 +8,7 @@ message:  	.section ".toc"  	.balign 8  pattern: -	.llong 0x5555AAAA5555AAAA +	.8byte 0x5555AAAA5555AAAA  	.text  FUNC_START(_start) diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 958c11c14acd..7bfcd454fb2a 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile @@ -15,6 +15,7 @@ $(OUTPUT)/tm-syscall: tm-syscall-asm.S  $(OUTPUT)/tm-syscall: CFLAGS += -I../../../../../usr/include  $(OUTPUT)/tm-tmspr: CFLAGS += -pthread  $(OUTPUT)/tm-vmx-unavail: CFLAGS += -pthread -m64 +$(OUTPUT)/tm-resched-dscr: ../pmu/lib.o  SIGNAL_CONTEXT_CHK_TESTS := $(patsubst %,$(OUTPUT)/%,$(SIGNAL_CONTEXT_CHK_TESTS))  $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S diff --git a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c index e79ccd6aada1..a7ac2e4c60d9 100644 --- a/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c +++ b/tools/testing/selftests/powerpc/tm/tm-resched-dscr.c @@ -30,6 +30,7 @@  #include "utils.h"  #include "tm.h" +#include "../pmu/lib.h"  #define SPRN_DSCR       0x03 @@ -75,8 +76,6 @@ int test_body(void)  		);  		assert(rv); /* make sure the transaction aborted */  		if ((texasr >> 56) != TM_CAUSE_RESCHED) { -			putchar('.'); -			fflush(stdout);  			continue;  		}  		if (dscr2 != dscr1) { @@ -89,7 +88,12 @@ int test_body(void)  	}  } -int main(void) +static int tm_resched_dscr(void)  { -	return test_harness(test_body, "tm_resched_dscr"); +	return eat_cpu(test_body); +} + +int main(int argc, const char *argv[]) +{ +	return test_harness(tm_resched_dscr, "tm_resched_dscr");  } diff --git a/tools/testing/selftests/pstore/.gitignore b/tools/testing/selftests/pstore/.gitignore new file mode 100644 index 000000000000..5a4a26e5464b --- /dev/null +++ b/tools/testing/selftests/pstore/.gitignore @@ -0,0 +1,2 @@ +logs +*uuid diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile index 83dd42b2129e..d4064c742c26 100644 --- a/tools/testing/selftests/ptp/Makefile +++ b/tools/testing/selftests/ptp/Makefile @@ -1,3 +1,4 @@ +CFLAGS += -I../../../../usr/include/  TEST_PROGS := testptp  LDLIBS += -lrt  all: $(TEST_PROGS) diff --git a/tools/testing/selftests/rcutorture/bin/config_override.sh b/tools/testing/selftests/rcutorture/bin/config_override.sh new file mode 100755 index 000000000000..49fa51726ce3 --- /dev/null +++ b/tools/testing/selftests/rcutorture/bin/config_override.sh @@ -0,0 +1,61 @@ +#!/bin/bash +# +# config_override.sh base override +# +# Combines base and override, removing any Kconfig options from base +# that conflict with any in override, concatenating what remains and +# sending the result to standard output. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, you can access it online at +# http://www.gnu.org/licenses/gpl-2.0.html. +# +# Copyright (C) IBM Corporation, 2017 +# +# Authors: Paul E. McKenney <paulmck@linux.vnet.ibm.com> + +base=$1 +if test -r $base +then +	: +else +	echo Base file $base unreadable!!! +	exit 1 +fi + +override=$2 +if test -r $override +then +	: +else +	echo Override file $override unreadable!!! +	exit 1 +fi + +T=/tmp/config_override.sh.$$ +trap 'rm -rf $T' 0 +mkdir $T + +sed < $override -e 's/^/grep -v "/' -e 's/=.*$/="/' | +	awk ' +	{ +		if (last) +			print last " |"; +		last = $0; +	} +	END { +		if (last) +			print last; +	}' > $T/script +sh $T/script < $base +cat $override diff --git a/tools/testing/selftests/rcutorture/bin/functions.sh b/tools/testing/selftests/rcutorture/bin/functions.sh index 1426a9b97494..07a13779eece 100644 --- a/tools/testing/selftests/rcutorture/bin/functions.sh +++ b/tools/testing/selftests/rcutorture/bin/functions.sh @@ -66,9 +66,34 @@ configfrag_boot_params () {  # configfrag_boot_cpus bootparam-string config-fragment-file config-cpus  # -# Decreases number of CPUs based on any maxcpus= boot parameters specified. +# Decreases number of CPUs based on any nr_cpus= boot parameters specified.  configfrag_boot_cpus () {  	local bootargs="`configfrag_boot_params "$1" "$2"`" +	local nr_cpus +	if echo "${bootargs}" | grep -q 'nr_cpus=[0-9]' +	then +		nr_cpus="`echo "${bootargs}" | sed -e 's/^.*nr_cpus=\([0-9]*\).*$/\1/'`" +		if test "$3" -gt "$nr_cpus" +		then +			echo $nr_cpus +		else +			echo $3 +		fi +	else +		echo $3 +	fi +} + +# configfrag_boot_maxcpus bootparam-string config-fragment-file config-cpus +# +# Decreases number of CPUs based on any maxcpus= boot parameters specified. +# This allows tests where additional CPUs come online later during the +# test run.  However, the torture parameters will be set based on the +# number of CPUs initially present, so the scripting should schedule +# test runs based on the maxcpus= boot parameter controlling the initial +# number of CPUs instead of on the ultimate number of CPUs. +configfrag_boot_maxcpus () { +	local bootargs="`configfrag_boot_params "$1" "$2"`"  	local maxcpus  	if echo "${bootargs}" | grep -q 'maxcpus=[0-9]'  	then diff --git a/tools/testing/selftests/rcutorture/bin/kvm-build.sh b/tools/testing/selftests/rcutorture/bin/kvm-build.sh index c29f2ec0bf9f..46752c164676 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-build.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-build.sh @@ -2,7 +2,7 @@  #  # Build a kvm-ready Linux kernel from the tree in the current directory.  # -# Usage: kvm-build.sh config-template build-dir more-configs +# Usage: kvm-build.sh config-template build-dir  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License as published by @@ -34,24 +34,17 @@ then  	echo "kvm-build.sh :$builddir: Not a writable directory, cannot build into it"  	exit 1  fi -moreconfigs=${3} -if test -z "$moreconfigs" -o ! -r "$moreconfigs" -then -	echo "kvm-build.sh :$moreconfigs: Not a readable file" -	exit 1 -fi  T=/tmp/test-linux.sh.$$  trap 'rm -rf $T' 0  mkdir $T -grep -v 'CONFIG_[A-Z]*_TORTURE_TEST=' < ${config_template} > $T/config +cp ${config_template} $T/config  cat << ___EOF___ >> $T/config  CONFIG_INITRAMFS_SOURCE="$TORTURE_INITRD"  CONFIG_VIRTIO_PCI=y  CONFIG_VIRTIO_CONSOLE=y  ___EOF___ -cat $moreconfigs >> $T/config  configinit.sh $T/config O=$builddir  retval=$? diff --git a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh index 93eede4e8fbe..0af36a721b9c 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-test-1-run.sh @@ -40,7 +40,7 @@  T=/tmp/kvm-test-1-run.sh.$$  trap 'rm -rf $T' 0 -touch $T +mkdir $T  . $KVM/bin/functions.sh  . $CONFIGFRAG/ver_functions.sh @@ -60,37 +60,33 @@ then  	echo "kvm-test-1-run.sh :$resdir: Not a writable directory, cannot store results into it"  	exit 1  fi -cp $config_template $resdir/ConfigFragment  echo ' ---' `date`: Starting build  echo ' ---' Kconfig fragment at: $config_template >> $resdir/log +touch $resdir/ConfigFragment.input $resdir/ConfigFragment  if test -r "$config_dir/CFcommon"  then -	cat < $config_dir/CFcommon >> $T +	echo " --- $config_dir/CFcommon" >> $resdir/ConfigFragment.input +	cat < $config_dir/CFcommon >> $resdir/ConfigFragment.input +	config_override.sh $config_dir/CFcommon $config_template > $T/Kc1 +	grep '#CHECK#' $config_dir/CFcommon >> $resdir/ConfigFragment +else +	cp $config_template $T/Kc1  fi -# Optimizations below this point -# CONFIG_USB=n -# CONFIG_SECURITY=n -# CONFIG_NFS_FS=n -# CONFIG_SOUND=n -# CONFIG_INPUT_JOYSTICK=n -# CONFIG_INPUT_TABLET=n -# CONFIG_INPUT_TOUCHSCREEN=n -# CONFIG_INPUT_MISC=n -# CONFIG_INPUT_MOUSE=n -# # CONFIG_NET=n # disables console access, so accept the slower build. -# CONFIG_SCSI=n -# CONFIG_ATA=n -# CONFIG_FAT_FS=n -# CONFIG_MSDOS_FS=n -# CONFIG_VFAT_FS=n -# CONFIG_ISO9660_FS=n -# CONFIG_QUOTA=n -# CONFIG_HID=n -# CONFIG_CRYPTO=n -# CONFIG_PCCARD=n -# CONFIG_PCMCIA=n -# CONFIG_CARDBUS=n -# CONFIG_YENTA=n +echo " --- $config_template" >> $resdir/ConfigFragment.input +cat $config_template >> $resdir/ConfigFragment.input +grep '#CHECK#' $config_template >> $resdir/ConfigFragment +if test -n "$TORTURE_KCONFIG_ARG" +then +	echo $TORTURE_KCONFIG_ARG | tr -s " " "\012" > $T/cmdline +	echo " --- --kconfig argument" >> $resdir/ConfigFragment.input +	cat $T/cmdline >> $resdir/ConfigFragment.input +	config_override.sh $T/Kc1 $T/cmdline > $T/Kc2 +	# Note that "#CHECK#" is not permitted on commandline. +else +	cp $T/Kc1 $T/Kc2 +fi +cat $T/Kc2 >> $resdir/ConfigFragment +  base_resdir=`echo $resdir | sed -e 's/\.[0-9]\+$//'`  if test "$base_resdir" != "$resdir" -a -f $base_resdir/bzImage -a -f $base_resdir/vmlinux  then @@ -100,7 +96,9 @@ then  	KERNEL=$base_resdir/${BOOT_IMAGE##*/} # use the last component of ${BOOT_IMAGE}  	ln -s $base_resdir/Make*.out $resdir  # for kvm-recheck.sh  	ln -s $base_resdir/.config $resdir  # for kvm-recheck.sh -elif kvm-build.sh $config_template $builddir $T +	# Arch-independent indicator +	touch $resdir/builtkernel +elif kvm-build.sh $T/Kc2 $builddir  then  	# Had to build a kernel for this test.  	QEMU="`identify_qemu $builddir/vmlinux`" @@ -112,6 +110,8 @@ then  	then  		cp $builddir/$BOOT_IMAGE $resdir  		KERNEL=$resdir/${BOOT_IMAGE##*/} +		# Arch-independent indicator +		touch $resdir/builtkernel  	else  		echo No identifiable boot image, not running KVM, see $resdir.  		echo Do the torture scripts know about your architecture? @@ -149,7 +149,7 @@ fi  # Generate -smp qemu argument.  qemu_args="-enable-kvm -nographic $qemu_args" -cpu_count=`configNR_CPUS.sh $config_template` +cpu_count=`configNR_CPUS.sh $resdir/ConfigFragment`  cpu_count=`configfrag_boot_cpus "$boot_args" "$config_template" "$cpu_count"`  vcpus=`identify_qemu_vcpus`  if test $cpu_count -gt $vcpus diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 50091de3a911..b55895fb10ed 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -41,6 +41,7 @@ PATH=${KVM}/bin:$PATH; export PATH  TORTURE_DEFCONFIG=defconfig  TORTURE_BOOT_IMAGE=""  TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD +TORTURE_KCONFIG_ARG=""  TORTURE_KMAKE_ARG=""  TORTURE_SHUTDOWN_GRACE=180  TORTURE_SUITE=rcu @@ -65,6 +66,7 @@ usage () {  	echo "       --duration minutes"  	echo "       --interactive"  	echo "       --jitter N [ maxsleep (us) [ maxspin (us) ] ]" +	echo "       --kconfig Kconfig-options"  	echo "       --kmake-arg kernel-make-arguments"  	echo "       --mac nn:nn:nn:nn:nn:nn"  	echo "       --no-initrd" @@ -129,6 +131,11 @@ do  		jitter="$2"  		shift  		;; +	--kconfig) +		checkarg --kconfig "(Kconfig options)" $# "$2" '^CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\( CONFIG_[A-Z0-9_]\+=\([ynm]\|[0-9]\+\)\)*$' '^error$' +		TORTURE_KCONFIG_ARG="$2" +		shift +		;;  	--kmake-arg)  		checkarg --kmake-arg "(kernel make arguments)" $# "$2" '.*' '^error$'  		TORTURE_KMAKE_ARG="$2" @@ -205,6 +212,7 @@ do  	then  		cpu_count=`configNR_CPUS.sh $CONFIGFRAG/$CF1`  		cpu_count=`configfrag_boot_cpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"` +		cpu_count=`configfrag_boot_maxcpus "$TORTURE_BOOTARGS" "$CONFIGFRAG/$CF1" "$cpu_count"`  		for ((cur_rep=0;cur_rep<$config_reps;cur_rep++))  		do  			echo $CF1 $cpu_count >> $T/cfgcpu @@ -275,6 +283,7 @@ TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE  TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY  TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG  TORTURE_INITRD="$TORTURE_INITRD"; export TORTURE_INITRD +TORTURE_KCONFIG_ARG="$TORTURE_KCONFIG_ARG"; export TORTURE_KCONFIG_ARG  TORTURE_KMAKE_ARG="$TORTURE_KMAKE_ARG"; export TORTURE_KMAKE_ARG  TORTURE_QEMU_CMD="$TORTURE_QEMU_CMD"; export TORTURE_QEMU_CMD  TORTURE_QEMU_INTERACTIVE="$TORTURE_QEMU_INTERACTIVE"; export TORTURE_QEMU_INTERACTIVE @@ -324,6 +333,7 @@ function dump(first, pastlast, batchnum)  {  	print "echo ----Start batch " batchnum ": `date`";  	print "echo ----Start batch " batchnum ": `date` >> " rd "/log"; +	print "needqemurun="  	jn=1  	for (j = first; j < pastlast; j++) {  		builddir=KVM "/b" jn @@ -359,10 +369,11 @@ function dump(first, pastlast, batchnum)  	for (j = 1; j < jn; j++) {  		builddir=KVM "/b" j  		print "rm -f " builddir ".ready" -		print "if test -z \"$TORTURE_BUILDONLY\"" +		print "if test -f \"" rd cfr[j] "/builtkernel\""  		print "then" -		print "\techo ----", cfr[j], cpusr[j] ovf ": Starting kernel. `date`"; -		print "\techo ----", cfr[j], cpusr[j] ovf ": Starting kernel. `date` >> " rd "/log"; +		print "\techo ----", cfr[j], cpusr[j] ovf ": Kernel present. `date`"; +		print "\techo ----", cfr[j], cpusr[j] ovf ": Kernel present. `date` >> " rd "/log"; +		print "\tneedqemurun=1"  		print "fi"  	}  	njitter = 0; @@ -377,13 +388,22 @@ function dump(first, pastlast, batchnum)  		njitter = 0;  		print "echo Build-only run, so suppressing jitter >> " rd "/log"  	} -	for (j = 0; j < njitter; j++) -		print "jitter.sh " j " " dur " " ja[2] " " ja[3] "&" -	print "wait" -	print "if test -z \"$TORTURE_BUILDONLY\"" +	if (TORTURE_BUILDONLY) { +		print "needqemurun=" +	} +	print "if test -n \"$needqemurun\""  	print "then" +	print "\techo ---- Starting kernels. `date`"; +	print "\techo ---- Starting kernels. `date` >> " rd "/log"; +	for (j = 0; j < njitter; j++) +		print "\tjitter.sh " j " " dur " " ja[2] " " ja[3] "&" +	print "\twait"  	print "\techo ---- All kernel runs complete. `date`";  	print "\techo ---- All kernel runs complete. `date` >> " rd "/log"; +	print "else" +	print "\twait" +	print "\techo ---- No kernel runs. `date`"; +	print "\techo ---- No kernel runs. `date` >> " rd "/log";  	print "fi"  	for (j = 1; j < jn; j++) {  		builddir=KVM "/b" j diff --git a/tools/testing/selftests/rcutorture/configs/rcu/BUSTED.boot b/tools/testing/selftests/rcutorture/configs/rcu/BUSTED.boot index 6804f9dcfc1b..be7728db42fd 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/BUSTED.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/BUSTED.boot @@ -1 +1 @@ -rcutorture.torture_type=rcu_busted +rcutorture.torture_type=busted diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-C.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-C.boot deleted file mode 100644 index 84a7d51b7481..000000000000 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-C.boot +++ /dev/null @@ -1 +0,0 @@ -rcutorture.torture_type=srcud diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u index 6bc24e99862f..c15ada821e45 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u @@ -4,6 +4,7 @@ CONFIG_PREEMPT_VOLUNTARY=n  CONFIG_PREEMPT=n  #CHECK#CONFIG_TINY_SRCU=y  CONFIG_RCU_TRACE=n -CONFIG_DEBUG_LOCK_ALLOC=n +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y  CONFIG_DEBUG_OBJECTS_RCU_HEAD=n  CONFIG_PREEMPT_COUNT=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot index 1d14e1383016..9f3a4d28e508 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot @@ -1,4 +1,4 @@ -rcutorture.torture_type=rcu_bh maxcpus=8 +rcutorture.torture_type=rcu_bh maxcpus=8 nr_cpus=43  rcutree.gp_preinit_delay=3  rcutree.gp_init_delay=3  rcutree.gp_cleanup_delay=3 diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt index 9ad3f89c8dc7..af6fca03602f 100644 --- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt +++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt @@ -69,11 +69,11 @@ CONFIG_RCU_TORTURE_TEST_RUNNABLE  CONFIG_PREEMPT_RCU  CONFIG_TREE_RCU  CONFIG_TINY_RCU +CONFIG_TASKS_RCU  	These are controlled by CONFIG_PREEMPT and/or CONFIG_SMP.  CONFIG_SRCU -CONFIG_TASKS_RCU  	Selected by CONFIG_RCU_TORTURE_TEST, so cannot disable. diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 73f5ea6778ce..4d6f92a9df6b 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -107,7 +107,7 @@ TEST(mode_strict_support)  	ASSERT_EQ(0, ret) {  		TH_LOG("Kernel does not support CONFIG_SECCOMP");  	} -	syscall(__NR_exit, 1); +	syscall(__NR_exit, 0);  }  TEST_SIGNAL(mode_strict_cannot_call_prctl, SIGKILL) diff --git a/tools/testing/selftests/sigaltstack/sas.c b/tools/testing/selftests/sigaltstack/sas.c index ccd07343d418..7d406c3973ba 100644 --- a/tools/testing/selftests/sigaltstack/sas.c +++ b/tools/testing/selftests/sigaltstack/sas.c @@ -17,6 +17,8 @@  #include <assert.h>  #include <errno.h> +#include "../kselftest.h" +  #ifndef SS_AUTODISARM  #define SS_AUTODISARM  (1U << 31)  #endif @@ -41,8 +43,7 @@ void my_usr1(int sig, siginfo_t *si, void *u)  	if (sp < (unsigned long)sstack ||  			sp >= (unsigned long)sstack + SIGSTKSZ) { -		printf("[FAIL]\tSP is not on sigaltstack\n"); -		exit(EXIT_FAILURE); +		ksft_exit_fail_msg("SP is not on sigaltstack\n");  	}  	/* put some data on stack. other sighandler will try to overwrite it */  	aa = alloca(1024); @@ -50,21 +51,22 @@ void my_usr1(int sig, siginfo_t *si, void *u)  	p = (struct stk_data *)(aa + 512);  	strcpy(p->msg, msg);  	p->flag = 1; -	printf("[RUN]\tsignal USR1\n"); +	ksft_print_msg("[RUN]\tsignal USR1\n");  	err = sigaltstack(NULL, &stk);  	if (err) { -		perror("[FAIL]\tsigaltstack()"); +		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));  		exit(EXIT_FAILURE);  	}  	if (stk.ss_flags != SS_DISABLE) -		printf("[FAIL]\tss_flags=%x, should be SS_DISABLE\n", +		ksft_test_result_fail("tss_flags=%x, should be SS_DISABLE\n",  				stk.ss_flags);  	else -		printf("[OK]\tsigaltstack is disabled in sighandler\n"); +		ksft_test_result_pass( +				"sigaltstack is disabled in sighandler\n");  	swapcontext(&sc, &uc); -	printf("%s\n", p->msg); +	ksft_print_msg("%s\n", p->msg);  	if (!p->flag) { -		printf("[RUN]\tAborting\n"); +		ksft_exit_skip("[RUN]\tAborting\n");  		exit(EXIT_FAILURE);  	}  } @@ -74,13 +76,13 @@ void my_usr2(int sig, siginfo_t *si, void *u)  	char *aa;  	struct stk_data *p; -	printf("[RUN]\tsignal USR2\n"); +	ksft_print_msg("[RUN]\tsignal USR2\n");  	aa = alloca(1024);  	/* dont run valgrind on this */  	/* try to find the data stored by previous sighandler */  	p = memmem(aa, 1024, msg, strlen(msg));  	if (p) { -		printf("[FAIL]\tsigaltstack re-used\n"); +		ksft_test_result_fail("sigaltstack re-used\n");  		/* corrupt the data */  		strcpy(p->msg, msg2);  		/* tell other sighandler that his data is corrupted */ @@ -90,7 +92,7 @@ void my_usr2(int sig, siginfo_t *si, void *u)  static void switch_fn(void)  { -	printf("[RUN]\tswitched to user ctx\n"); +	ksft_print_msg("[RUN]\tswitched to user ctx\n");  	raise(SIGUSR2);  	setcontext(&sc);  } @@ -101,6 +103,8 @@ int main(void)  	stack_t stk;  	int err; +	ksft_print_header(); +  	sigemptyset(&act.sa_mask);  	act.sa_flags = SA_ONSTACK | SA_SIGINFO;  	act.sa_sigaction = my_usr1; @@ -110,19 +114,20 @@ int main(void)  	sstack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,  		      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);  	if (sstack == MAP_FAILED) { -		perror("mmap()"); +		ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));  		return EXIT_FAILURE;  	}  	err = sigaltstack(NULL, &stk);  	if (err) { -		perror("[FAIL]\tsigaltstack()"); +		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));  		exit(EXIT_FAILURE);  	}  	if (stk.ss_flags == SS_DISABLE) { -		printf("[OK]\tInitial sigaltstack state was SS_DISABLE\n"); +		ksft_test_result_pass( +				"Initial sigaltstack state was SS_DISABLE\n");  	} else { -		printf("[FAIL]\tInitial sigaltstack state was %x; " +		ksft_exit_fail_msg("Initial sigaltstack state was %x; "  		       "should have been SS_DISABLE\n", stk.ss_flags);  		return EXIT_FAILURE;  	} @@ -133,7 +138,8 @@ int main(void)  	err = sigaltstack(&stk, NULL);  	if (err) {  		if (errno == EINVAL) { -			printf("[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n"); +			ksft_exit_skip( +				"[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n");  			/*  			 * If test cases for the !SS_AUTODISARM variant were  			 * added, we could still run them.  We don't have any @@ -142,7 +148,9 @@ int main(void)  			 */  			return 0;  		} else { -			perror("[FAIL]\tsigaltstack(SS_ONSTACK | SS_AUTODISARM)"); +			ksft_exit_fail_msg( +				"sigaltstack(SS_ONSTACK | SS_AUTODISARM)  %s\n", +					strerror(errno));  			return EXIT_FAILURE;  		}  	} @@ -150,7 +158,7 @@ int main(void)  	ustack = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE,  		      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);  	if (ustack == MAP_FAILED) { -		perror("mmap()"); +		ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));  		return EXIT_FAILURE;  	}  	getcontext(&uc); @@ -162,16 +170,17 @@ int main(void)  	err = sigaltstack(NULL, &stk);  	if (err) { -		perror("[FAIL]\tsigaltstack()"); +		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));  		exit(EXIT_FAILURE);  	}  	if (stk.ss_flags != SS_AUTODISARM) { -		printf("[FAIL]\tss_flags=%x, should be SS_AUTODISARM\n", +		ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n",  				stk.ss_flags);  		exit(EXIT_FAILURE);  	} -	printf("[OK]\tsigaltstack is still SS_AUTODISARM after signal\n"); +	ksft_test_result_pass( +			"sigaltstack is still SS_AUTODISARM after signal\n"); -	printf("[OK]\tTest passed\n"); +	ksft_exit_pass();  	return 0;  } diff --git a/tools/testing/selftests/splice/.gitignore b/tools/testing/selftests/splice/.gitignore new file mode 100644 index 000000000000..1e23fefd68e8 --- /dev/null +++ b/tools/testing/selftests/splice/.gitignore @@ -0,0 +1 @@ +default_file_splice_read diff --git a/tools/testing/selftests/splice/Makefile b/tools/testing/selftests/splice/Makefile index 9fc78e5e5451..7e1187e007fa 100644 --- a/tools/testing/selftests/splice/Makefile +++ b/tools/testing/selftests/splice/Makefile @@ -1,7 +1,4 @@  TEST_PROGS := default_file_splice_read.sh -EXTRA := default_file_splice_read -all: $(TEST_PROGS) $(EXTRA) +TEST_GEN_PROGS_EXTENDED := default_file_splice_read  include ../lib.mk - -EXTRA_CLEAN := $(EXTRA) diff --git a/tools/testing/selftests/sync/sync_test.c b/tools/testing/selftests/sync/sync_test.c index 62fa666e501a..7f7938263c5c 100644 --- a/tools/testing/selftests/sync/sync_test.c +++ b/tools/testing/selftests/sync/sync_test.c @@ -31,62 +31,83 @@  #include <sys/types.h>  #include <sys/stat.h>  #include <sys/wait.h> +#include <errno.h> +#include <string.h> +#include "../kselftest.h"  #include "synctest.h"  static int run_test(int (*test)(void), char *name)  {  	int result;  	pid_t childpid; +	int ret;  	fflush(stdout);  	childpid = fork();  	if (childpid) {  		waitpid(childpid, &result, 0); -		if (WIFEXITED(result)) -			return WEXITSTATUS(result); +		if (WIFEXITED(result)) { +			ret = WEXITSTATUS(result); +			if (!ret) +				ksft_test_result_pass("[RUN]\t%s\n", name); +			else +				ksft_test_result_fail("[RUN]\t%s\n", name); +			return ret; +		}  		return 1;  	} -	printf("[RUN]\tExecuting %s\n", name);  	exit(test());  } -static int sync_api_supported(void) +static void sync_api_supported(void)  {  	struct stat sbuf; +	int ret; -	return 0 == stat("/sys/kernel/debug/sync/sw_sync", &sbuf); +	ret = stat("/sys/kernel/debug/sync/sw_sync", &sbuf); +	if (!ret) +		return; + +	if (errno == ENOENT) +		ksft_exit_skip("Sync framework not supported by kernel\n"); + +	if (errno == EACCES) +		ksft_exit_skip("Run Sync test as root.\n"); + +	ksft_exit_fail_msg("stat failed on /sys/kernel/debug/sync/sw_sync: %s", +				strerror(errno));  }  int main(void)  { -	int err = 0; +	int err; -	if (!sync_api_supported()) { -		printf("SKIP: Sync framework not supported by kernel\n"); -		return 0; -	} +	ksft_print_header(); + +	sync_api_supported(); -	printf("[RUN]\tTesting sync framework\n"); +	ksft_print_msg("[RUN]\tTesting sync framework\n"); -	err += RUN_TEST(test_alloc_timeline); -	err += RUN_TEST(test_alloc_fence); -	err += RUN_TEST(test_alloc_fence_negative); +	RUN_TEST(test_alloc_timeline); +	RUN_TEST(test_alloc_fence); +	RUN_TEST(test_alloc_fence_negative); -	err += RUN_TEST(test_fence_one_timeline_wait); -	err += RUN_TEST(test_fence_one_timeline_merge); -	err += RUN_TEST(test_fence_merge_same_fence); -	err += RUN_TEST(test_fence_multi_timeline_wait); -	err += RUN_TEST(test_stress_two_threads_shared_timeline); -	err += RUN_TEST(test_consumer_stress_multi_producer_single_consumer); -	err += RUN_TEST(test_merge_stress_random_merge); +	RUN_TEST(test_fence_one_timeline_wait); +	RUN_TEST(test_fence_one_timeline_merge); +	RUN_TEST(test_fence_merge_same_fence); +	RUN_TEST(test_fence_multi_timeline_wait); +	RUN_TEST(test_stress_two_threads_shared_timeline); +	RUN_TEST(test_consumer_stress_multi_producer_single_consumer); +	RUN_TEST(test_merge_stress_random_merge); +	err = ksft_get_fail_cnt();  	if (err) -		printf("[FAIL]\tsync errors: %d\n", err); -	else -		printf("[OK]\tsync\n"); +		ksft_exit_fail_msg("%d out of %d sync tests failed\n", +					err, ksft_test_num()); -	return !!err; +	/* need this return to keep gcc happy */ +	return ksft_exit_pass();  } diff --git a/tools/testing/selftests/sync/synctest.h b/tools/testing/selftests/sync/synctest.h index e7d1d57dba7a..90a8e5369914 100644 --- a/tools/testing/selftests/sync/synctest.h +++ b/tools/testing/selftests/sync/synctest.h @@ -29,10 +29,11 @@  #define SELFTESTS_SYNCTEST_H  #include <stdio.h> +#include "../kselftest.h"  #define ASSERT(cond, msg) do { \  	if (!(cond)) { \ -		printf("[ERROR]\t%s", (msg)); \ +		ksft_print_msg("[ERROR]\t%s", (msg)); \  		return 1; \  	} \  } while (0) diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh index ec232c3cfcaa..ec232c3cfcaa 100644..100755 --- a/tools/testing/selftests/sysctl/sysctl.sh +++ b/tools/testing/selftests/sysctl/sysctl.sh diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json b/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json index af519bc97a8e..6973bdc5b5bf 100644 --- a/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json @@ -1111,5 +1111,55 @@          "teardown": [              "$TC actions flush action gact"          ] +    }, +    { +        "id": "a568", +        "name": "Add action with ife type", +        "category": [ +            "actions", +            "ife" +        ], +        "setup": [ +            [ +                "$TC actions flush action ife", +                0, +                1, +                255 +            ], +            "$TC actions add action ife encode type 0xDEAD index 1" +        ], +        "cmdUnderTest": "$TC actions get action ife index 1", +        "expExitCode": "0", +        "verifyCmd": "$TC actions get action ife index 1", +        "matchPattern": "type 0xDEAD", +        "matchCount": "1", +        "teardown": [ +            "$TC actions flush action ife" +        ] +    }, +    { +        "id": "b983", +        "name": "Add action without ife type", +        "category": [ +            "actions", +            "ife" +        ], +        "setup": [ +            [ +                "$TC actions flush action ife", +                0, +                1, +                255 +            ], +            "$TC actions add action ife encode index 1" +        ], +        "cmdUnderTest": "$TC actions get action ife index 1", +        "expExitCode": "0", +        "verifyCmd": "$TC actions get action ife index 1", +        "matchPattern": "type 0xED3E", +        "matchCount": "1", +        "teardown": [ +            "$TC actions flush action ife" +        ]      }  ]
\ No newline at end of file diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile index a9b86133b9b3..ae4593115408 100644 --- a/tools/testing/selftests/timers/Makefile +++ b/tools/testing/selftests/timers/Makefile @@ -1,5 +1,4 @@ -BUILD_FLAGS = -DKTEST -CFLAGS += -O3 -Wl,-no-as-needed -Wall $(BUILD_FLAGS) +CFLAGS += -O3 -Wl,-no-as-needed -Wall  LDFLAGS += -lrt -lpthread -lm  # these are all "safe" tests that don't modify @@ -7,9 +6,11 @@ LDFLAGS += -lrt -lpthread -lm  TEST_GEN_PROGS = posix_timers nanosleep nsleep-lat set-timer-lat mqueue-lat \  	     inconsistency-check raw_skew threadtest rtctest -TEST_GEN_PROGS_EXTENDED = alarmtimer-suspend valid-adjtimex adjtick change_skew \ +DESTRUCTIVE_TESTS = alarmtimer-suspend valid-adjtimex adjtick change_skew \  		      skew_consistency clocksource-switch freq-step leap-a-day \ -		      leapcrash set-tai set-2038 set-tz rtctest_setdate +		      leapcrash set-tai set-2038 set-tz + +TEST_GEN_PROGS_EXTENDED = $(DESTRUCTIVE_TESTS) rtctest_setdate  include ../lib.mk @@ -18,16 +19,4 @@ include ../lib.mk  # and may modify the system time or trigger  # other behavior like suspend  run_destructive_tests: run_tests -	./alarmtimer-suspend -	./valid-adjtimex -	./adjtick -	./change_skew -	./skew_consistency -	./clocksource-switch -	./freq-step -	./leap-a-day -s -i 10 -	./leapcrash -	./set-tz -	./set-tai -	./set-2038 - +	$(call RUN_TESTS, $(DESTRUCTIVE_TESTS)) diff --git a/tools/testing/selftests/timers/adjtick.c b/tools/testing/selftests/timers/adjtick.c index 9887fd538fec..0caca3a06bd2 100644 --- a/tools/testing/selftests/timers/adjtick.c +++ b/tools/testing/selftests/timers/adjtick.c @@ -23,18 +23,7 @@  #include <sys/timex.h>  #include <time.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define CLOCK_MONOTONIC_RAW	4 diff --git a/tools/testing/selftests/timers/alarmtimer-suspend.c b/tools/testing/selftests/timers/alarmtimer-suspend.c index 2b361b830395..4da09dbf83ba 100644 --- a/tools/testing/selftests/timers/alarmtimer-suspend.c +++ b/tools/testing/selftests/timers/alarmtimer-suspend.c @@ -28,18 +28,7 @@  #include <signal.h>  #include <stdlib.h>  #include <pthread.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define CLOCK_REALTIME			0  #define CLOCK_MONOTONIC			1 diff --git a/tools/testing/selftests/timers/change_skew.c b/tools/testing/selftests/timers/change_skew.c index cb1968977c04..c4eab7124990 100644 --- a/tools/testing/selftests/timers/change_skew.c +++ b/tools/testing/selftests/timers/change_skew.c @@ -28,18 +28,7 @@  #include <sys/time.h>  #include <sys/timex.h>  #include <time.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000LL diff --git a/tools/testing/selftests/timers/clocksource-switch.c b/tools/testing/selftests/timers/clocksource-switch.c index 5ff165373f8b..bfc974b4572d 100644 --- a/tools/testing/selftests/timers/clocksource-switch.c +++ b/tools/testing/selftests/timers/clocksource-switch.c @@ -34,18 +34,7 @@  #include <fcntl.h>  #include <string.h>  #include <sys/wait.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  int get_clocksources(char list[][30]) @@ -61,7 +50,7 @@ int get_clocksources(char list[][30])  	close(fd); -	for (i = 0; i < 30; i++) +	for (i = 0; i < 10; i++)  		list[i][0] = '\0';  	head = buf; diff --git a/tools/testing/selftests/timers/freq-step.c b/tools/testing/selftests/timers/freq-step.c index e8c61830825a..14a2b77fd012 100644 --- a/tools/testing/selftests/timers/freq-step.c +++ b/tools/testing/selftests/timers/freq-step.c @@ -33,6 +33,10 @@  #define MAX_FREQ_ERROR 10e-6  #define MAX_STDDEV 1000e-9 +#ifndef ADJ_SETOFFSET +  #define ADJ_SETOFFSET 0x0100 +#endif +  struct sample {  	double offset;  	double time; @@ -229,10 +233,9 @@ static void init_test(void)  	printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t",  	       1e9 * precision); -	if (precision > MAX_PRECISION) { -		printf("[SKIP]\n"); -		ksft_exit_skip(); -	} +	if (precision > MAX_PRECISION) +		ksft_exit_skip("precision: %.0f ns > MAX_PRECISION: %.0f ns\n", +				1e9 * precision, 1e9 * MAX_PRECISION);  	printf("[OK]\n");  	srand(ts.tv_sec ^ ts.tv_nsec); @@ -262,7 +265,7 @@ int main(int argc, char **argv)  	set_frequency(0.0);  	if (fails) -		ksft_exit_fail(); +		return ksft_exit_fail(); -	ksft_exit_pass(); +	return ksft_exit_pass();  } diff --git a/tools/testing/selftests/timers/inconsistency-check.c b/tools/testing/selftests/timers/inconsistency-check.c index 74c60e8759a0..022d3ffe3fbf 100644 --- a/tools/testing/selftests/timers/inconsistency-check.c +++ b/tools/testing/selftests/timers/inconsistency-check.c @@ -28,18 +28,7 @@  #include <sys/timex.h>  #include <string.h>  #include <signal.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define CALLS_PER_LOOP 64  #define NSEC_PER_SEC 1000000000ULL diff --git a/tools/testing/selftests/timers/leap-a-day.c b/tools/testing/selftests/timers/leap-a-day.c index fb46ad6ac92c..19e46ed5dfb5 100644 --- a/tools/testing/selftests/timers/leap-a-day.c +++ b/tools/testing/selftests/timers/leap-a-day.c @@ -48,18 +48,7 @@  #include <string.h>  #include <signal.h>  #include <unistd.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000ULL  #define CLOCK_TAI 11 @@ -190,18 +179,18 @@ int main(int argc, char **argv)  	struct sigevent se;  	struct sigaction act;  	int signum = SIGRTMAX; -	int settime = 0; +	int settime = 1;  	int tai_time = 0;  	int insert = 1; -	int iterations = -1; +	int iterations = 10;  	int opt;  	/* Process arguments */  	while ((opt = getopt(argc, argv, "sti:")) != -1) {  		switch (opt) { -		case 's': -			printf("Setting time to speed up testing\n"); -			settime = 1; +		case 'w': +			printf("Only setting leap-flag, not changing time. It could take up to a day for leap to trigger.\n"); +			settime = 0;  			break;  		case 'i':  			iterations = atoi(optarg); @@ -210,9 +199,10 @@ int main(int argc, char **argv)  			tai_time = 1;  			break;  		default: -			printf("Usage: %s [-s] [-i <iterations>]\n", argv[0]); -			printf("	-s: Set time to right before leap second each iteration\n"); -			printf("	-i: Number of iterations\n"); +			printf("Usage: %s [-w] [-i <iterations>]\n", argv[0]); +			printf("	-w: Set flag and wait for leap second each iteration"); +			printf("	    (default sets time to right before leapsecond)\n"); +			printf("	-i: Number of iterations (-1 = infinite, default is 10)\n");  			printf("	-t: Print TAI time\n");  			exit(-1);  		} diff --git a/tools/testing/selftests/timers/leapcrash.c b/tools/testing/selftests/timers/leapcrash.c index a1071bdbdeb7..830c462f605d 100644 --- a/tools/testing/selftests/timers/leapcrash.c +++ b/tools/testing/selftests/timers/leapcrash.c @@ -22,20 +22,7 @@  #include <sys/timex.h>  #include <string.h>  #include <signal.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif - -  /* clear NTP time_status & time_state */  int clear_time_state(void) diff --git a/tools/testing/selftests/timers/mqueue-lat.c b/tools/testing/selftests/timers/mqueue-lat.c index a2a3924d0b41..1867db5d6f5e 100644 --- a/tools/testing/selftests/timers/mqueue-lat.c +++ b/tools/testing/selftests/timers/mqueue-lat.c @@ -29,18 +29,7 @@  #include <signal.h>  #include <errno.h>  #include <mqueue.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000ULL diff --git a/tools/testing/selftests/timers/nanosleep.c b/tools/testing/selftests/timers/nanosleep.c index ff942ff7c9b3..8adb0bb51d4d 100644 --- a/tools/testing/selftests/timers/nanosleep.c +++ b/tools/testing/selftests/timers/nanosleep.c @@ -27,18 +27,7 @@  #include <sys/timex.h>  #include <string.h>  #include <signal.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000ULL diff --git a/tools/testing/selftests/timers/nsleep-lat.c b/tools/testing/selftests/timers/nsleep-lat.c index 2d7898fda0f1..c3c3dc10db17 100644 --- a/tools/testing/selftests/timers/nsleep-lat.c +++ b/tools/testing/selftests/timers/nsleep-lat.c @@ -24,18 +24,7 @@  #include <sys/timex.h>  #include <string.h>  #include <signal.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000ULL diff --git a/tools/testing/selftests/timers/raw_skew.c b/tools/testing/selftests/timers/raw_skew.c index 30906bfd9c1b..ca6cd146aafe 100644 --- a/tools/testing/selftests/timers/raw_skew.c +++ b/tools/testing/selftests/timers/raw_skew.c @@ -25,19 +25,7 @@  #include <sys/time.h>  #include <sys/timex.h>  #include <time.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif -  #define CLOCK_MONOTONIC_RAW		4  #define NSEC_PER_SEC 1000000000LL diff --git a/tools/testing/selftests/timers/rtctest.c b/tools/testing/selftests/timers/rtctest.c index f61170f7b024..411eff625e66 100644 --- a/tools/testing/selftests/timers/rtctest.c +++ b/tools/testing/selftests/timers/rtctest.c @@ -221,6 +221,11 @@ test_READ:  	/* Read the current alarm settings */  	retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);  	if (retval == -1) { +		if (errno == EINVAL) { +			fprintf(stderr, +					"\n...EINVAL reading current alarm setting.\n"); +			goto test_PIE; +		}  		perror("RTC_ALM_READ ioctl");  		exit(errno);  	} @@ -231,7 +236,7 @@ test_READ:  	/* Enable alarm interrupts */  	retval = ioctl(fd, RTC_AIE_ON, 0);  	if (retval == -1) { -		if (errno == EINVAL) { +		if (errno == EINVAL || errno == EIO) {  			fprintf(stderr,  				"\n...Alarm IRQs not supported.\n");  			goto test_PIE; diff --git a/tools/testing/selftests/timers/set-2038.c b/tools/testing/selftests/timers/set-2038.c index c8a7e14446b1..688cfd81b531 100644 --- a/tools/testing/selftests/timers/set-2038.c +++ b/tools/testing/selftests/timers/set-2038.c @@ -27,18 +27,7 @@  #include <unistd.h>  #include <time.h>  #include <sys/time.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000LL diff --git a/tools/testing/selftests/timers/set-tai.c b/tools/testing/selftests/timers/set-tai.c index dc88dbc8831f..70fed27d8fd3 100644 --- a/tools/testing/selftests/timers/set-tai.c +++ b/tools/testing/selftests/timers/set-tai.c @@ -23,18 +23,7 @@  #include <string.h>  #include <signal.h>  #include <unistd.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  int set_tai(int offset)  { diff --git a/tools/testing/selftests/timers/set-timer-lat.c b/tools/testing/selftests/timers/set-timer-lat.c index 4fc98c5b0899..9c92b7bd5641 100644 --- a/tools/testing/selftests/timers/set-timer-lat.c +++ b/tools/testing/selftests/timers/set-timer-lat.c @@ -20,6 +20,7 @@   */ +#include <errno.h>  #include <stdio.h>  #include <unistd.h>  #include <time.h> @@ -27,18 +28,7 @@  #include <signal.h>  #include <stdlib.h>  #include <pthread.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define CLOCK_REALTIME			0  #define CLOCK_MONOTONIC			1 @@ -63,6 +53,7 @@ int alarmcount;  int clock_id;  struct timespec start_time;  long long max_latency_ns; +int timer_fired_early;  char *clockstring(int clockid)  { @@ -115,16 +106,23 @@ void sigalarm(int signo)  	delta_ns -= NSEC_PER_SEC * TIMER_SECS * alarmcount;  	if (delta_ns < 0) -		printf("%s timer fired early: FAIL\n", clockstring(clock_id)); +		timer_fired_early = 1;  	if (delta_ns > max_latency_ns)  		max_latency_ns = delta_ns;  } -int do_timer(int clock_id, int flags) +void describe_timer(int flags, int interval) +{ +	printf("%-22s %s %s ", +			clockstring(clock_id), +			flags ? "ABSTIME":"RELTIME", +			interval ? "PERIODIC":"ONE-SHOT"); +} + +int setup_timer(int clock_id, int flags, int interval, timer_t *tm1)  {  	struct sigevent se; -	timer_t tm1;  	struct itimerspec its1, its2;  	int err; @@ -136,8 +134,9 @@ int do_timer(int clock_id, int flags)  	max_latency_ns = 0;  	alarmcount = 0; +	timer_fired_early = 0; -	err = timer_create(clock_id, &se, &tm1); +	err = timer_create(clock_id, &se, tm1);  	if (err) {  		if ((clock_id == CLOCK_REALTIME_ALARM) ||  		    (clock_id == CLOCK_BOOTTIME_ALARM)) { @@ -158,32 +157,97 @@ int do_timer(int clock_id, int flags)  		its1.it_value.tv_sec = TIMER_SECS;  		its1.it_value.tv_nsec = 0;  	} -	its1.it_interval.tv_sec = TIMER_SECS; +	its1.it_interval.tv_sec = interval;  	its1.it_interval.tv_nsec = 0; -	err = timer_settime(tm1, flags, &its1, &its2); +	err = timer_settime(*tm1, flags, &its1, &its2);  	if (err) {  		printf("%s - timer_settime() failed\n", clockstring(clock_id));  		return -1;  	} -	while (alarmcount < 5) -		sleep(1); +	return 0; +} -	printf("%-22s %s max latency: %10lld ns : ", -			clockstring(clock_id), -			flags ? "ABSTIME":"RELTIME", -			max_latency_ns); +int check_timer_latency(int flags, int interval) +{ +	int err = 0; + +	describe_timer(flags, interval); +	printf("timer fired early: %7d : ", timer_fired_early); +	if (!timer_fired_early) { +		printf("[OK]\n"); +	} else { +		printf("[FAILED]\n"); +		err = -1; +	} + +	describe_timer(flags, interval); +	printf("max latency: %10lld ns : ", max_latency_ns); -	timer_delete(tm1);  	if (max_latency_ns < UNRESONABLE_LATENCY) {  		printf("[OK]\n"); +	} else { +		printf("[FAILED]\n"); +		err = -1; +	} +	return err; +} + +int check_alarmcount(int flags, int interval) +{ +	describe_timer(flags, interval); +	printf("count: %19d : ", alarmcount); +	if (alarmcount == 1) { +		printf("[OK]\n");  		return 0;  	}  	printf("[FAILED]\n");  	return -1;  } +int do_timer(int clock_id, int flags) +{ +	timer_t tm1; +	const int interval = TIMER_SECS; +	int err; + +	err = setup_timer(clock_id, flags, interval, &tm1); +	if (err) +		return err; + +	while (alarmcount < 5) +		sleep(1); + +	timer_delete(tm1); +	return check_timer_latency(flags, interval); +} + +int do_timer_oneshot(int clock_id, int flags) +{ +	timer_t tm1; +	const int interval = 0; +	struct timeval timeout; +	fd_set fds; +	int err; + +	err = setup_timer(clock_id, flags, interval, &tm1); +	if (err) +		return err; + +	memset(&timeout, 0, sizeof(timeout)); +	timeout.tv_sec = 5; +	FD_ZERO(&fds); +	do { +		err = select(FD_SETSIZE, &fds, NULL, NULL, &timeout); +	} while (err == -1 && errno == EINTR); + +	timer_delete(tm1); +	err = check_timer_latency(flags, interval); +	err |= check_alarmcount(flags, interval); +	return err; +} +  int main(void)  {  	struct sigaction act; @@ -209,6 +273,8 @@ int main(void)  		ret |= do_timer(clock_id, TIMER_ABSTIME);  		ret |= do_timer(clock_id, 0); +		ret |= do_timer_oneshot(clock_id, TIMER_ABSTIME); +		ret |= do_timer_oneshot(clock_id, 0);  	}  	if (ret)  		return ksft_exit_fail(); diff --git a/tools/testing/selftests/timers/set-tz.c b/tools/testing/selftests/timers/set-tz.c index f4184928b16b..877fd5532fee 100644 --- a/tools/testing/selftests/timers/set-tz.c +++ b/tools/testing/selftests/timers/set-tz.c @@ -23,18 +23,7 @@  #include <string.h>  #include <signal.h>  #include <unistd.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  int set_tz(int min, int dst)  { diff --git a/tools/testing/selftests/timers/skew_consistency.c b/tools/testing/selftests/timers/skew_consistency.c index 2a996e072259..022b711c78ee 100644 --- a/tools/testing/selftests/timers/skew_consistency.c +++ b/tools/testing/selftests/timers/skew_consistency.c @@ -35,18 +35,7 @@  #include <stdlib.h>  #include <string.h>  #include <sys/wait.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000LL diff --git a/tools/testing/selftests/timers/threadtest.c b/tools/testing/selftests/timers/threadtest.c index e632e116f05e..759c9c06f1a0 100644 --- a/tools/testing/selftests/timers/threadtest.c +++ b/tools/testing/selftests/timers/threadtest.c @@ -21,19 +21,7 @@  #include <stdlib.h>  #include <sys/time.h>  #include <pthread.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif -  /* serializes shared list access */  pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER; diff --git a/tools/testing/selftests/timers/valid-adjtimex.c b/tools/testing/selftests/timers/valid-adjtimex.c index 60fe3c569bd9..d9d3ab93b31a 100644 --- a/tools/testing/selftests/timers/valid-adjtimex.c +++ b/tools/testing/selftests/timers/valid-adjtimex.c @@ -32,18 +32,7 @@  #include <string.h>  #include <signal.h>  #include <unistd.h> -#ifdef KTEST  #include "../kselftest.h" -#else -static inline int ksft_exit_pass(void) -{ -	exit(0); -} -static inline int ksft_exit_fail(void) -{ -	exit(1); -} -#endif  #define NSEC_PER_SEC 1000000000LL  #define USEC_PER_SEC 1000000LL diff --git a/tools/testing/selftests/vm/userfaultfd.c b/tools/testing/selftests/vm/userfaultfd.c index 1eae79ae5b4e..a2c53a3d223d 100644 --- a/tools/testing/selftests/vm/userfaultfd.c +++ b/tools/testing/selftests/vm/userfaultfd.c @@ -66,6 +66,8 @@  #include <sys/wait.h>  #include <pthread.h>  #include <linux/userfaultfd.h> +#include <setjmp.h> +#include <stdbool.h>  #ifdef __NR_userfaultfd @@ -82,11 +84,17 @@ static int bounces;  #define TEST_SHMEM	3  static int test_type; +/* exercise the test_uffdio_*_eexist every ALARM_INTERVAL_SECS */ +#define ALARM_INTERVAL_SECS 10 +static volatile bool test_uffdio_copy_eexist = true; +static volatile bool test_uffdio_zeropage_eexist = true; + +static bool map_shared;  static int huge_fd;  static char *huge_fd_off0;  static unsigned long long *count_verify;  static int uffd, uffd_flags, finished, *pipefd; -static char *area_src, *area_dst; +static char *area_src, *area_src_alias, *area_dst, *area_dst_alias;  static char *zeropage;  pthread_attr_t attr; @@ -125,6 +133,9 @@ static void anon_allocate_area(void **alloc_area)  	}  } +static void noop_alias_mapping(__u64 *start, size_t len, unsigned long offset) +{ +}  /* HugeTLB memory */  static int hugetlb_release_pages(char *rel_area) @@ -145,17 +156,51 @@ static int hugetlb_release_pages(char *rel_area)  static void hugetlb_allocate_area(void **alloc_area)  { +	void *area_alias = NULL; +	char **alloc_area_alias;  	*alloc_area = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, -				MAP_PRIVATE | MAP_HUGETLB, huge_fd, -				*alloc_area == area_src ? 0 : -				nr_pages * page_size); +			   (map_shared ? MAP_SHARED : MAP_PRIVATE) | +			   MAP_HUGETLB, +			   huge_fd, *alloc_area == area_src ? 0 : +			   nr_pages * page_size);  	if (*alloc_area == MAP_FAILED) {  		fprintf(stderr, "mmap of hugetlbfs file failed\n");  		*alloc_area = NULL;  	} -	if (*alloc_area == area_src) +	if (map_shared) { +		area_alias = mmap(NULL, nr_pages * page_size, PROT_READ | PROT_WRITE, +				  MAP_SHARED | MAP_HUGETLB, +				  huge_fd, *alloc_area == area_src ? 0 : +				  nr_pages * page_size); +		if (area_alias == MAP_FAILED) { +			if (munmap(*alloc_area, nr_pages * page_size) < 0) +				perror("hugetlb munmap"), exit(1); +			*alloc_area = NULL; +			return; +		} +	} +	if (*alloc_area == area_src) {  		huge_fd_off0 = *alloc_area; +		alloc_area_alias = &area_src_alias; +	} else { +		alloc_area_alias = &area_dst_alias; +	} +	if (area_alias) +		*alloc_area_alias = area_alias; +} + +static void hugetlb_alias_mapping(__u64 *start, size_t len, unsigned long offset) +{ +	if (!map_shared) +		return; +	/* +	 * We can't zap just the pagetable with hugetlbfs because +	 * MADV_DONTEED won't work. So exercise -EEXIST on a alias +	 * mapping where the pagetables are not established initially, +	 * this way we'll exercise the -EEXEC at the fs level. +	 */ +	*start = (unsigned long) area_dst_alias + offset;  }  /* Shared memory */ @@ -185,6 +230,7 @@ struct uffd_test_ops {  	unsigned long expected_ioctls;  	void (*allocate_area)(void **alloc_area);  	int (*release_pages)(char *rel_area); +	void (*alias_mapping)(__u64 *start, size_t len, unsigned long offset);  };  #define ANON_EXPECTED_IOCTLS		((1 << _UFFDIO_WAKE) | \ @@ -195,18 +241,21 @@ static struct uffd_test_ops anon_uffd_test_ops = {  	.expected_ioctls = ANON_EXPECTED_IOCTLS,  	.allocate_area	= anon_allocate_area,  	.release_pages	= anon_release_pages, +	.alias_mapping = noop_alias_mapping,  };  static struct uffd_test_ops shmem_uffd_test_ops = { -	.expected_ioctls = UFFD_API_RANGE_IOCTLS_BASIC, +	.expected_ioctls = ANON_EXPECTED_IOCTLS,  	.allocate_area	= shmem_allocate_area,  	.release_pages	= shmem_release_pages, +	.alias_mapping = noop_alias_mapping,  };  static struct uffd_test_ops hugetlb_uffd_test_ops = {  	.expected_ioctls = UFFD_API_RANGE_IOCTLS_BASIC,  	.allocate_area	= hugetlb_allocate_area,  	.release_pages	= hugetlb_release_pages, +	.alias_mapping = hugetlb_alias_mapping,  };  static struct uffd_test_ops *uffd_test_ops; @@ -331,6 +380,23 @@ static void *locking_thread(void *arg)  	return NULL;  } +static void retry_copy_page(int ufd, struct uffdio_copy *uffdio_copy, +			    unsigned long offset) +{ +	uffd_test_ops->alias_mapping(&uffdio_copy->dst, +				     uffdio_copy->len, +				     offset); +	if (ioctl(ufd, UFFDIO_COPY, uffdio_copy)) { +		/* real retval in ufdio_copy.copy */ +		if (uffdio_copy->copy != -EEXIST) +			fprintf(stderr, "UFFDIO_COPY retry error %Ld\n", +				uffdio_copy->copy), exit(1); +	} else { +		fprintf(stderr,	"UFFDIO_COPY retry unexpected %Ld\n", +			uffdio_copy->copy), exit(1); +	} +} +  static int copy_page(int ufd, unsigned long offset)  {  	struct uffdio_copy uffdio_copy; @@ -351,8 +417,13 @@ static int copy_page(int ufd, unsigned long offset)  	} else if (uffdio_copy.copy != page_size) {  		fprintf(stderr, "UFFDIO_COPY unexpected copy %Ld\n",  			uffdio_copy.copy), exit(1); -	} else +	} else { +		if (test_uffdio_copy_eexist) { +			test_uffdio_copy_eexist = false; +			retry_copy_page(ufd, &uffdio_copy, offset); +		}  		return 1; +	}  	return 0;  } @@ -408,6 +479,7 @@ static void *uffd_poll_thread(void *arg)  				userfaults++;  			break;  		case UFFD_EVENT_FORK: +			close(uffd);  			uffd = msg.arg.fork.ufd;  			pollfd[0].fd = uffd;  			break; @@ -572,6 +644,17 @@ static int userfaultfd_open(int features)  	return 0;  } +sigjmp_buf jbuf, *sigbuf; + +static void sighndl(int sig, siginfo_t *siginfo, void *ptr) +{ +	if (sig == SIGBUS) { +		if (sigbuf) +			siglongjmp(*sigbuf, 1); +		abort(); +	} +} +  /*   * For non-cooperative userfaultfd test we fork() a process that will   * generate pagefaults, will mremap the area monitored by the @@ -585,19 +668,59 @@ static int userfaultfd_open(int features)   * The release of the pages currently generates event for shmem and   * anonymous memory (UFFD_EVENT_REMOVE), hence it is not checked   * for hugetlb. + * For signal test(UFFD_FEATURE_SIGBUS), signal_test = 1, we register + * monitored area, generate pagefaults and test that signal is delivered. + * Use UFFDIO_COPY to allocate missing page and retry. For signal_test = 2 + * test robustness use case - we release monitored area, fork a process + * that will generate pagefaults and verify signal is generated. + * This also tests UFFD_FEATURE_EVENT_FORK event along with the signal + * feature. Using monitor thread, verify no userfault events are generated.   */ -static int faulting_process(void) +static int faulting_process(int signal_test)  {  	unsigned long nr;  	unsigned long long count;  	unsigned long split_nr_pages; +	unsigned long lastnr; +	struct sigaction act; +	unsigned long signalled = 0;  	if (test_type != TEST_HUGETLB)  		split_nr_pages = (nr_pages + 1) / 2;  	else  		split_nr_pages = nr_pages; +	if (signal_test) { +		sigbuf = &jbuf; +		memset(&act, 0, sizeof(act)); +		act.sa_sigaction = sighndl; +		act.sa_flags = SA_SIGINFO; +		if (sigaction(SIGBUS, &act, 0)) { +			perror("sigaction"); +			return 1; +		} +		lastnr = (unsigned long)-1; +	} +  	for (nr = 0; nr < split_nr_pages; nr++) { +		if (signal_test) { +			if (sigsetjmp(*sigbuf, 1) != 0) { +				if (nr == lastnr) { +					fprintf(stderr, "Signal repeated\n"); +					return 1; +				} + +				lastnr = nr; +				if (signal_test == 1) { +					if (copy_page(uffd, nr * page_size)) +						signalled++; +				} else { +					signalled++; +					continue; +				} +			} +		} +  		count = *area_count(area_dst, nr);  		if (count != count_verify[nr]) {  			fprintf(stderr, @@ -607,6 +730,9 @@ static int faulting_process(void)  		}  	} +	if (signal_test) +		return signalled != split_nr_pages; +  	if (test_type == TEST_HUGETLB)  		return 0; @@ -636,6 +762,23 @@ static int faulting_process(void)  	return 0;  } +static void retry_uffdio_zeropage(int ufd, +				  struct uffdio_zeropage *uffdio_zeropage, +				  unsigned long offset) +{ +	uffd_test_ops->alias_mapping(&uffdio_zeropage->range.start, +				     uffdio_zeropage->range.len, +				     offset); +	if (ioctl(ufd, UFFDIO_ZEROPAGE, uffdio_zeropage)) { +		if (uffdio_zeropage->zeropage != -EEXIST) +			fprintf(stderr, "UFFDIO_ZEROPAGE retry error %Ld\n", +				uffdio_zeropage->zeropage), exit(1); +	} else { +		fprintf(stderr, "UFFDIO_ZEROPAGE retry unexpected %Ld\n", +			uffdio_zeropage->zeropage), exit(1); +	} +} +  static int uffdio_zeropage(int ufd, unsigned long offset)  {  	struct uffdio_zeropage uffdio_zeropage; @@ -670,8 +813,14 @@ static int uffdio_zeropage(int ufd, unsigned long offset)  		if (uffdio_zeropage.zeropage != page_size) {  			fprintf(stderr, "UFFDIO_ZEROPAGE unexpected %Ld\n",  				uffdio_zeropage.zeropage), exit(1); -		} else +		} else { +			if (test_uffdio_zeropage_eexist) { +				test_uffdio_zeropage_eexist = false; +				retry_uffdio_zeropage(ufd, &uffdio_zeropage, +						      offset); +			}  			return 1; +		}  	} else {  		fprintf(stderr,  			"UFFDIO_ZEROPAGE succeeded %Ld\n", @@ -761,7 +910,7 @@ static int userfaultfd_events_test(void)  		perror("fork"), exit(1);  	if (!pid) -		return faulting_process(); +		return faulting_process(0);  	waitpid(pid, &err, 0);  	if (err) @@ -778,6 +927,72 @@ static int userfaultfd_events_test(void)  	return userfaults != nr_pages;  } +static int userfaultfd_sig_test(void) +{ +	struct uffdio_register uffdio_register; +	unsigned long expected_ioctls; +	unsigned long userfaults; +	pthread_t uffd_mon; +	int err, features; +	pid_t pid; +	char c; + +	printf("testing signal delivery: "); +	fflush(stdout); + +	if (uffd_test_ops->release_pages(area_dst)) +		return 1; + +	features = UFFD_FEATURE_EVENT_FORK|UFFD_FEATURE_SIGBUS; +	if (userfaultfd_open(features) < 0) +		return 1; +	fcntl(uffd, F_SETFL, uffd_flags | O_NONBLOCK); + +	uffdio_register.range.start = (unsigned long) area_dst; +	uffdio_register.range.len = nr_pages * page_size; +	uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; +	if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) +		fprintf(stderr, "register failure\n"), exit(1); + +	expected_ioctls = uffd_test_ops->expected_ioctls; +	if ((uffdio_register.ioctls & expected_ioctls) != +	    expected_ioctls) +		fprintf(stderr, +			"unexpected missing ioctl for anon memory\n"), +			exit(1); + +	if (faulting_process(1)) +		fprintf(stderr, "faulting process failed\n"), exit(1); + +	if (uffd_test_ops->release_pages(area_dst)) +		return 1; + +	if (pthread_create(&uffd_mon, &attr, uffd_poll_thread, NULL)) +		perror("uffd_poll_thread create"), exit(1); + +	pid = fork(); +	if (pid < 0) +		perror("fork"), exit(1); + +	if (!pid) +		exit(faulting_process(2)); + +	waitpid(pid, &err, 0); +	if (err) +		fprintf(stderr, "faulting process failed\n"), exit(1); + +	if (write(pipefd[1], &c, sizeof(c)) != sizeof(c)) +		perror("pipe write"), exit(1); +	if (pthread_join(uffd_mon, (void **)&userfaults)) +		return 1; + +	printf("done.\n"); +	if (userfaults) +		fprintf(stderr, "Signal test failed, userfaults: %ld\n", +			userfaults); +	close(uffd); +	return userfaults != 0; +}  static int userfaultfd_stress(void)  {  	void *area; @@ -879,6 +1094,15 @@ static int userfaultfd_stress(void)  			return 1;  		} +		if (area_dst_alias) { +			uffdio_register.range.start = (unsigned long) +				area_dst_alias; +			if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) { +				fprintf(stderr, "register failure alias\n"); +				return 1; +			} +		} +  		/*  		 * The madvise done previously isn't enough: some  		 * uffd_thread could have read userfaults (one of @@ -912,9 +1136,17 @@ static int userfaultfd_stress(void)  		/* unregister */  		if (ioctl(uffd, UFFDIO_UNREGISTER, &uffdio_register.range)) { -			fprintf(stderr, "register failure\n"); +			fprintf(stderr, "unregister failure\n");  			return 1;  		} +		if (area_dst_alias) { +			uffdio_register.range.start = (unsigned long) area_dst; +			if (ioctl(uffd, UFFDIO_UNREGISTER, +				  &uffdio_register.range)) { +				fprintf(stderr, "unregister failure alias\n"); +				return 1; +			} +		}  		/* verification */  		if (bounces & BOUNCE_VERIFY) { @@ -936,6 +1168,10 @@ static int userfaultfd_stress(void)  		area_src = area_dst;  		area_dst = tmp_area; +		tmp_area = area_src_alias; +		area_src_alias = area_dst_alias; +		area_dst_alias = tmp_area; +  		printf("userfaults:");  		for (cpu = 0; cpu < nr_cpus; cpu++)  			printf(" %lu", userfaults[cpu]); @@ -946,7 +1182,8 @@ static int userfaultfd_stress(void)  		return err;  	close(uffd); -	return userfaultfd_zeropage_test() || userfaultfd_events_test(); +	return userfaultfd_zeropage_test() || userfaultfd_sig_test() +		|| userfaultfd_events_test();  }  /* @@ -981,7 +1218,12 @@ static void set_test_type(const char *type)  	} else if (!strcmp(type, "hugetlb")) {  		test_type = TEST_HUGETLB;  		uffd_test_ops = &hugetlb_uffd_test_ops; +	} else if (!strcmp(type, "hugetlb_shared")) { +		map_shared = true; +		test_type = TEST_HUGETLB; +		uffd_test_ops = &hugetlb_uffd_test_ops;  	} else if (!strcmp(type, "shmem")) { +		map_shared = true;  		test_type = TEST_SHMEM;  		uffd_test_ops = &shmem_uffd_test_ops;  	} else { @@ -1001,12 +1243,25 @@ static void set_test_type(const char *type)  		fprintf(stderr, "Impossible to run this test\n"), exit(2);  } +static void sigalrm(int sig) +{ +	if (sig != SIGALRM) +		abort(); +	test_uffdio_copy_eexist = true; +	test_uffdio_zeropage_eexist = true; +	alarm(ALARM_INTERVAL_SECS); +} +  int main(int argc, char **argv)  {  	if (argc < 4)  		fprintf(stderr, "Usage: <test type> <MiB> <bounces> [hugetlbfs_file]\n"),  				exit(1); +	if (signal(SIGALRM, sigalrm) == SIG_ERR) +		fprintf(stderr, "failed to arm SIGALRM"), exit(1); +	alarm(ALARM_INTERVAL_SECS); +  	set_test_type(argv[1]);  	nr_cpus = sysconf(_SC_NPROCESSORS_ONLN); diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c index a74c9d739d07..a1391be2dc1e 100644 --- a/tools/testing/selftests/watchdog/watchdog-test.c +++ b/tools/testing/selftests/watchdog/watchdog-test.c @@ -9,12 +9,25 @@  #include <unistd.h>  #include <fcntl.h>  #include <signal.h> +#include <getopt.h>  #include <sys/ioctl.h>  #include <linux/types.h>  #include <linux/watchdog.h> +#define DEFAULT_PING_RATE	1 +  int fd;  const char v = 'V'; +static const char sopts[] = "bdehp:t:"; +static const struct option lopts[] = { +	{"bootstatus",          no_argument, NULL, 'b'}, +	{"disable",             no_argument, NULL, 'd'}, +	{"enable",              no_argument, NULL, 'e'}, +	{"help",                no_argument, NULL, 'h'}, +	{"pingrate",      required_argument, NULL, 'p'}, +	{"timeout",       required_argument, NULL, 't'}, +	{NULL,                  no_argument, NULL, 0x0} +};  /*   * This function simply sends an IOCTL to the driver, which in turn ticks @@ -23,12 +36,12 @@ const char v = 'V';   */  static void keep_alive(void)  { -    int dummy; -    int ret; +	int dummy; +	int ret; -    ret = ioctl(fd, WDIOC_KEEPALIVE, &dummy); -    if (!ret) -        printf("."); +	ret = ioctl(fd, WDIOC_KEEPALIVE, &dummy); +	if (!ret) +		printf(".");  }  /* @@ -38,75 +51,110 @@ static void keep_alive(void)  static void term(int sig)  { -    int ret = write(fd, &v, 1); +	int ret = write(fd, &v, 1); -    close(fd); -    if (ret < 0) -	printf("\nStopping watchdog ticks failed (%d)...\n", errno); -    else -	printf("\nStopping watchdog ticks...\n"); -    exit(0); +	close(fd); +	if (ret < 0) +		printf("\nStopping watchdog ticks failed (%d)...\n", errno); +	else +		printf("\nStopping watchdog ticks...\n"); +	exit(0); +} + +static void usage(char *progname) +{ +	printf("Usage: %s [options]\n", progname); +	printf(" -b, --bootstatus    Get last boot status (Watchdog/POR)\n"); +	printf(" -d, --disable       Turn off the watchdog timer\n"); +	printf(" -e, --enable        Turn on the watchdog timer\n"); +	printf(" -h, --help          Print the help message\n"); +	printf(" -p, --pingrate=P    Set ping rate to P seconds (default %d)\n", DEFAULT_PING_RATE); +	printf(" -t, --timeout=T     Set timeout to T seconds\n"); +	printf("\n"); +	printf("Parameters are parsed left-to-right in real-time.\n"); +	printf("Example: %s -d -t 10 -p 5 -e\n", progname);  }  int main(int argc, char *argv[])  { -    int flags; -    unsigned int ping_rate = 1; -    int ret; -    int i; +	int flags; +	unsigned int ping_rate = DEFAULT_PING_RATE; +	int ret; +	int c; +	int oneshot = 0; + +	setbuf(stdout, NULL); -    setbuf(stdout, NULL); +	fd = open("/dev/watchdog", O_WRONLY); -    fd = open("/dev/watchdog", O_WRONLY); +	if (fd == -1) { +		printf("Watchdog device not enabled.\n"); +		exit(-1); +	} -    if (fd == -1) { -	printf("Watchdog device not enabled.\n"); -	exit(-1); -    } +	while ((c = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) { +		switch (c) { +		case 'b': +			flags = 0; +			oneshot = 1; +			ret = ioctl(fd, WDIOC_GETBOOTSTATUS, &flags); +			if (!ret) +				printf("Last boot is caused by: %s.\n", (flags != 0) ? +					"Watchdog" : "Power-On-Reset"); +			else +				printf("WDIOC_GETBOOTSTATUS errno '%s'\n", strerror(errno)); +			break; +		case 'd': +			flags = WDIOS_DISABLECARD; +			ret = ioctl(fd, WDIOC_SETOPTIONS, &flags); +			if (!ret) +				printf("Watchdog card disabled.\n"); +			else +				printf("WDIOS_DISABLECARD errno '%s'\n", strerror(errno)); +			break; +		case 'e': +			flags = WDIOS_ENABLECARD; +			ret = ioctl(fd, WDIOC_SETOPTIONS, &flags); +			if (!ret) +				printf("Watchdog card enabled.\n"); +			else +				printf("WDIOS_ENABLECARD errno '%s'\n", strerror(errno)); +			break; +		case 'p': +			ping_rate = strtoul(optarg, NULL, 0); +			if (!ping_rate) +				ping_rate = DEFAULT_PING_RATE; +			printf("Watchdog ping rate set to %u seconds.\n", ping_rate); +			break; +		case 't': +			flags = strtoul(optarg, NULL, 0); +			ret = ioctl(fd, WDIOC_SETTIMEOUT, &flags); +			if (!ret) +				printf("Watchdog timeout set to %u seconds.\n", flags); +			else +				printf("WDIOC_SETTIMEOUT errno '%s'\n", strerror(errno)); +			break; +		default: +			usage(argv[0]); +			goto end; +		} +	} -    for (i = 1; i < argc; i++) { -        if (!strncasecmp(argv[i], "-d", 2)) { -            flags = WDIOS_DISABLECARD; -            ret = ioctl(fd, WDIOC_SETOPTIONS, &flags); -            if (!ret) -                printf("Watchdog card disabled.\n"); -        } else if (!strncasecmp(argv[i], "-e", 2)) { -            flags = WDIOS_ENABLECARD; -            ret = ioctl(fd, WDIOC_SETOPTIONS, &flags); -            if (!ret) -                printf("Watchdog card enabled.\n"); -        } else if (!strncasecmp(argv[i], "-t", 2) && argv[2]) { -            flags = atoi(argv[i + 1]); -            ret = ioctl(fd, WDIOC_SETTIMEOUT, &flags); -            if (!ret) -                printf("Watchdog timeout set to %u seconds.\n", flags); -            i++; -        } else if (!strncasecmp(argv[i], "-p", 2) && argv[2]) { -            ping_rate = strtoul(argv[i + 1], NULL, 0); -            printf("Watchdog ping rate set to %u seconds.\n", ping_rate); -            i++; -        } else { -            printf("-d to disable, -e to enable, -t <n> to set " -                "the timeout,\n-p <n> to set the ping rate, and "); -            printf("run by itself to tick the card.\n"); -            printf("Parameters are parsed left-to-right in real-time.\n"); -            printf("Example: %s -d -t 10 -p 5 -e\n", argv[0]); -            goto end; -        } -    } +	if (oneshot) +		goto end; -    printf("Watchdog Ticking Away!\n"); +	printf("Watchdog Ticking Away!\n"); -    signal(SIGINT, term); +	signal(SIGINT, term); -    while(1) { -	keep_alive(); -	sleep(ping_rate); -    } +	while (1) { +		keep_alive(); +		sleep(ping_rate); +	}  end: -    ret = write(fd, &v, 1); -    if (ret < 0) -	printf("Stopping watchdog ticks failed (%d)...\n", errno); -    close(fd); -    return 0; +	ret = write(fd, &v, 1); +	if (ret < 0) +		printf("Stopping watchdog ticks failed (%d)...\n", errno); +	close(fd); +	return 0;  } diff --git a/tools/testing/selftests/x86/fsgsbase.c b/tools/testing/selftests/x86/fsgsbase.c index b4967d875236..f249e042b3b5 100644 --- a/tools/testing/selftests/x86/fsgsbase.c +++ b/tools/testing/selftests/x86/fsgsbase.c @@ -285,9 +285,12 @@ static void *threadproc(void *ctx)  	}  } -static void set_gs_and_switch_to(unsigned long local, unsigned long remote) +static void set_gs_and_switch_to(unsigned long local, +				 unsigned short force_sel, +				 unsigned long remote)  {  	unsigned long base; +	unsigned short sel_pre_sched, sel_post_sched;  	bool hard_zero = false;  	if (local == HARD_ZERO) { @@ -297,6 +300,8 @@ static void set_gs_and_switch_to(unsigned long local, unsigned long remote)  	printf("[RUN]\tARCH_SET_GS(0x%lx)%s, then schedule to 0x%lx\n",  	       local, hard_zero ? " and clear gs" : "", remote); +	if (force_sel) +		printf("\tBefore schedule, set selector to 0x%hx\n", force_sel);  	if (syscall(SYS_arch_prctl, ARCH_SET_GS, local) != 0)  		err(1, "ARCH_SET_GS");  	if (hard_zero) @@ -307,18 +312,35 @@ static void set_gs_and_switch_to(unsigned long local, unsigned long remote)  		printf("[FAIL]\tGSBASE wasn't set as expected\n");  	} +	if (force_sel) { +		asm volatile ("mov %0, %%gs" : : "rm" (force_sel)); +		sel_pre_sched = force_sel; +		local = read_base(GS); + +		/* +		 * Signal delivery seems to mess up weird selectors.  Put it +		 * back. +		 */ +		asm volatile ("mov %0, %%gs" : : "rm" (force_sel)); +	} else { +		asm volatile ("mov %%gs, %0" : "=rm" (sel_pre_sched)); +	} +  	remote_base = remote;  	ftx = 1;  	syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);  	while (ftx != 0)  		syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0); +	asm volatile ("mov %%gs, %0" : "=rm" (sel_post_sched));  	base = read_base(GS); -	if (base == local) { -		printf("[OK]\tGSBASE remained 0x%lx\n", local); +	if (base == local && sel_pre_sched == sel_post_sched) { +		printf("[OK]\tGS/BASE remained 0x%hx/0x%lx\n", +		       sel_pre_sched, local);  	} else {  		nerrs++; -		printf("[FAIL]\tGSBASE changed to 0x%lx\n", base); +		printf("[FAIL]\tGS/BASE changed from 0x%hx/0x%lx to 0x%hx/0x%lx\n", +		       sel_pre_sched, local, sel_post_sched, base);  	}  } @@ -381,8 +403,15 @@ int main()  	for (int local = 0; local < 4; local++) {  		for (int remote = 0; remote < 4; remote++) { -			set_gs_and_switch_to(bases_with_hard_zero[local], -					     bases_with_hard_zero[remote]); +			for (unsigned short s = 0; s < 5; s++) { +				unsigned short sel = s; +				if (s == 4) +					asm ("mov %%ss, %0" : "=rm" (sel)); +				set_gs_and_switch_to( +					bases_with_hard_zero[local], +					sel, +					bases_with_hard_zero[remote]); +			}  		}  	}  | 
