diff options
Diffstat (limited to 'tools/testing')
176 files changed, 12513 insertions, 2251 deletions
diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index 64cae1a5deff..e1f75a1914a1 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c @@ -370,7 +370,7 @@ acpi_status __wrap_acpi_evaluate_object(acpi_handle handle, acpi_string path, } EXPORT_SYMBOL(__wrap_acpi_evaluate_object); -union acpi_object * __wrap_acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, +union acpi_object * __wrap_acpi_evaluate_dsm(acpi_handle handle, const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4) { union acpi_object *obj = ERR_PTR(-ENXIO); @@ -379,11 +379,11 @@ union acpi_object * __wrap_acpi_evaluate_dsm(acpi_handle handle, const u8 *uuid, rcu_read_lock(); ops = list_first_or_null_rcu(&iomap_head, typeof(*ops), list); if (ops) - obj = ops->evaluate_dsm(handle, uuid, rev, func, argv4); + obj = ops->evaluate_dsm(handle, guid, rev, func, argv4); rcu_read_unlock(); if (IS_ERR(obj)) - return acpi_evaluate_dsm(handle, uuid, rev, func, argv4); + return acpi_evaluate_dsm(handle, guid, rev, func, argv4); return obj; } EXPORT_SYMBOL(__wrap_acpi_evaluate_dsm); diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index c2187178fb13..bef419d4266d 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -1527,9 +1527,6 @@ static void nfit_test1_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en); - set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en); - set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en); - set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en); } static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa, @@ -1546,8 +1543,8 @@ static int nfit_test_blk_do_io(struct nd_blk_region *ndbr, resource_size_t dpa, else { memcpy(iobuf, mmio->addr.base + dpa, len); - /* give us some some coverage of the mmio_flush_range() API */ - mmio_flush_range(mmio->addr.base + dpa, len); + /* give us some some coverage of the arch_invalidate_pmem() API */ + arch_invalidate_pmem(mmio->addr.base + dpa, len); } nd_region_release_lane(nd_region, lane); @@ -1559,7 +1556,7 @@ static unsigned long nfit_ctl_handle; union acpi_object *result; static union acpi_object *nfit_test_evaluate_dsm(acpi_handle handle, - const u8 *uuid, u64 rev, u64 func, union acpi_object *argv4) + const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4) { if (handle != &nfit_ctl_handle) return ERR_PTR(-ENXIO); @@ -1943,7 +1940,7 @@ static __init int nfit_test_init(void) nfit_test->setup = nfit_test0_setup; break; case 1: - nfit_test->num_pm = 1; + nfit_test->num_pm = 2; nfit_test->dcr_idx = NUM_DCR; nfit_test->num_dcr = 2; nfit_test->alloc = nfit_test1_alloc; diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h index f54c0032c6ff..d3d63dd5ed38 100644 --- a/tools/testing/nvdimm/test/nfit_test.h +++ b/tools/testing/nvdimm/test/nfit_test.h @@ -13,6 +13,7 @@ #ifndef __NFIT_TEST_H__ #define __NFIT_TEST_H__ #include <linux/list.h> +#include <linux/uuid.h> #include <linux/ioport.h> #include <linux/spinlock_types.h> @@ -36,7 +37,8 @@ typedef void *acpi_handle; typedef struct nfit_test_resource *(*nfit_test_lookup_fn)(resource_size_t); typedef union acpi_object *(*nfit_test_evaluate_dsm_fn)(acpi_handle handle, - const u8 *uuid, u64 rev, u64 func, union acpi_object *argv4); + const guid_t *guid, u64 rev, u64 func, + union acpi_object *argv4); void __iomem *__wrap_ioremap_nocache(resource_size_t offset, unsigned long size); void __wrap_iounmap(volatile void __iomem *addr); diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 26ce4f7168be..ff805643b5f7 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -52,6 +52,10 @@ override LDFLAGS = override MAKEFLAGS = endif +ifneq ($(KBUILD_SRC),) +override LDFLAGS = +endif + BUILD := $(O) ifndef BUILD BUILD := $(KBUILD_OUTPUT) @@ -62,32 +66,32 @@ endif export BUILD all: - for TARGET in $(TARGETS); do \ + @for TARGET in $(TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ mkdir $$BUILD_TARGET -p; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET;\ done; run_tests: all - for TARGET in $(TARGETS); do \ + @for TARGET in $(TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET run_tests;\ done; hotplug: - for TARGET in $(TARGETS_HOTPLUG); do \ + @for TARGET in $(TARGETS_HOTPLUG); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET;\ done; run_hotplug: hotplug - for TARGET in $(TARGETS_HOTPLUG); do \ + @for TARGET in $(TARGETS_HOTPLUG); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET run_full_test;\ done; clean_hotplug: - for TARGET in $(TARGETS_HOTPLUG); do \ + @for TARGET in $(TARGETS_HOTPLUG); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET clean;\ done; @@ -103,7 +107,7 @@ install: ifdef INSTALL_PATH @# Ask all targets to install their files mkdir -p $(INSTALL_PATH) - for TARGET in $(TARGETS); do \ + @for TARGET in $(TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET INSTALL_PATH=$(INSTALL_PATH)/$$TARGET install; \ done; @@ -128,7 +132,7 @@ else endif clean: - for TARGET in $(TARGETS); do \ + @for TARGET in $(TARGETS); do \ BUILD_TARGET=$$BUILD/$$TARGET; \ make OUTPUT=$$BUILD_TARGET -C $$TARGET clean;\ done; diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile index f389b02d43a0..f4b23d697448 100644 --- a/tools/testing/selftests/bpf/Makefile +++ b/tools/testing/selftests/bpf/Makefile @@ -14,9 +14,10 @@ LDLIBS += -lcap -lelf TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \ test_align -TEST_GEN_FILES = test_pkt_access.o test_xdp.o test_l4lb.o test_tcp_estats.o +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_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 @@ -36,6 +37,5 @@ CLANG ?= clang %.o: %.c $(CLANG) -I. -I./include/uapi -I../../../include/uapi \ - -I../../../../samples/bpf/ \ -Wno-compare-distinct-pointer-types \ -O2 -target bpf -c $< -o $@ diff --git a/tools/testing/selftests/bpf/bpf_endian.h b/tools/testing/selftests/bpf/bpf_endian.h index 487cbfb89beb..74af266aa512 100644 --- a/tools/testing/selftests/bpf/bpf_endian.h +++ b/tools/testing/selftests/bpf/bpf_endian.h @@ -23,11 +23,19 @@ # define __bpf_htons(x) __builtin_bswap16(x) # define __bpf_constant_ntohs(x) ___constant_swab16(x) # define __bpf_constant_htons(x) ___constant_swab16(x) +# define __bpf_ntohl(x) __builtin_bswap32(x) +# define __bpf_htonl(x) __builtin_bswap32(x) +# define __bpf_constant_ntohl(x) ___constant_swab32(x) +# define __bpf_constant_htonl(x) ___constant_swab32(x) #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ # define __bpf_ntohs(x) (x) # define __bpf_htons(x) (x) # define __bpf_constant_ntohs(x) (x) # define __bpf_constant_htons(x) (x) +# define __bpf_ntohl(x) (x) +# define __bpf_htonl(x) (x) +# define __bpf_constant_ntohl(x) (x) +# define __bpf_constant_htonl(x) (x) #else # error "Fix your compiler's __BYTE_ORDER__?!" #endif @@ -38,5 +46,11 @@ #define bpf_ntohs(x) \ (__builtin_constant_p(x) ? \ __bpf_constant_ntohs(x) : __bpf_ntohs(x)) +#define bpf_htonl(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_htonl(x) : __bpf_htonl(x)) +#define bpf_ntohl(x) \ + (__builtin_constant_p(x) ? \ + __bpf_constant_ntohl(x) : __bpf_ntohl(x)) #endif /* __BPF_ENDIAN__ */ diff --git a/tools/testing/selftests/bpf/bpf_helpers.h b/tools/testing/selftests/bpf/bpf_helpers.h new file mode 100644 index 000000000000..36fb9161b34a --- /dev/null +++ b/tools/testing/selftests/bpf/bpf_helpers.h @@ -0,0 +1,207 @@ +#ifndef __BPF_HELPERS_H +#define __BPF_HELPERS_H + +/* helper macro to place programs, maps, license in + * different sections in elf_bpf file. Section names + * are interpreted by elf_bpf loader + */ +#define SEC(NAME) __attribute__((section(NAME), used)) + +/* helper functions called from eBPF programs written in C */ +static void *(*bpf_map_lookup_elem)(void *map, void *key) = + (void *) BPF_FUNC_map_lookup_elem; +static int (*bpf_map_update_elem)(void *map, void *key, void *value, + unsigned long long flags) = + (void *) BPF_FUNC_map_update_elem; +static int (*bpf_map_delete_elem)(void *map, void *key) = + (void *) BPF_FUNC_map_delete_elem; +static int (*bpf_probe_read)(void *dst, int size, void *unsafe_ptr) = + (void *) BPF_FUNC_probe_read; +static unsigned long long (*bpf_ktime_get_ns)(void) = + (void *) BPF_FUNC_ktime_get_ns; +static int (*bpf_trace_printk)(const char *fmt, int fmt_size, ...) = + (void *) BPF_FUNC_trace_printk; +static void (*bpf_tail_call)(void *ctx, void *map, int index) = + (void *) BPF_FUNC_tail_call; +static unsigned long long (*bpf_get_smp_processor_id)(void) = + (void *) BPF_FUNC_get_smp_processor_id; +static unsigned long long (*bpf_get_current_pid_tgid)(void) = + (void *) BPF_FUNC_get_current_pid_tgid; +static unsigned long long (*bpf_get_current_uid_gid)(void) = + (void *) BPF_FUNC_get_current_uid_gid; +static int (*bpf_get_current_comm)(void *buf, int buf_size) = + (void *) BPF_FUNC_get_current_comm; +static unsigned long long (*bpf_perf_event_read)(void *map, + unsigned long long flags) = + (void *) BPF_FUNC_perf_event_read; +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) = + (void *) BPF_FUNC_perf_event_output; +static int (*bpf_get_stackid)(void *ctx, void *map, int flags) = + (void *) BPF_FUNC_get_stackid; +static int (*bpf_probe_write_user)(void *dst, void *src, int size) = + (void *) BPF_FUNC_probe_write_user; +static int (*bpf_current_task_under_cgroup)(void *map, int index) = + (void *) BPF_FUNC_current_task_under_cgroup; +static int (*bpf_skb_get_tunnel_key)(void *ctx, void *key, int size, int flags) = + (void *) BPF_FUNC_skb_get_tunnel_key; +static int (*bpf_skb_set_tunnel_key)(void *ctx, void *key, int size, int flags) = + (void *) BPF_FUNC_skb_set_tunnel_key; +static int (*bpf_skb_get_tunnel_opt)(void *ctx, void *md, int size) = + (void *) BPF_FUNC_skb_get_tunnel_opt; +static int (*bpf_skb_set_tunnel_opt)(void *ctx, void *md, int size) = + (void *) BPF_FUNC_skb_set_tunnel_opt; +static unsigned long long (*bpf_get_prandom_u32)(void) = + (void *) BPF_FUNC_get_prandom_u32; +static int (*bpf_xdp_adjust_head)(void *ctx, int offset) = + (void *) BPF_FUNC_xdp_adjust_head; +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 + */ +struct sk_buff; +unsigned long long load_byte(void *skb, + unsigned long long off) asm("llvm.bpf.load.byte"); +unsigned long long load_half(void *skb, + unsigned long long off) asm("llvm.bpf.load.half"); +unsigned long long load_word(void *skb, + unsigned long long off) asm("llvm.bpf.load.word"); + +/* a helper structure used by eBPF C program + * to describe map attributes to elf_bpf loader + */ +struct bpf_map_def { + unsigned int type; + unsigned int key_size; + unsigned int value_size; + 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) = + (void *) BPF_FUNC_skb_load_bytes; +static int (*bpf_skb_store_bytes)(void *ctx, int off, void *from, int len, int flags) = + (void *) BPF_FUNC_skb_store_bytes; +static int (*bpf_l3_csum_replace)(void *ctx, int off, int from, int to, int flags) = + (void *) BPF_FUNC_l3_csum_replace; +static int (*bpf_l4_csum_replace)(void *ctx, int off, int from, int to, int flags) = + (void *) BPF_FUNC_l4_csum_replace; +static int (*bpf_skb_under_cgroup)(void *ctx, void *map, int index) = + (void *) BPF_FUNC_skb_under_cgroup; +static int (*bpf_skb_change_head)(void *, int len, int flags) = + (void *) BPF_FUNC_skb_change_head; + +#if defined(__x86_64__) + +#define PT_REGS_PARM1(x) ((x)->di) +#define PT_REGS_PARM2(x) ((x)->si) +#define PT_REGS_PARM3(x) ((x)->dx) +#define PT_REGS_PARM4(x) ((x)->cx) +#define PT_REGS_PARM5(x) ((x)->r8) +#define PT_REGS_RET(x) ((x)->sp) +#define PT_REGS_FP(x) ((x)->bp) +#define PT_REGS_RC(x) ((x)->ax) +#define PT_REGS_SP(x) ((x)->sp) +#define PT_REGS_IP(x) ((x)->ip) + +#elif defined(__s390x__) + +#define PT_REGS_PARM1(x) ((x)->gprs[2]) +#define PT_REGS_PARM2(x) ((x)->gprs[3]) +#define PT_REGS_PARM3(x) ((x)->gprs[4]) +#define PT_REGS_PARM4(x) ((x)->gprs[5]) +#define PT_REGS_PARM5(x) ((x)->gprs[6]) +#define PT_REGS_RET(x) ((x)->gprs[14]) +#define PT_REGS_FP(x) ((x)->gprs[11]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->gprs[2]) +#define PT_REGS_SP(x) ((x)->gprs[15]) +#define PT_REGS_IP(x) ((x)->psw.addr) + +#elif defined(__aarch64__) + +#define PT_REGS_PARM1(x) ((x)->regs[0]) +#define PT_REGS_PARM2(x) ((x)->regs[1]) +#define PT_REGS_PARM3(x) ((x)->regs[2]) +#define PT_REGS_PARM4(x) ((x)->regs[3]) +#define PT_REGS_PARM5(x) ((x)->regs[4]) +#define PT_REGS_RET(x) ((x)->regs[30]) +#define PT_REGS_FP(x) ((x)->regs[29]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->regs[0]) +#define PT_REGS_SP(x) ((x)->sp) +#define PT_REGS_IP(x) ((x)->pc) + +#elif defined(__mips__) + +#define PT_REGS_PARM1(x) ((x)->regs[4]) +#define PT_REGS_PARM2(x) ((x)->regs[5]) +#define PT_REGS_PARM3(x) ((x)->regs[6]) +#define PT_REGS_PARM4(x) ((x)->regs[7]) +#define PT_REGS_PARM5(x) ((x)->regs[8]) +#define PT_REGS_RET(x) ((x)->regs[31]) +#define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */ +#define PT_REGS_RC(x) ((x)->regs[1]) +#define PT_REGS_SP(x) ((x)->regs[29]) +#define PT_REGS_IP(x) ((x)->cp0_epc) + +#elif defined(__powerpc__) + +#define PT_REGS_PARM1(x) ((x)->gpr[3]) +#define PT_REGS_PARM2(x) ((x)->gpr[4]) +#define PT_REGS_PARM3(x) ((x)->gpr[5]) +#define PT_REGS_PARM4(x) ((x)->gpr[6]) +#define PT_REGS_PARM5(x) ((x)->gpr[7]) +#define PT_REGS_RC(x) ((x)->gpr[3]) +#define PT_REGS_SP(x) ((x)->sp) +#define PT_REGS_IP(x) ((x)->nip) + +#elif defined(__sparc__) + +#define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0]) +#define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1]) +#define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2]) +#define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3]) +#define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4]) +#define PT_REGS_RET(x) ((x)->u_regs[UREG_I7]) +#define PT_REGS_RC(x) ((x)->u_regs[UREG_I0]) +#define PT_REGS_SP(x) ((x)->u_regs[UREG_FP]) +#if defined(__arch64__) +#define PT_REGS_IP(x) ((x)->tpc) +#else +#define PT_REGS_IP(x) ((x)->pc) +#endif + +#endif + +#ifdef __powerpc__ +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = (ctx)->link; }) +#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP +#elif defined(__sparc__) +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ (ip) = PT_REGS_RET(ctx); }) +#define BPF_KRETPROBE_READ_RET_IP BPF_KPROBE_READ_RET_IP +#else +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ \ + bpf_probe_read(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); }) +#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ \ + bpf_probe_read(&(ip), sizeof(ip), \ + (void *)(PT_REGS_FP(ctx) + sizeof(ip))); }) +#endif + +#endif diff --git a/tools/testing/selftests/bpf/bpf_util.h b/tools/testing/selftests/bpf/bpf_util.h index 20ecbaa0d85d..6c53a8906eff 100644 --- a/tools/testing/selftests/bpf/bpf_util.h +++ b/tools/testing/selftests/bpf/bpf_util.h @@ -12,6 +12,7 @@ static inline unsigned int bpf_num_possible_cpus(void) unsigned int start, end, possible_cpus = 0; char buff[128]; FILE *fp; + int n; fp = fopen(fcpu, "r"); if (!fp) { @@ -20,17 +21,17 @@ static inline unsigned int bpf_num_possible_cpus(void) } while (fgets(buff, sizeof(buff), fp)) { - if (sscanf(buff, "%u-%u", &start, &end) == 2) { - possible_cpus = start == 0 ? end + 1 : 0; - break; + n = sscanf(buff, "%u-%u", &start, &end); + if (n == 0) { + printf("Failed to retrieve # possible CPUs!\n"); + exit(1); + } else if (n == 1) { + end = start; } + possible_cpus = start == 0 ? end + 1 : 0; + break; } - fclose(fp); - if (!possible_cpus) { - printf("Failed to retrieve # possible CPUs!\n"); - exit(1); - } return possible_cpus; } 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 9644d4e069de..8591c89c0828 100644 --- a/tools/testing/selftests/bpf/test_align.c +++ b/tools/testing/selftests/bpf/test_align.c @@ -9,6 +9,8 @@ #include <stddef.h> #include <stdbool.h> +#include <sys/resource.h> + #include <linux/unistd.h> #include <linux/filter.h> #include <linux/bpf_perf_event.h> @@ -25,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]; @@ -34,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 = { @@ -51,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"}, }, }, { @@ -77,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"}, }, }, { @@ -104,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"}, }, }, { @@ -124,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)), \ @@ -164,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))"}, }, }, { @@ -195,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))"}, }, }, { @@ -235,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))"}, }, }, { @@ -295,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. */ - "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", + {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 + */ + 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))"}, }, }, }; @@ -371,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; @@ -378,27 +629,50 @@ static int do_test_single(struct bpf_align_test *test) prog_len = probe_filter_length(prog); fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, prog, prog_len, 1, "GPL", 0, - bpf_vlog, sizeof(bpf_vlog)); - if (fd_prog < 0) { + bpf_vlog, sizeof(bpf_vlog), 2); + 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; } @@ -426,12 +700,15 @@ static int do_test(unsigned int from, unsigned int to) } printf("Results: %d pass %d fail\n", all_pass, all_fail); - return 0; + return all_fail ? EXIT_FAILURE : EXIT_SUCCESS; } int main(int argc, char **argv) { unsigned int from = 0, to = ARRAY_SIZE(tests); + struct rlimit rinf = { RLIM_INFINITY, RLIM_INFINITY }; + + setrlimit(RLIMIT_MEMLOCK, &rinf); if (argc == 3) { unsigned int l = atoi(argv[argc - 2]); diff --git a/tools/testing/selftests/bpf/test_maps.c b/tools/testing/selftests/bpf/test_maps.c index 93314524de0d..fe3a443a1102 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; @@ -239,6 +240,54 @@ static void test_hashmap_percpu(int task, void *data) close(fd); } +static void test_hashmap_walk(int task, void *data) +{ + int fd, i, max_entries = 100000; + long long key, value, next_key; + bool next_key_valid = true; + + fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), + max_entries, map_flags); + if (fd < 0) { + printf("Failed to create hashmap '%s'!\n", strerror(errno)); + exit(1); + } + + for (i = 0; i < max_entries; i++) { + key = i; value = key; + assert(bpf_map_update_elem(fd, &key, &value, BPF_NOEXIST) == 0); + } + + for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key, + &next_key) == 0; i++) { + key = next_key; + assert(bpf_map_lookup_elem(fd, &key, &value) == 0); + } + + assert(i == max_entries); + + assert(bpf_map_get_next_key(fd, NULL, &key) == 0); + for (i = 0; next_key_valid; i++) { + next_key_valid = bpf_map_get_next_key(fd, &key, &next_key) == 0; + assert(bpf_map_lookup_elem(fd, &key, &value) == 0); + value++; + assert(bpf_map_update_elem(fd, &key, &value, BPF_EXIST) == 0); + key = next_key; + } + + assert(i == max_entries); + + for (i = 0; bpf_map_get_next_key(fd, !i ? NULL : &key, + &next_key) == 0; i++) { + key = next_key; + assert(bpf_map_lookup_elem(fd, &key, &value) == 0); + assert(value - 1 == key); + } + + assert(i == max_entries); + close(fd); +} + static void test_arraymap(int task, void *data) { int key, next_key, fd; @@ -390,6 +439,444 @@ 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/detaching 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; + } + + err = bpf_prog_attach(-1, fd, __MAX_BPF_ATTACH_TYPE, 0); + if (!err) { + printf("Failed unknown prog attach\n"); + goto out_sockmap; + } + + err = bpf_prog_detach(fd, BPF_SK_SKB_STREAM_PARSER); + if (err) { + printf("Failed empty parser prog detach\n"); + goto out_sockmap; + } + + err = bpf_prog_detach(fd, BPF_SK_SKB_STREAM_VERDICT); + if (err) { + printf("Failed empty verdict prog detach\n"); + goto out_sockmap; + } + + err = bpf_prog_detach(fd, __MAX_BPF_ATTACH_TYPE); + if (!err) { + printf("Detach invalid prog successful\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; + } + + err = bpf_prog_attach(verdict_prog, map_fd_rx, + __MAX_BPF_ATTACH_TYPE, 0); + if (!err) { + printf("Attached unknown bpf prog\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); + } + + err = bpf_prog_detach(map_fd_rx, __MAX_BPF_ATTACH_TYPE); + if (!err) { + printf("Detached an invalid prog type.\n"); + goto out_sockmap; + } + + err = bpf_prog_detach(map_fd_rx, BPF_SK_SKB_STREAM_PARSER); + if (err) { + printf("Failed parser prog detach\n"); + goto out_sockmap; + } + + err = bpf_prog_detach(map_fd_rx, BPF_SK_SKB_STREAM_VERDICT); + if (err) { + printf("Failed parser prog detach\n"); + goto out_sockmap; + } + + /* 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) @@ -464,6 +951,7 @@ static void test_map_stress(void) run_parallel(100, test_hashmap, NULL); run_parallel(100, test_hashmap_percpu, NULL); run_parallel(100, test_hashmap_sizes, NULL); + run_parallel(100, test_hashmap_walk, NULL); run_parallel(100, test_arraymap, NULL); run_parallel(100, test_arraymap_percpu, NULL); @@ -549,12 +1037,16 @@ static void run_all_tests(void) { test_hashmap(0, NULL); test_hashmap_percpu(0, NULL); + test_hashmap_walk(0, NULL); test_arraymap(0, NULL); test_arraymap_percpu(0, NULL); 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_obj_id.c b/tools/testing/selftests/bpf/test_obj_id.c new file mode 100644 index 000000000000..880d2963b472 --- /dev/null +++ b/tools/testing/selftests/bpf/test_obj_id.c @@ -0,0 +1,35 @@ +/* Copyright (c) 2017 Facebook + * + * 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. + */ +#include <stddef.h> +#include <linux/bpf.h> +#include <linux/pkt_cls.h> +#include "bpf_helpers.h" + +/* It is a dumb bpf program such that it must have no + * issue to be loaded since testing the verifier is + * not the focus here. + */ + +int _version SEC("version") = 1; + +struct bpf_map_def SEC("maps") test_map_id = { + .type = BPF_MAP_TYPE_ARRAY, + .key_size = sizeof(__u32), + .value_size = sizeof(__u64), + .max_entries = 1, +}; + +SEC("test_obj_id_dummy") +int test_obj_id(struct __sk_buff *skb) +{ + __u32 key = 0; + __u64 *value; + + value = bpf_map_lookup_elem(&test_map_id, &key); + + return TC_ACT_OK; +} diff --git a/tools/testing/selftests/bpf/test_pkt_md_access.c b/tools/testing/selftests/bpf/test_pkt_md_access.c new file mode 100644 index 000000000000..7956302ecdf2 --- /dev/null +++ b/tools/testing/selftests/bpf/test_pkt_md_access.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2017 Facebook + * + * 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. + */ +#include <stddef.h> +#include <string.h> +#include <linux/bpf.h> +#include <linux/pkt_cls.h> +#include "bpf_helpers.h" + +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) +{ + TEST_FIELD(__u8, len, 0xFF); + TEST_FIELD(__u16, len, 0xFFFF); + TEST_FIELD(__u32, len, 0xFFFFFFFF); + TEST_FIELD(__u16, protocol, 0xFFFF); + TEST_FIELD(__u32, protocol, 0xFFFFFFFF); + TEST_FIELD(__u8, hash, 0xFF); + TEST_FIELD(__u16, hash, 0xFFFF); + TEST_FIELD(__u32, hash, 0xFFFFFFFF); + + return TC_ACT_OK; +} diff --git a/tools/testing/selftests/bpf/test_progs.c b/tools/testing/selftests/bpf/test_progs.c index b59f5ed4ae40..11ee25cea227 100644 --- a/tools/testing/selftests/bpf/test_progs.c +++ b/tools/testing/selftests/bpf/test_progs.c @@ -22,6 +22,8 @@ typedef __u16 __sum16; #include <sys/wait.h> #include <sys/resource.h> +#include <sys/types.h> +#include <fcntl.h> #include <linux/bpf.h> #include <linux/err.h> @@ -70,41 +72,9 @@ static struct { pass_cnt++; \ printf("%s:PASS:%s %d nsec\n", __func__, tag, duration);\ } \ + __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) { @@ -127,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); @@ -159,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) @@ -220,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) @@ -277,8 +253,244 @@ 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); +} + +static inline __u64 ptr_to_u64(const void *ptr) +{ + return (__u64) (unsigned long) ptr; +} + +static void test_bpf_obj_id(void) +{ + const __u64 array_magic_value = 0xfaceb00c; + const __u32 array_key = 0; + const int nr_iters = 2; + const char *file = "./test_obj_id.o"; + const char *jit_sysctl = "/proc/sys/net/core/bpf_jit_enable"; + + struct bpf_object *objs[nr_iters]; + int prog_fds[nr_iters], map_fds[nr_iters]; + /* +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], zeros[128]; + __u32 i, next_id, info_len, nr_id_found, duration = 0; + int sysctl_fd, jit_enabled = 0, err = 0; + __u64 array_value; + + sysctl_fd = open(jit_sysctl, 0, O_RDONLY); + if (sysctl_fd != -1) { + char tmpc; + + if (read(sysctl_fd, &tmpc, sizeof(tmpc)) == 1) + jit_enabled = (tmpc != '0'); + close(sysctl_fd); + } + + err = bpf_prog_get_fd_by_id(0); + CHECK(err >= 0 || errno != ENOENT, + "get-fd-by-notexist-prog-id", "err %d errno %d\n", err, errno); + + err = bpf_map_get_fd_by_id(0); + CHECK(err >= 0 || errno != ENOENT, + "get-fd-by-notexist-map-id", "err %d errno %d\n", err, errno); + + for (i = 0; i < nr_iters; i++) + 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); + prog_infos[i].xlated_prog_len = sizeof(xlated_insns); + err = bpf_obj_get_info_by_fd(prog_fds[i], &prog_infos[i], + &info_len); + if (CHECK(err || + prog_infos[i].type != BPF_PROG_TYPE_SOCKET_FILTER || + info_len != sizeof(struct bpf_prog_info) || + (jit_enabled && !prog_infos[i].jited_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 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, + !!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"); + assert(map_fds[i] >= 0); + err = bpf_map_update_elem(map_fds[i], &array_key, + &array_magic_value, 0); + assert(!err); + + /* 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 || + map_infos[i].type != BPF_MAP_TYPE_ARRAY || + map_infos[i].key_size != sizeof(__u32) || + map_infos[i].value_size != sizeof(__u64) || + map_infos[i].max_entries != 1 || + map_infos[i].map_flags != 0 || + info_len != sizeof(struct bpf_map_info), + "get-map-info(fd)", + "err %d errno %d type %d(%d) info_len %u(%lu) key_size %u value_size %u max_entries %u map_flags %X\n", + err, errno, + map_infos[i].type, BPF_MAP_TYPE_ARRAY, + info_len, sizeof(struct bpf_map_info), + map_infos[i].key_size, + map_infos[i].value_size, + map_infos[i].max_entries, + map_infos[i].map_flags)) + goto done; + } + + /* Check bpf_prog_get_next_id() */ + nr_id_found = 0; + next_id = 0; + while (!bpf_prog_get_next_id(next_id, &next_id)) { + struct bpf_prog_info prog_info = {}; + int prog_fd; + + info_len = sizeof(prog_info); + + prog_fd = bpf_prog_get_fd_by_id(next_id); + if (prog_fd < 0 && errno == ENOENT) + /* The bpf_prog is in the dead row */ + continue; + if (CHECK(prog_fd < 0, "get-prog-fd(next_id)", + "prog_fd %d next_id %d errno %d\n", + prog_fd, next_id, errno)) + break; + + for (i = 0; i < nr_iters; i++) + if (prog_infos[i].id == next_id) + break; + + if (i == nr_iters) + continue; + + 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)", + "err %d errno %d info_len %u(%lu) memcmp %d\n", + err, errno, info_len, sizeof(struct bpf_prog_info), + memcmp(&prog_info, &prog_infos[i], info_len)); + + close(prog_fd); + } + CHECK(nr_id_found != nr_iters, + "check total prog id found by get_next_id", + "nr_id_found %u(%u)\n", + nr_id_found, nr_iters); + + /* Check bpf_map_get_next_id() */ + nr_id_found = 0; + next_id = 0; + while (!bpf_map_get_next_id(next_id, &next_id)) { + struct bpf_map_info map_info = {}; + int map_fd; + + info_len = sizeof(map_info); + + map_fd = bpf_map_get_fd_by_id(next_id); + if (map_fd < 0 && errno == ENOENT) + /* The bpf_map is in the dead row */ + continue; + if (CHECK(map_fd < 0, "get-map-fd(next_id)", + "map_fd %d next_id %u errno %d\n", + map_fd, next_id, errno)) + break; + + for (i = 0; i < nr_iters; i++) + if (map_infos[i].id == next_id) + break; + + if (i == nr_iters) + continue; + + nr_id_found++; + + err = bpf_map_lookup_elem(map_fd, &array_key, &array_value); + assert(!err); + + err = bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len); + CHECK(err || info_len != sizeof(struct bpf_map_info) || + memcmp(&map_info, &map_infos[i], info_len) || + array_value != array_magic_value, + "check get-map-info(next_id->fd)", + "err %d errno %d info_len %u(%lu) memcmp %d array_value %llu(%llu)\n", + err, errno, info_len, sizeof(struct bpf_map_info), + memcmp(&map_info, &map_infos[i], info_len), + array_value, array_magic_value); + + close(map_fd); + } + CHECK(nr_id_found != nr_iters, + "check total map id found by get_next_id", + "nr_id_found %u(%u)\n", + nr_id_found, nr_iters); + +done: + for (i = 0; i < nr_iters; i++) + bpf_object__close(objs[i]); +} + +static void test_pkt_md_access(void) +{ + const char *file = "./test_pkt_md_access.o"; + struct bpf_object *obj; + __u32 duration, retval; + int err, prog_fd; + + err = bpf_prog_load(file, BPF_PROG_TYPE_SCHED_CLS, &obj, &prog_fd); + if (err) { + error_cnt++; + return; + } + + err = bpf_prog_test_run(prog_fd, 10, &pkt_v4, sizeof(pkt_v4), + NULL, NULL, &retval, &duration); + CHECK(err || retval, "", + "err %d errno %d retval %d duration %d\n", + err, errno, retval, duration); bpf_object__close(obj); } @@ -293,7 +505,9 @@ int main(void) test_xdp(); test_l4lb(); test_tcp_estats(); + test_bpf_obj_id(); + test_pkt_md_access(); printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, error_cnt); - return 0; + return error_cnt ? EXIT_FAILURE : EXIT_SUCCESS; } diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index 0ff8c55c0464..26f3250bdcd2 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, @@ -1073,44 +1327,75 @@ static struct bpf_test tests[] = { .result = ACCEPT, }, { - "check cb access: byte, oob 1", + "__sk_buff->hash, offset 0, byte store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: byte, oob 2", + "__sk_buff->tc_index, offset 3, byte store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_B, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[0]) - 1), + offsetof(struct __sk_buff, tc_index) + 3), + BPF_EXIT_INSN(), + }, + .errstr = "invalid bpf_context access", + .result = REJECT, + }, + { + "check skb->hash byte load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#if __BYTE_ORDER == __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash)), +#else + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 3), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + }, + { + "check skb->hash byte load not permitted 1", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 1), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: byte, oob 3", + "check skb->hash byte load not permitted 2", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash) + 2), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: byte, oob 4", + "check skb->hash byte load not permitted 3", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), +#if __BYTE_ORDER == __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 3), +#else BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) - 1), + offsetof(struct __sk_buff, hash)), +#endif BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", @@ -1184,48 +1469,58 @@ 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 cb access: half, oob 1", + "check __sk_buff->hash, offset 0, half store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: half, oob 2", + "check __sk_buff->tc_index, offset 2, half store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[0]) - 2), + offsetof(struct __sk_buff, tc_index) + 2), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: half, oob 3", + "check skb->hash half load permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), +#if __BYTE_ORDER == __LITTLE_ENDIAN BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4]) + 4), + offsetof(struct __sk_buff, hash)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash) + 2), +#endif BPF_EXIT_INSN(), }, - .errstr = "invalid bpf_context access", - .result = REJECT, + .result = ACCEPT, }, { - "check cb access: half, oob 4", + "check skb->hash half load not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), +#if __BYTE_ORDER == __LITTLE_ENDIAN BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) - 2), + offsetof(struct __sk_buff, hash) + 2), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, hash)), +#endif BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", @@ -1279,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", @@ -1290,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", @@ -1301,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", @@ -1312,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", @@ -1339,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", @@ -1350,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", @@ -1368,28 +1669,6 @@ static struct bpf_test tests[] = { "check cb access: double, oob 2", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[4]) + 8), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .result = REJECT, - }, - { - "check cb access: double, oob 3", - .insns = { - BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, - offsetof(struct __sk_buff, cb[0]) - 8), - BPF_EXIT_INSN(), - }, - .errstr = "invalid bpf_context access", - .result = REJECT, - }, - { - "check cb access: double, oob 4", - .insns = { - BPF_MOV64_IMM(BPF_REG_0, 0), BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, offsetof(struct __sk_buff, cb[4])), BPF_EXIT_INSN(), @@ -1398,22 +1677,22 @@ static struct bpf_test tests[] = { .result = REJECT, }, { - "check cb access: double, oob 5", + "check __sk_buff->ifindex dw store not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), - BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[4]) + 8), + BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, + offsetof(struct __sk_buff, ifindex)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", .result = REJECT, }, { - "check cb access: double, oob 6", + "check __sk_buff->ifindex dw load not permitted", .insns = { BPF_MOV64_IMM(BPF_REG_0, 0), BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, - offsetof(struct __sk_buff, cb[0]) - 8), + offsetof(struct __sk_buff, ifindex)), BPF_EXIT_INSN(), }, .errstr = "invalid bpf_context access", @@ -1505,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", @@ -1517,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", @@ -1561,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", @@ -1573,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", @@ -1914,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", @@ -1981,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, }, { @@ -1998,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, }, { @@ -2200,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, }, { @@ -2217,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, }, { @@ -2234,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, }, { @@ -2305,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), @@ -2634,12 +2910,250 @@ 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, }, { + "direct packet access: test18 (imm += pkt_ptr, 1)", + .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_IMM(BPF_REG_0, 8), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_2), + 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_SCHED_CLS, + }, + { + "direct packet access: test19 (imm += pkt_ptr, 2)", + .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, 3), + BPF_MOV64_IMM(BPF_REG_4, 4), + BPF_ALU64_REG(BPF_ADD, BPF_REG_4, BPF_REG_2), + BPF_STX_MEM(BPF_B, BPF_REG_4, BPF_REG_4, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + }, + { + "direct packet access: test20 (x += pkt_ptr, 1)", + .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_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, 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, 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), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "direct packet access: test21 (x += pkt_ptr, 2)", + .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, 9), + 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, 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, 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), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "direct packet access: test22 (x += pkt_ptr, 3)", + .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_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_2, -8), + BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_3, -16), + BPF_LDX_MEM(BPF_DW, BPF_REG_3, BPF_REG_10, -16), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 11), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8), + 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, 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), + BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 2), + BPF_MOV64_IMM(BPF_REG_2, 1), + BPF_STX_MEM(BPF_H, BPF_REG_4, BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = ACCEPT, + }, + { + "direct packet access: test23 (x += pkt_ptr, 4)", + .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_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_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_0, 31), + 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_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), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .result = REJECT, + .errstr = "invalid access to packet, off=0 size=8, R5(id=1,off=0,r=0)", + }, + { + "direct packet access: test24 (x += pkt_ptr, 5)", + .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_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, 0xff), + BPF_MOV64_REG(BPF_REG_4, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_0, 64), + 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, 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), + BPF_EXIT_INSN(), + }, + .prog_type = BPF_PROG_TYPE_SCHED_CLS, + .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, @@ -2929,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)), @@ -2949,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)), @@ -2979,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)), @@ -3000,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)), @@ -3025,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)), @@ -3054,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)), @@ -3079,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)), @@ -3139,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, @@ -3163,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, @@ -3191,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, @@ -3232,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, }, @@ -3256,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, }, @@ -3272,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), @@ -3283,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, @@ -3310,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, @@ -3340,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, @@ -3483,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), @@ -3528,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, }, @@ -3908,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, }, @@ -3974,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, }, @@ -4020,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, }, @@ -4042,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, }, @@ -4158,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, }, @@ -4181,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, }, @@ -4269,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, }, @@ -4306,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), @@ -4395,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, @@ -4423,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, @@ -4442,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, @@ -4461,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, @@ -4480,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, @@ -4523,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", @@ -4548,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, @@ -4730,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, }, @@ -4786,7 +5531,7 @@ static struct bpf_test tests[] = { BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, sizeof(struct test_val), 4), BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_JMP_REG(BPF_JSGE, BPF_REG_4, BPF_REG_2, 2), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_EMIT_CALL(BPF_FUNC_probe_read), BPF_MOV64_IMM(BPF_REG_0, 0), @@ -4812,7 +5557,7 @@ static struct bpf_test tests[] = { BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, sizeof(struct test_val) + 1, 4), BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_JMP_REG(BPF_JSGE, BPF_REG_4, BPF_REG_2, 2), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_EMIT_CALL(BPF_FUNC_probe_read), BPF_MOV64_IMM(BPF_REG_0, 0), @@ -4840,7 +5585,7 @@ static struct bpf_test tests[] = { BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, sizeof(struct test_val) - 20, 4), BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_JMP_REG(BPF_JSGE, BPF_REG_4, BPF_REG_2, 2), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_EMIT_CALL(BPF_FUNC_probe_read), BPF_MOV64_IMM(BPF_REG_0, 0), @@ -4867,7 +5612,7 @@ static struct bpf_test tests[] = { BPF_JMP_IMM(BPF_JSGT, BPF_REG_2, sizeof(struct test_val) - 19, 4), BPF_MOV64_IMM(BPF_REG_4, 0), - BPF_JMP_REG(BPF_JGE, BPF_REG_4, BPF_REG_2, 2), + BPF_JMP_REG(BPF_JSGE, BPF_REG_4, BPF_REG_2, 2), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_EMIT_CALL(BPF_FUNC_probe_read), BPF_MOV64_IMM(BPF_REG_0, 0), @@ -4879,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), @@ -4892,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, }, @@ -4977,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), @@ -4986,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, }, { @@ -5018,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, }, { @@ -5068,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, }, { @@ -5235,6 +5990,661 @@ static struct bpf_test tests[] = { }, .result = ACCEPT, }, + { + "check bpf_perf_event_data->sample_period byte load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#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 + BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period) + 7), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check bpf_perf_event_data->sample_period half load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#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 + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period) + 6), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check bpf_perf_event_data->sample_period word load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#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 + BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period) + 4), +#endif + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check bpf_perf_event_data->sample_period dword load permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, + offsetof(struct bpf_perf_event_data, sample_period)), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_PERF_EVENT, + }, + { + "check skb->data half load not permitted", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#if __BYTE_ORDER == __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, data)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, data) + 2), +#endif + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + }, + { + "check skb->tc_classid half load not permitted for lwt prog", + .insns = { + BPF_MOV64_IMM(BPF_REG_0, 0), +#if __BYTE_ORDER == __LITTLE_ENDIAN + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid)), +#else + BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_1, + offsetof(struct __sk_buff, tc_classid) + 2), +#endif + BPF_EXIT_INSN(), + }, + .result = REJECT, + .errstr = "invalid bpf_context access", + .prog_type = BPF_PROG_TYPE_LWT_IN, + }, + { + "bounds checks mixing signed and unsigned, positive bounds", + .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, 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, 2), + BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 3), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_1, 4, 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 = "R0 min value is negative", + .result = REJECT, + }, + { + "bounds checks mixing signed and unsigned", + .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, 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, -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 = "R0 min value is negative", + .result = REJECT, + }, + { + "bounds checks mixing signed and unsigned, 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, 9), + 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, -1), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 5), + BPF_MOV64_IMM(BPF_REG_8, 0), + BPF_ALU64_REG(BPF_ADD, BPF_REG_8, BPF_REG_1), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_8, 1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_8), + BPF_ST_MEM(BPF_B, BPF_REG_8, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "R8 invalid mem access 'inv'", + .result = REJECT, + }, + { + "bounds checks mixing signed and unsigned, variant 3", + .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_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, -1), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 4), + BPF_MOV64_REG(BPF_REG_8, BPF_REG_1), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_8, 1, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_8), + BPF_ST_MEM(BPF_B, BPF_REG_8, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "R8 invalid mem access 'inv'", + .result = REJECT, + }, + { + "bounds checks mixing signed and unsigned, variant 4", + .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, 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, 1), + BPF_ALU64_REG(BPF_AND, BPF_REG_1, BPF_REG_2), + 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 }, + .result = ACCEPT, + }, + { + "bounds checks mixing signed and unsigned, variant 5", + .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), + BPF_MOV64_IMM(BPF_REG_2, -1), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, 5), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_1, 1, 4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 4), + BPF_ALU64_REG(BPF_SUB, 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 = "R0 min value is negative", + .result = REJECT, + }, + { + "bounds checks mixing signed and unsigned, variant 6", + .insns = { + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_REG(BPF_REG_3, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, -512), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_10, -16), + BPF_MOV64_IMM(BPF_REG_6, -1), + BPF_JMP_REG(BPF_JGT, BPF_REG_4, BPF_REG_6, 5), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_4, 1, 4), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 1), + BPF_MOV64_IMM(BPF_REG_5, 0), + BPF_ST_MEM(BPF_H, BPF_REG_10, -512, 0), + BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_load_bytes), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .errstr = "R4 min value is negative, either use unsigned", + .result = REJECT, + }, + { + "bounds checks mixing signed and unsigned, variant 7", + .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, 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), + 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 }, + .result = ACCEPT, + }, + { + "bounds checks mixing signed and unsigned, variant 8", + .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), + BPF_MOV64_IMM(BPF_REG_2, -1), + BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + 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 = "R0 min value is negative", + .result = 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, 10), + BPF_ST_MEM(BPF_DW, BPF_REG_10, -16, -8), + BPF_LDX_MEM(BPF_DW, BPF_REG_1, BPF_REG_10, -16), + BPF_LD_IMM64(BPF_REG_2, -9223372036854775808ULL), + BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + 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 }, + .result = ACCEPT, + }, + { + "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), + 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), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_JMP_REG(BPF_JGT, BPF_REG_2, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + 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 = "R0 min value is negative", + .result = REJECT, + }, + { + "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), + 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), + BPF_MOV64_IMM(BPF_REG_2, -1), + BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), + /* Dead branch. */ + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + 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 = "R0 min value is negative", + .result = REJECT, + }, + { + "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), + 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), + BPF_MOV64_IMM(BPF_REG_2, -6), + BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + 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 = "R0 min value is negative", + .result = REJECT, + }, + { + "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), + 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, 6), + 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, 2), + BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_7, 1), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_7, 0, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_ALU64_REG(BPF_ADD, BPF_REG_7, BPF_REG_1), + BPF_JMP_IMM(BPF_JSGT, BPF_REG_7, 4, 2), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_7), + BPF_ST_MEM(BPF_B, BPF_REG_0, 0, 0), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .errstr = "R0 min value is negative", + .result = REJECT, + }, + { + "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)), + 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_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, -1), + BPF_MOV64_IMM(BPF_REG_8, 2), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_9, 42, 6), + BPF_JMP_REG(BPF_JSGT, BPF_REG_8, BPF_REG_1, 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(), + BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_2, -3), + BPF_JMP_IMM(BPF_JA, 0, 0, -7), + }, + .fixup_map1 = { 4 }, + .errstr = "R0 min value is negative", + .result = REJECT, + }, + { + "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), + 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, 4), + 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, -6), + BPF_JMP_REG(BPF_JGE, BPF_REG_2, BPF_REG_1, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + BPF_ALU64_REG(BPF_ADD, BPF_REG_0, BPF_REG_1), + BPF_JMP_IMM(BPF_JGT, BPF_REG_0, 1, 2), + BPF_MOV64_IMM(BPF_REG_0, 0), + BPF_EXIT_INSN(), + 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 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, + }, + { + "invalid 64-bit BPF_END", + .insns = { + BPF_MOV32_IMM(BPF_REG_0, 0), + { + .code = BPF_ALU64 | BPF_END | BPF_TO_LE, + .dst_reg = BPF_REG_0, + .src_reg = 0, + .off = 0, + .imm = 32, + }, + BPF_EXIT_INSN(), + }, + .errstr = "BPF_END uses reserved fields", + .result = REJECT, + }, }; static int probe_filter_length(const struct bpf_insn *fp) @@ -5358,7 +6768,7 @@ static void do_test_single(struct bpf_test *test, bool unpriv, fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, prog, prog_len, test->flags & F_LOAD_WITH_STRICT_ALIGNMENT, - "GPL", 0, bpf_vlog, sizeof(bpf_vlog)); + "GPL", 0, bpf_vlog, sizeof(bpf_vlog), 1); expected_ret = unpriv && test->result_unpriv != UNDEF ? test->result_unpriv : test->result; @@ -5484,7 +6894,7 @@ static int do_test(bool unpriv, unsigned int from, unsigned int to) } printf("Summary: %d PASSED, %d FAILED\n", passes, errors); - return errors ? -errors : 0; + return errors ? EXIT_FAILURE : EXIT_SUCCESS; } int main(int argc, char **argv) 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/Makefile b/tools/testing/selftests/breakpoints/Makefile index 6b214b7b10fb..247b0a1899d7 100644 --- a/tools/testing/selftests/breakpoints/Makefile +++ b/tools/testing/selftests/breakpoints/Makefile @@ -2,14 +2,14 @@ uname_M := $(shell uname -m 2>/dev/null || echo not) ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) +TEST_GEN_PROGS := step_after_suspend_test + ifeq ($(ARCH),x86) -TEST_GEN_PROGS := breakpoint_test +TEST_GEN_PROGS += breakpoint_test endif ifneq (,$(filter $(ARCH),aarch64 arm64)) -TEST_GEN_PROGS := breakpoint_test_arm64 +TEST_GEN_PROGS += breakpoint_test_arm64 endif -TEST_GEN_PROGS += step_after_suspend_test - include ../lib.mk diff --git a/tools/testing/selftests/breakpoints/breakpoint_test.c b/tools/testing/selftests/breakpoints/breakpoint_test.c index 120895ab5505..901b85ea6a59 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test.c @@ -16,6 +16,8 @@ #include <signal.h> #include <sys/types.h> #include <sys/wait.h> +#include <errno.h> +#include <string.h> #include "../kselftest.h" @@ -42,10 +44,9 @@ static void set_breakpoint_addr(void *addr, int n) ret = ptrace(PTRACE_POKEUSER, child_pid, offsetof(struct user, u_debugreg[n]), addr); - if (ret) { - perror("Can't set breakpoint addr\n"); - ksft_exit_fail(); - } + if (ret) + ksft_exit_fail_msg("Can't set breakpoint addr: %s\n", + strerror(errno)); } static void toggle_breakpoint(int n, int type, int len, @@ -106,8 +107,8 @@ static void toggle_breakpoint(int n, int type, int len, ret = ptrace(PTRACE_POKEUSER, child_pid, offsetof(struct user, u_debugreg[7]), dr7); if (ret) { - perror("Can't set dr7"); - ksft_exit_fail(); + ksft_print_msg("Can't set dr7: %s\n", strerror(errno)); + exit(-1); } } @@ -206,7 +207,7 @@ static void trigger_tests(void) ret = ptrace(PTRACE_TRACEME, 0, NULL, 0); if (ret) { - perror("Can't be traced?\n"); + ksft_print_msg("Can't be traced? %s\n", strerror(errno)); return; } @@ -261,29 +262,30 @@ static void trigger_tests(void) static void check_success(const char *msg) { - const char *msg2; int child_nr_tests; int status; + int ret; /* Wait for the child to SIGTRAP */ wait(&status); - msg2 = "Failed"; + ret = 0; if (WSTOPSIG(status) == SIGTRAP) { child_nr_tests = ptrace(PTRACE_PEEKDATA, child_pid, &nr_tests, 0); if (child_nr_tests == nr_tests) - msg2 = "Ok"; - if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) { - perror("Can't poke\n"); - ksft_exit_fail(); - } + ret = 1; + if (ptrace(PTRACE_POKEDATA, child_pid, &trapped, 1)) + ksft_exit_fail_msg("Can't poke: %s\n", strerror(errno)); } nr_tests++; - printf("%s [%s]\n", msg, msg2); + if (ret) + ksft_test_result_pass(msg); + else + ksft_test_result_fail(msg); } static void launch_instruction_breakpoints(char *buf, int local, int global) @@ -294,7 +296,7 @@ static void launch_instruction_breakpoints(char *buf, int local, int global) set_breakpoint_addr(dummy_funcs[i], i); toggle_breakpoint(i, BP_X, 1, local, global, 1); ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test breakpoint %d with local: %d global: %d", + sprintf(buf, "Test breakpoint %d with local: %d global: %d\n", i, local, global); check_success(buf); toggle_breakpoint(i, BP_X, 1, local, global, 0); @@ -316,8 +318,9 @@ static void launch_watchpoints(char *buf, int mode, int len, set_breakpoint_addr(&dummy_var[i], i); toggle_breakpoint(i, mode, len, local, global, 1); ptrace(PTRACE_CONT, child_pid, NULL, 0); - sprintf(buf, "Test %s watchpoint %d with len: %d local: " - "%d global: %d", mode_str, i, len, local, global); + sprintf(buf, + "Test %s watchpoint %d with len: %d local: %d global: %d\n", + mode_str, i, len, local, global); check_success(buf); toggle_breakpoint(i, mode, len, local, global, 0); } @@ -364,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); } @@ -378,10 +381,12 @@ int main(int argc, char **argv) pid_t pid; int ret; + ksft_print_header(); + pid = fork(); if (!pid) { trigger_tests(); - return 0; + exit(0); } child_pid = pid; @@ -392,5 +397,5 @@ int main(int argc, char **argv) wait(NULL); - return ksft_exit_pass(); + ksft_exit_pass(); } diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c index 3897e996541e..960d02100c26 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c @@ -43,19 +43,25 @@ static void child(int size, int wr) volatile uint8_t *addr = &var[32 + wr]; if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { - perror("ptrace(PTRACE_TRACEME) failed"); + ksft_print_msg( + "ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); _exit(1); } if (raise(SIGSTOP) != 0) { - perror("raise(SIGSTOP) failed"); + ksft_print_msg( + "raise(SIGSTOP) failed: %s\n", strerror(errno)); _exit(1); } if ((uintptr_t) addr % size) { - perror("Wrong address write for the given size\n"); + ksft_print_msg( + "Wrong address write for the given size: %s\n", + strerror(errno)); _exit(1); } + switch (size) { case 1: *addr = 47; @@ -100,12 +106,14 @@ static bool set_watchpoint(pid_t pid, int size, int wp) if (ptrace(PTRACE_SETREGSET, pid, NT_ARM_HW_WATCH, &iov) == 0) return true; - if (errno == EIO) { - printf("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) " - "not supported on this hardware\n"); - ksft_exit_skip(); - } - perror("ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed"); + if (errno == EIO) + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) not supported on this hardware: %s\n", + strerror(errno)); + + ksft_print_msg( + "ptrace(PTRACE_SETREGSET, NT_ARM_HW_WATCH) failed: %s\n", + strerror(errno)); return false; } @@ -117,7 +125,8 @@ static bool run_test(int wr_size, int wp_size, int wr, int wp) pid_t wpid; if (pid < 0) { - perror("fork() failed"); + ksft_test_result_fail( + "fork() failed: %s\n", strerror(errno)); return false; } if (pid == 0) @@ -125,15 +134,17 @@ static bool run_test(int wr_size, int wp_size, int wr, int wp) wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg( + "child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGSTOP) { - printf("child did not stop with SIGSTOP\n"); + ksft_print_msg("child did not stop with SIGSTOP\n"); return false; } @@ -141,42 +152,49 @@ static bool run_test(int wr_size, int wp_size, int wr, int wp) return false; if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { - perror("ptrace(PTRACE_SINGLESTEP) failed"); + ksft_print_msg( + "ptrace(PTRACE_SINGLESTEP) failed: %s\n", + strerror(errno)); return false; } alarm(3); wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); return false; } alarm(0); if (WIFEXITED(status)) { - printf("child did not single-step\t"); + ksft_print_msg("child did not single-step\n"); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg("child did not stop\n"); return false; } if (WSTOPSIG(status) != SIGTRAP) { - printf("child did not stop with SIGTRAP\n"); + ksft_print_msg("child did not stop with SIGTRAP\n"); return false; } if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo) != 0) { - perror("ptrace(PTRACE_GETSIGINFO)"); + ksft_print_msg( + "ptrace(PTRACE_GETSIGINFO): %s\n", + strerror(errno)); return false; } if (siginfo.si_code != TRAP_HWBKPT) { - printf("Unexpected si_code %d\n", siginfo.si_code); + ksft_print_msg( + "Unexpected si_code %d\n", siginfo.si_code); return false; } kill(pid, SIGKILL); wpid = waitpid(pid, &status, 0); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg( + "waitpid() failed: %s\n", strerror(errno)); return false; } return true; @@ -194,6 +212,8 @@ int main(int argc, char **argv) int wr, wp, size; bool result; + ksft_print_header(); + act.sa_handler = sigalrm; sigemptyset(&act.sa_mask); act.sa_flags = 0; @@ -201,14 +221,16 @@ int main(int argc, char **argv) for (size = 1; size <= 32; size = size*2) { for (wr = 0; wr <= 32; wr = wr + size) { for (wp = wr - size; wp <= wr + size; wp = wp + size) { - printf("Test size = %d write offset = %d watchpoint offset = %d\t", size, wr, wp); result = run_test(size, MIN(size, 8), wr, wp); - if ((result && wr == wp) || (!result && wr != wp)) { - printf("[OK]\n"); - ksft_inc_pass_cnt(); - } else { - printf("[FAILED]\n"); - ksft_inc_fail_cnt(); + if ((result && wr == wp) || + (!result && wr != wp)) + ksft_test_result_pass( + "Test size = %d write offset = %d watchpoint offset = %d\n", + size, wr, wp); + else { + ksft_test_result_fail( + "Test size = %d write offset = %d watchpoint offset = %d\n", + size, wr, wp); succeeded = false; } } @@ -216,19 +238,18 @@ int main(int argc, char **argv) } for (size = 1; size <= 32; size = size*2) { - printf("Test size = %d write offset = %d watchpoint offset = -8\t", size, -size); - - if (run_test(size, 8, -size, -8)) { - printf("[OK]\n"); - ksft_inc_pass_cnt(); - } else { - printf("[FAILED]\n"); - ksft_inc_fail_cnt(); + if (run_test(size, 8, -size, -8)) + ksft_test_result_pass( + "Test size = %d write offset = %d watchpoint offset = -8\n", + size, -size); + else { + ksft_test_result_fail( + "Test size = %d write offset = %d watchpoint offset = -8\n", + size, -size); succeeded = false; } } - ksft_print_cnts(); if (succeeded) ksft_exit_pass(); else diff --git a/tools/testing/selftests/breakpoints/step_after_suspend_test.c b/tools/testing/selftests/breakpoints/step_after_suspend_test.c index 60b8a95dac26..3fece06e9f64 100644 --- a/tools/testing/selftests/breakpoints/step_after_suspend_test.c +++ b/tools/testing/selftests/breakpoints/step_after_suspend_test.c @@ -37,17 +37,19 @@ void child(int cpu) CPU_ZERO(&set); CPU_SET(cpu, &set); if (sched_setaffinity(0, sizeof(set), &set) != 0) { - perror("sched_setaffinity() failed"); + ksft_print_msg("sched_setaffinity() failed: %s\n", + strerror(errno)); _exit(1); } if (ptrace(PTRACE_TRACEME, 0, NULL, NULL) != 0) { - perror("ptrace(PTRACE_TRACEME) failed"); + ksft_print_msg("ptrace(PTRACE_TRACEME) failed: %s\n", + strerror(errno)); _exit(1); } if (raise(SIGSTOP) != 0) { - perror("raise(SIGSTOP) failed"); + ksft_print_msg("raise(SIGSTOP) failed: %s\n", strerror(errno)); _exit(1); } @@ -61,7 +63,7 @@ bool run_test(int cpu) pid_t wpid; if (pid < 0) { - perror("fork() failed"); + ksft_print_msg("fork() failed: %s\n", strerror(errno)); return false; } if (pid == 0) @@ -69,57 +71,64 @@ bool run_test(int cpu) wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg("child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGSTOP) { - printf("child did not stop with SIGSTOP\n"); + ksft_print_msg("child did not stop with SIGSTOP: %s\n", + strerror(errno)); return false; } if (ptrace(PTRACE_SINGLESTEP, pid, NULL, NULL) < 0) { if (errno == EIO) { - printf("ptrace(PTRACE_SINGLESTEP) not supported on this architecture\n"); - ksft_exit_skip(); + ksft_exit_skip( + "ptrace(PTRACE_SINGLESTEP) not supported on this architecture: %s\n", + strerror(errno)); } - perror("ptrace(PTRACE_SINGLESTEP) failed"); + ksft_print_msg("ptrace(PTRACE_SINGLESTEP) failed: %s\n", + strerror(errno)); return false; } wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg("waitpid() failed: $s\n", strerror(errno)); return false; } if (WIFEXITED(status)) { - printf("child did not single-step\n"); + ksft_print_msg("child did not single-step: %s\n", + strerror(errno)); return false; } if (!WIFSTOPPED(status)) { - printf("child did not stop\n"); + ksft_print_msg("child did not stop: %s\n", strerror(errno)); return false; } if (WSTOPSIG(status) != SIGTRAP) { - printf("child did not stop with SIGTRAP\n"); + ksft_print_msg("child did not stop with SIGTRAP: %s\n", + strerror(errno)); return false; } if (ptrace(PTRACE_CONT, pid, NULL, NULL) < 0) { - perror("ptrace(PTRACE_CONT) failed"); + ksft_print_msg("ptrace(PTRACE_CONT) failed: %s\n", + strerror(errno)); return false; } wpid = waitpid(pid, &status, __WALL); if (wpid != pid) { - perror("waitpid() failed"); + ksft_print_msg("waitpid() failed: %s\n", strerror(errno)); return false; } if (!WIFEXITED(status)) { - printf("child did not exit after PTRACE_CONT\n"); + ksft_print_msg("child did not exit after PTRACE_CONT: %s\n", + strerror(errno)); return false; } @@ -135,28 +144,21 @@ void suspend(void) struct itimerspec spec = {}; power_state_fd = open("/sys/power/state", O_RDWR); - if (power_state_fd < 0) { - perror("open(\"/sys/power/state\") failed (is this test running as root?)"); - ksft_exit_fail(); - } + if (power_state_fd < 0) + ksft_exit_fail_msg( + "open(\"/sys/power/state\") failed (is this test running as root?)\n"); timerfd = timerfd_create(CLOCK_BOOTTIME_ALARM, 0); - if (timerfd < 0) { - perror("timerfd_create() failed"); - ksft_exit_fail(); - } + if (timerfd < 0) + ksft_exit_fail_msg("timerfd_create() failed\n"); spec.it_value.tv_sec = 5; err = timerfd_settime(timerfd, 0, &spec, NULL); - if (err < 0) { - perror("timerfd_settime() failed"); - ksft_exit_fail(); - } + if (err < 0) + ksft_exit_fail_msg("timerfd_settime() failed\n"); - if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem")) { - perror("entering suspend failed"); - ksft_exit_fail(); - } + if (write(power_state_fd, "mem", strlen("mem")) != strlen("mem")) + ksft_exit_fail_msg("Failed to enter Suspend state\n"); close(timerfd); close(power_state_fd); @@ -171,6 +173,8 @@ int main(int argc, char **argv) int err; int cpu; + ksft_print_header(); + while ((opt = getopt(argc, argv, "n")) != -1) { switch (opt) { case 'n': @@ -187,10 +191,8 @@ int main(int argc, char **argv) suspend(); err = sched_getaffinity(0, sizeof(available_cpus), &available_cpus); - if (err < 0) { - perror("sched_getaffinity() failed"); - ksft_exit_fail(); - } + if (err < 0) + ksft_exit_fail_msg("sched_getaffinity() failed\n"); for (cpu = 0; cpu < CPU_SETSIZE; cpu++) { bool test_success; @@ -199,18 +201,14 @@ int main(int argc, char **argv) continue; test_success = run_test(cpu); - printf("CPU %d: ", cpu); if (test_success) { - printf("[OK]\n"); - ksft_inc_pass_cnt(); + ksft_test_result_pass("CPU %d\n", cpu); } else { - printf("[FAILED]\n"); - ksft_inc_fail_cnt(); + ksft_test_result_fail("CPU %d\n", cpu); succeeded = false; } } - ksft_print_cnts(); if (succeeded) ksft_exit_pass(); else diff --git a/tools/testing/selftests/capabilities/test_execve.c b/tools/testing/selftests/capabilities/test_execve.c index 10a21a958aaf..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,23 +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"); - - if (umount2(".", MNT_DETACH) != 0) - err(1, "detach 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); @@ -157,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); @@ -177,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; } } @@ -198,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) @@ -212,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(); @@ -224,178 +235,193 @@ 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(); /* 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", - false, false, true, false); + 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; } @@ -403,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 717581145cfc..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: "$1 > $testlog + 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/ftrace/test.d/event/toplevel-enable.tc b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc index 0bb5df3c00d4..15e2d3fe1731 100644 --- a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc @@ -28,7 +28,9 @@ echo '*:*' > set_event yield -count=`cat trace | grep -v ^# | wc -l` +echo 0 > tracing_on + +count=`head -n 128 trace | grep -v ^# | wc -l` if [ $count -eq 0 ]; then fail "none of events are recorded" fi @@ -36,10 +38,12 @@ fi do_reset echo 1 > events/enable +echo 1 > tracing_on yield -count=`cat trace | grep -v ^# | wc -l` +echo 0 > tracing_on +count=`head -n 128 trace | grep -v ^# | wc -l` if [ $count -eq 0 ]; then fail "none of events are recorded" fi diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc index 9dcd0ca1f49c..8095e122daa9 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-glob.tc @@ -11,17 +11,6 @@ fi disable_tracing clear_trace -# filter by ?, schedule is always good -if ! echo "sch?dule" > set_ftrace_filter; then - # test for powerpc 64 - if ! echo ".sch?dule" > set_ftrace_filter; then - fail "can not enable schedule filter" - fi - cat set_ftrace_filter | grep '^.schedule$' -else - cat set_ftrace_filter | grep '^schedule$' -fi - ftrace_filter_check() { # glob grep echo "$1" > set_ftrace_filter cut -f1 -d" " set_ftrace_filter > $TMPDIR/actual @@ -39,11 +28,28 @@ ftrace_filter_check '*schedule*' '^.*schedule.*$' # filter by *, end match ftrace_filter_check 'schedule*' '^schedule.*$' +# Advanced full-glob matching feature is recently supported. +# Skip the tests if we are sure the kernel does not support it. +if grep -q 'accepts: .* glob-matching-pattern' README ; then + # filter by *, both side match ftrace_filter_check 'sch*ule' '^sch.*ule$' # filter by char class. ftrace_filter_check '[Ss]y[Ss]_*' '^[Ss]y[Ss]_.*$' +# filter by ?, schedule is always good +if ! echo "sch?dule" > set_ftrace_filter; then + # test for powerpc 64 + if ! echo ".sch?dule" > set_ftrace_filter; then + fail "can not enable schedule filter" + fi + cat set_ftrace_filter | grep '^.schedule$' +else + cat set_ftrace_filter | grep '^schedule$' +fi + +fi + echo > set_ftrace_filter enable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc index aa31368851c9..77dfb6b48186 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc @@ -72,6 +72,15 @@ run_enable_disable() { test_event_enabled $check_disable echo "schedule:${enable}_event:$EVENT" > set_ftrace_filter + if [ -d ../../instances ]; then # Check instances + cur=`cat set_ftrace_filter` + top=`cat ../../set_ftrace_filter` + if [ "$cur" = "$top" ]; then + echo "This kernel is too old to support per instance filter" + reset_ftrace_filter + exit_unsupported + fi + fi echo " make sure it works 5 times" diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc index c8e02ec01eaf..7a9ab4ff83b6 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc @@ -63,6 +63,10 @@ fi # powerpc uses .schedule func="schedule" +available_file=available_filter_functions +if [ -d ../../instances -a -f ../../available_filter_functions ]; then + available_file=../../available_filter_functions +fi x=`grep '^\.schedule$' available_filter_functions | wc -l` if [ "$x" -eq 1 ]; then func=".schedule" @@ -71,6 +75,15 @@ fi echo '** SET TRACEOFF' echo "$func:traceoff" > set_ftrace_filter +if [ -d ../../instances ]; then # Check instances + cur=`cat set_ftrace_filter` + top=`cat ../../set_ftrace_filter` + if [ "$cur" = "$top" ]; then + echo "This kernel is too old to support per instance filter" + reset_ftrace_filter + exit_unsupported + fi +fi cnt=`grep schedule set_ftrace_filter | wc -l` if [ $cnt -ne 1 ]; then @@ -90,11 +103,11 @@ if [ $on != "0" ]; then fail "Tracing is not off" fi -line1=`cat trace | tail -1` +csum1=`md5sum trace` sleep $SLEEP_TIME -line2=`cat trace | tail -1` +csum2=`md5sum trace` -if [ "$line1" != "$line2" ]; then +if [ "$csum1" != "$csum2" ]; then fail "Tracing file is still changing" fi diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc index c73db7863adb..8a353314dc9b 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc @@ -82,7 +82,10 @@ rmdir foo if [ -d foo ]; then fail "foo still exists" fi - +if grep -q "schedule:enable_event:sched:sched_switch" ../set_ftrace_filter; then + echo "Older kernel detected. Cleanup filter" + echo '!schedule:enable_event:sched:sched_switch' > ../set_ftrace_filter +fi instance_slam() { while :; do diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc new file mode 100644 index 000000000000..b9302cc82c12 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_eventname.tc @@ -0,0 +1,36 @@ +#!/bin/sh +# description: Kprobe event auto/manual naming + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +disable_events +echo > kprobe_events + +:;: "Add an event on function without name" ;: + +FUNC=`grep " [tT] .*vfs_read$" /proc/kallsyms | tail -n 1 | cut -f 3 -d " "` +[ "x" != "x$FUNC" ] || exit_unresolved +echo "p $FUNC" > kprobe_events +PROBE_NAME=`echo $FUNC | tr ".:" "_"` +test -d events/kprobes/p_${PROBE_NAME}_0 || exit_failure + +:;: "Add an event on function with new name" ;: + +echo "p:event1 $FUNC" > kprobe_events +test -d events/kprobes/event1 || exit_failure + +:;: "Add an event on function with new name and group" ;: + +echo "p:kprobes2/event2 $FUNC" > kprobe_events +test -d events/kprobes2/event2 || exit_failure + +:;: "Add an event on dot function without name" ;: + +FUNC=`grep -m 10 " [tT] .*\.isra\..*$" /proc/kallsyms | tail -n 1 | cut -f 3 -d " "` +[ "x" != "x$FUNC" ] || exit_unresolved +echo "p $FUNC" > kprobe_events +EVENT=`grep $FUNC kprobe_events | cut -f 1 -d " " | cut -f 2 -d:` +[ "x" != "x$EVENT" ] || exit_failure +test -d events/$EVENT || exit_failure + +echo > kprobe_events diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_module.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_module.tc new file mode 100644 index 000000000000..6d634e4b7680 --- /dev/null +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_module.tc @@ -0,0 +1,28 @@ +#!/bin/sh +# description: Kprobe dynamic event - probing module + +[ -f kprobe_events ] || exit_unsupported # this is configurable + +disable_events +echo > kprobe_events + +:;: "Add an event on a module function without specifying event name" ;: + +MOD=`lsmod | head -n 2 | tail -n 1 | cut -f1 -d" "` +FUNC=`grep -m 1 ".* t .*\\[$MOD\\]" /proc/kallsyms | xargs | cut -f3 -d" "` +[ "x" != "x$MOD" -a "y" != "y$FUNC" ] || exit_unresolved +echo "p $MOD:$FUNC" > kprobe_events +PROBE_NAME=`echo $MOD:$FUNC | tr ".:" "_"` +test -d events/kprobes/p_${PROBE_NAME}_0 || exit_failure + +:;: "Add an event on a module function with new event name" ;: + +echo "p:event1 $MOD:$FUNC" > kprobe_events +test -d events/kprobes/event1 || exit_failure + +:;: "Add an event on a module function with new event and group name" ;: + +echo "p:kprobes1/event1 $MOD:$FUNC" > kprobe_events +test -d events/kprobes1/event1 || exit_failure + +echo > kprobe_events diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc index 57abdf1caabf..7ec6f2639ad6 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_maxactive.tc @@ -2,6 +2,7 @@ # description: Kretprobe dynamic event with maxactive [ -f kprobe_events ] || exit_unsupported # this is configurable +grep -q 'r\[maxactive\]' README || exit_unsupported # this is older version echo > kprobe_events diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc index f4d1ff785d67..a4fd4c851a5b 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/multiple_kprobes.tc @@ -1,11 +1,13 @@ #!/bin/sh # description: Register/unregister many kprobe events +[ -f kprobe_events ] || exit_unsupported # this is configurable + # ftrace fentry skip size depends on the machine architecture. -# Currently HAVE_KPROBES_ON_FTRACE defined on x86 and powerpc +# Currently HAVE_KPROBES_ON_FTRACE defined on x86 and powerpc64le case `uname -m` in x86_64|i[3456]86) OFFS=5;; - ppc*) OFFS=4;; + ppc64le) OFFS=8;; *) OFFS=0;; esac diff --git a/tools/testing/selftests/futex/Makefile b/tools/testing/selftests/futex/Makefile index e2fbb890aef9..f0c0369ccb79 100644 --- a/tools/testing/selftests/futex/Makefile +++ b/tools/testing/selftests/futex/Makefile @@ -7,14 +7,17 @@ TEST_PROGS := run.sh include ../lib.mk all: - for DIR in $(SUBDIRS); do \ + @for DIR in $(SUBDIRS); do \ BUILD_TARGET=$(OUTPUT)/$$DIR; \ mkdir $$BUILD_TARGET -p; \ make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + if [ -e $$DIR/$(TEST_PROGS) ]; then + rsync -a $$DIR/$(TEST_PROGS) $$BUILD_TARGET/; + fi done override define RUN_TESTS - @if [ `dirname $(OUTPUT)` = $(PWD) ]; then ./run.sh; fi + @cd $(OUTPUT); ./run.sh endef override define INSTALL_RULE @@ -33,7 +36,7 @@ override define EMIT_TESTS endef override define CLEAN - for DIR in $(SUBDIRS); do \ + @for DIR in $(SUBDIRS); do \ BUILD_TARGET=$(OUTPUT)/$$DIR; \ mkdir $$BUILD_TARGET -p; \ make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ diff --git a/tools/testing/selftests/futex/functional/futex_requeue_pi.c b/tools/testing/selftests/futex/functional/futex_requeue_pi.c index 3da06ad23996..54cd5c414e82 100644 --- a/tools/testing/selftests/futex/functional/futex_requeue_pi.c +++ b/tools/testing/selftests/futex/functional/futex_requeue_pi.c @@ -32,6 +32,7 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-requeue-pi" #define MAX_WAKE_ITERS 1000 #define THREAD_MAX 10 #define SIGNAL_PERIOD_US 100 @@ -393,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 @@ -404,6 +407,6 @@ int main(int argc, char *argv[]) */ ret = unit_test(broadcast, locked, owner, timeout_ns); - print_result(ret); + print_result(TEST_NAME, ret); return ret; } 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 d5e4f2c4da2a..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 @@ -30,6 +30,8 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-requeue-pi-mismatched-ops" + futex_t f1 = FUTEX_INITIALIZER; futex_t f2 = FUTEX_INITIALIZER; int child_ret = 0; @@ -76,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)) { @@ -130,6 +133,6 @@ int main(int argc, char *argv[]) out: /* If the kernel crashes, we shouldn't return at all. */ - print_result(ret); + print_result(TEST_NAME, ret); return ret; } 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 3d7dc6afc3f8..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 @@ -32,6 +32,7 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-requeue-pi-signal-restart" #define DELAY_US 100 futex_t f1 = FUTEX_INITIALIZER; @@ -142,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); @@ -218,6 +220,6 @@ int main(int argc, char *argv[]) if (ret == RET_PASS && waiter_ret) ret = waiter_ret; - print_result(ret); + print_result(TEST_NAME, ret); return ret; } 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 5f687f247454..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 @@ -34,6 +34,7 @@ #include "logging.h" #include "futextest.h" +#define TEST_NAME "futex-wait-private-mapped-file" #define PAGE_SZ 4096 char pad[PAGE_SZ] = {1}; @@ -60,7 +61,7 @@ void *thr_futex_wait(void *arg) ret = futex_wait(&val, 1, &wait_timeout, 0); if (ret && errno != EWOULDBLOCK && errno != ETIMEDOUT) { error("futex error.\n", errno); - print_result(RET_ERROR); + print_result(TEST_NAME, RET_ERROR); exit(RET_ERROR); } @@ -96,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) { @@ -120,6 +123,6 @@ int main(int argc, char **argv) pthread_join(thr, NULL); out: - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_wait_timeout.c b/tools/testing/selftests/futex/functional/futex_wait_timeout.c index ab428ca894de..bab3dfe1787f 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_timeout.c +++ b/tools/testing/selftests/futex/functional/futex_wait_timeout.c @@ -27,6 +27,8 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-wait-timeout" + static long timeout_ns = 100000; /* 100us default timeout */ void usage(char *prog) @@ -66,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; @@ -81,6 +84,6 @@ int main(int argc, char *argv[]) ret = RET_FAIL; } - print_result(ret); + print_result(TEST_NAME, ret); return ret; } 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 fe7aee96844b..26975322545b 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c +++ b/tools/testing/selftests/futex/functional/futex_wait_uninitialized_heap.c @@ -36,6 +36,7 @@ #include "logging.h" #include "futextest.h" +#define TEST_NAME "futex-wait-uninitialized-heap" #define WAIT_US 5000000 static int child_blocked = 1; @@ -98,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])); @@ -119,6 +121,6 @@ int main(int argc, char **argv) } out: - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c index b6b027448825..da15a63269b4 100644 --- a/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c +++ b/tools/testing/selftests/futex/functional/futex_wait_wouldblock.c @@ -28,6 +28,7 @@ #include "futextest.h" #include "logging.h" +#define TEST_NAME "futex-wait-wouldblock" #define timeout_ns 100000 void usage(char *prog) @@ -63,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); @@ -74,6 +76,6 @@ int main(int argc, char *argv[]) ret = RET_FAIL; } - print_result(ret); + print_result(TEST_NAME, ret); return ret; } diff --git a/tools/testing/selftests/futex/include/logging.h b/tools/testing/selftests/futex/include/logging.h index e14469103f07..01989644e50a 100644 --- a/tools/testing/selftests/futex/include/logging.h +++ b/tools/testing/selftests/futex/include/logging.h @@ -107,24 +107,22 @@ void log_verbosity(int level) * * print_result() is primarily intended for functional tests. */ -void print_result(int ret) +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("Result: %s\n", result); } /* log level macros */ diff --git a/tools/testing/selftests/intel_pstate/.gitignore b/tools/testing/selftests/intel_pstate/.gitignore new file mode 100644 index 000000000000..3bfcbae5fa13 --- /dev/null +++ b/tools/testing/selftests/intel_pstate/.gitignore @@ -0,0 +1,2 @@ +aperf +msr diff --git a/tools/testing/selftests/intel_pstate/Makefile b/tools/testing/selftests/intel_pstate/Makefile index 19678e90efb2..a97e24edde39 100644 --- a/tools/testing/selftests/intel_pstate/Makefile +++ b/tools/testing/selftests/intel_pstate/Makefile @@ -1,7 +1,9 @@ CFLAGS := $(CFLAGS) -Wall -D_GNU_SOURCE -LDFLAGS := $(LDFLAGS) -lm +LDLIBS := $(LDLIBS) -lm +ifeq (,$(filter $(ARCH),x86)) TEST_GEN_FILES := msr aperf +endif TEST_PROGS := run.sh diff --git a/tools/testing/selftests/intel_pstate/run.sh b/tools/testing/selftests/intel_pstate/run.sh index 7868c106b8b1..d3ab48f91cd6 100755 --- a/tools/testing/selftests/intel_pstate/run.sh +++ b/tools/testing/selftests/intel_pstate/run.sh @@ -29,13 +29,12 @@ EVALUATE_ONLY=0 -max_cpus=$(($(nproc)-1)) +if ! uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ | grep -q x86; then + echo "$0 # Skipped: Test can only run on x86 architectures." + exit 0 +fi -# compile programs -gcc aperf.c -Wall -D_GNU_SOURCE -o aperf -lm -[ $? -ne 0 ] && echo "Problem compiling aperf.c." && exit 1 -gcc -o msr msr.c -lm -[ $? -ne 0 ] && echo "Problem compiling msr.c." && exit 1 +max_cpus=$(($(nproc)-1)) function run_test () { diff --git a/tools/testing/selftests/kcmp/kcmp_test.c b/tools/testing/selftests/kcmp/kcmp_test.c index a5a4da856dfe..73684c4a1ed6 100644 --- a/tools/testing/selftests/kcmp/kcmp_test.c +++ b/tools/testing/selftests/kcmp/kcmp_test.c @@ -8,7 +8,6 @@ #include <errno.h> #include <string.h> #include <fcntl.h> - #include <linux/unistd.h> #include <linux/kcmp.h> @@ -16,20 +15,28 @@ #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> +#include <sys/epoll.h> #include "../kselftest.h" -static long sys_kcmp(int pid1, int pid2, int type, int fd1, int fd2) +static long sys_kcmp(int pid1, int pid2, int type, unsigned long fd1, unsigned long fd2) { return syscall(__NR_kcmp, pid1, pid2, type, fd1, fd2); } +static const unsigned int duped_num = 64; + int main(int argc, char **argv) { const char kpath[] = "kcmp-test-file"; + struct kcmp_epoll_slot epoll_slot; + struct epoll_event ev; int pid1, pid2; + int pipefd[2]; int fd1, fd2; + int epollfd; int status; + int fddup; fd1 = open(kpath, O_RDWR | O_CREAT | O_TRUNC, 0644); pid1 = getpid(); @@ -39,6 +46,37 @@ int main(int argc, char **argv) ksft_exit_fail(); } + if (pipe(pipefd)) { + perror("Can't create pipe"); + ksft_exit_fail(); + } + + epollfd = epoll_create1(0); + if (epollfd < 0) { + perror("epoll_create1 failed"); + ksft_exit_fail(); + } + + memset(&ev, 0xff, sizeof(ev)); + ev.events = EPOLLIN | EPOLLOUT; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pipefd[0], &ev)) { + perror("epoll_ctl failed"); + ksft_exit_fail(); + } + + fddup = dup2(pipefd[1], duped_num); + if (fddup < 0) { + perror("dup2 failed"); + ksft_exit_fail(); + } + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fddup, &ev)) { + perror("epoll_ctl failed"); + ksft_exit_fail(); + } + close(fddup); + pid2 = fork(); if (pid2 < 0) { perror("fork failed"); @@ -95,6 +133,24 @@ int main(int argc, char **argv) ksft_inc_pass_cnt(); } + /* Compare epoll target */ + epoll_slot = (struct kcmp_epoll_slot) { + .efd = epollfd, + .tfd = duped_num, + .toff = 0, + }; + ret = sys_kcmp(pid1, pid1, KCMP_EPOLL_TFD, pipefd[1], + (unsigned long)(void *)&epoll_slot); + if (ret) { + printf("FAIL: 0 expected but %d returned (%s)\n", + ret, strerror(errno)); + ksft_inc_fail_cnt(); + ret = -1; + } else { + printf("PASS: 0 returned as expected\n"); + ksft_inc_pass_cnt(); + } + ksft_print_cnts(); if (ret) diff --git a/tools/testing/selftests/kmod/Makefile b/tools/testing/selftests/kmod/Makefile new file mode 100644 index 000000000000..fa2ccc5fb3de --- /dev/null +++ b/tools/testing/selftests/kmod/Makefile @@ -0,0 +1,11 @@ +# Makefile for kmod loading selftests + +# No binaries, but make sure arg-less "make" doesn't trigger "run_tests" +all: + +TEST_PROGS := kmod.sh + +include ../lib.mk + +# Nothing to clean up. +clean: diff --git a/tools/testing/selftests/kmod/config b/tools/testing/selftests/kmod/config new file mode 100644 index 000000000000..259f4fd6b5e2 --- /dev/null +++ b/tools/testing/selftests/kmod/config @@ -0,0 +1,7 @@ +CONFIG_TEST_KMOD=m +CONFIG_TEST_LKM=m +CONFIG_XFS_FS=m + +# For the module parameter force_init_test is used +CONFIG_TUN=m +CONFIG_BTRFS_FS=m diff --git a/tools/testing/selftests/kmod/kmod.sh b/tools/testing/selftests/kmod/kmod.sh new file mode 100755 index 000000000000..7956ea3be667 --- /dev/null +++ b/tools/testing/selftests/kmod/kmod.sh @@ -0,0 +1,615 @@ +#!/bin/bash +# +# Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> +# +# 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; or, when distributed separately from the Linux kernel or +# when incorporated into other software packages, subject to the following +# license: +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of copyleft-next (version 0.3.1 or later) as published +# at http://copyleft-next.org/. + +# This is a stress test script for kmod, the kernel module loader. It uses +# test_kmod which exposes a series of knobs for the API for us so we can +# tweak each test in userspace rather than in kernelspace. +# +# The way kmod works is it uses the kernel's usermode helper API to eventually +# call /sbin/modprobe. It has a limit of the number of concurrent calls +# possible. The kernel interface to load modules is request_module(), however +# mount uses get_fs_type(). Both behave slightly differently, but the +# differences are important enough to test each call separately. For this +# reason test_kmod starts by providing tests for both calls. +# +# The test driver test_kmod assumes a series of defaults which you can +# override by exporting to your environment prior running this script. +# For instance this script assumes you do not have xfs loaded upon boot. +# If this is false, export DEFAULT_KMOD_FS="ext4" prior to running this +# script if the filesyste module you don't have loaded upon bootup +# is ext4 instead. Refer to allow_user_defaults() for a list of user +# override variables possible. +# +# You'll want at least 4 GiB of RAM to expect to run these tests +# without running out of memory on them. For other requirements refer +# to test_reqs() + +set -e + +TEST_NAME="kmod" +TEST_DRIVER="test_${TEST_NAME}" +TEST_DIR=$(dirname $0) + +# This represents +# +# TEST_ID:TEST_COUNT:ENABLED +# +# TEST_ID: is the test id number +# TEST_COUNT: number of times we should run the test +# ENABLED: 1 if enabled, 0 otherwise +# +# Once these are enabled please leave them as-is. Write your own test, +# we have tons of space. +ALL_TESTS="0001:3:1" +ALL_TESTS="$ALL_TESTS 0002:3:1" +ALL_TESTS="$ALL_TESTS 0003:1:1" +ALL_TESTS="$ALL_TESTS 0004:1:1" +ALL_TESTS="$ALL_TESTS 0005:10:1" +ALL_TESTS="$ALL_TESTS 0006:10:1" +ALL_TESTS="$ALL_TESTS 0007:5:1" +ALL_TESTS="$ALL_TESTS 0008:150:1" +ALL_TESTS="$ALL_TESTS 0009:150:1" + +test_modprobe() +{ + if [ ! -d $DIR ]; then + echo "$0: $DIR not present" >&2 + echo "You must have the following enabled in your kernel:" >&2 + cat $TEST_DIR/config >&2 + exit 1 + fi +} + +function allow_user_defaults() +{ + if [ -z $DEFAULT_KMOD_DRIVER ]; then + DEFAULT_KMOD_DRIVER="test_module" + fi + + if [ -z $DEFAULT_KMOD_FS ]; then + DEFAULT_KMOD_FS="xfs" + fi + + if [ -z $PROC_DIR ]; then + PROC_DIR="/proc/sys/kernel/" + fi + + if [ -z $MODPROBE_LIMIT ]; then + MODPROBE_LIMIT=50 + fi + + if [ -z $DIR ]; then + DIR="/sys/devices/virtual/misc/${TEST_DRIVER}0/" + fi + + if [ -z $DEFAULT_NUM_TESTS ]; then + DEFAULT_NUM_TESTS=150 + fi + + MODPROBE_LIMIT_FILE="${PROC_DIR}/kmod-limit" +} + +test_reqs() +{ + if ! which modprobe 2> /dev/null > /dev/null; then + echo "$0: You need modprobe installed" >&2 + exit 1 + fi + + if ! which kmod 2> /dev/null > /dev/null; then + echo "$0: You need kmod installed" >&2 + exit 1 + fi + + # kmod 19 has a bad bug where it returns 0 when modprobe + # gets called *even* if the module was not loaded due to + # some bad heuristics. For details see: + # + # A work around is possible in-kernel but its rather + # complex. + KMOD_VERSION=$(kmod --version | awk '{print $3}') + if [[ $KMOD_VERSION -le 19 ]]; then + echo "$0: You need at least kmod 20" >&2 + echo "kmod <= 19 is buggy, for details see:" >&2 + echo "http://git.kernel.org/cgit/utils/kernel/kmod/kmod.git/commit/libkmod/libkmod-module.c?id=fd44a98ae2eb5eb32161088954ab21e58e19dfc4" >&2 + exit 1 + fi + + uid=$(id -u) + if [ $uid -ne 0 ]; then + echo $msg must be run as root >&2 + exit 0 + fi +} + +function load_req_mod() +{ + trap "test_modprobe" EXIT + + if [ ! -d $DIR ]; then + # Alanis: "Oh isn't it ironic?" + modprobe $TEST_DRIVER + fi +} + +test_finish() +{ + echo "Test completed" +} + +errno_name_to_val() +{ + case "$1" in + # kmod calls modprobe and upon of a module not found + # modprobe returns just 1... However in the kernel we + # *sometimes* see 256... + MODULE_NOT_FOUND) + echo 256;; + SUCCESS) + echo 0;; + -EPERM) + echo -1;; + -ENOENT) + echo -2;; + -EINVAL) + echo -22;; + -ERR_ANY) + echo -123456;; + *) + echo invalid;; + esac +} + +errno_val_to_name() + case "$1" in + 256) + echo MODULE_NOT_FOUND;; + 0) + echo SUCCESS;; + -1) + echo -EPERM;; + -2) + echo -ENOENT;; + -22) + echo -EINVAL;; + -123456) + echo -ERR_ANY;; + *) + echo invalid;; + esac + +config_set_test_case_driver() +{ + if ! echo -n 1 >$DIR/config_test_case; then + echo "$0: Unable to set to test case to driver" >&2 + exit 1 + fi +} + +config_set_test_case_fs() +{ + if ! echo -n 2 >$DIR/config_test_case; then + echo "$0: Unable to set to test case to fs" >&2 + exit 1 + fi +} + +config_num_threads() +{ + if ! echo -n $1 >$DIR/config_num_threads; then + echo "$0: Unable to set to number of threads" >&2 + exit 1 + fi +} + +config_get_modprobe_limit() +{ + if [[ -f ${MODPROBE_LIMIT_FILE} ]] ; then + MODPROBE_LIMIT=$(cat $MODPROBE_LIMIT_FILE) + fi + echo $MODPROBE_LIMIT +} + +config_num_thread_limit_extra() +{ + MODPROBE_LIMIT=$(config_get_modprobe_limit) + let EXTRA_LIMIT=$MODPROBE_LIMIT+$1 + config_num_threads $EXTRA_LIMIT +} + +# For special characters use printf directly, +# refer to kmod_test_0001 +config_set_driver() +{ + if ! echo -n $1 >$DIR/config_test_driver; then + echo "$0: Unable to set driver" >&2 + exit 1 + fi +} + +config_set_fs() +{ + if ! echo -n $1 >$DIR/config_test_fs; then + echo "$0: Unable to set driver" >&2 + exit 1 + fi +} + +config_get_driver() +{ + cat $DIR/config_test_driver +} + +config_get_test_result() +{ + cat $DIR/test_result +} + +config_reset() +{ + if ! echo -n "1" >"$DIR"/reset; then + echo "$0: reset shuld have worked" >&2 + exit 1 + fi +} + +config_show_config() +{ + echo "----------------------------------------------------" + cat "$DIR"/config + echo "----------------------------------------------------" +} + +config_trigger() +{ + if ! echo -n "1" >"$DIR"/trigger_config 2>/dev/null; then + echo "$1: FAIL - loading should have worked" + config_show_config + exit 1 + fi + echo "$1: OK! - loading kmod test" +} + +config_trigger_want_fail() +{ + if echo "1" > $DIR/trigger_config 2>/dev/null; then + echo "$1: FAIL - test case was expected to fail" + config_show_config + exit 1 + fi + echo "$1: OK! - kmod test case failed as expected" +} + +config_expect_result() +{ + RC=$(config_get_test_result) + RC_NAME=$(errno_val_to_name $RC) + + ERRNO_NAME=$2 + ERRNO=$(errno_name_to_val $ERRNO_NAME) + + if [[ $ERRNO_NAME = "-ERR_ANY" ]]; then + if [[ $RC -ge 0 ]]; then + echo "$1: FAIL, test expects $ERRNO_NAME - got $RC_NAME ($RC)" >&2 + config_show_config + exit 1 + fi + elif [[ $RC != $ERRNO ]]; then + echo "$1: FAIL, test expects $ERRNO_NAME ($ERRNO) - got $RC_NAME ($RC)" >&2 + config_show_config + exit 1 + fi + echo "$1: OK! - Return value: $RC ($RC_NAME), expected $ERRNO_NAME" +} + +kmod_defaults_driver() +{ + config_reset + modprobe -r $DEFAULT_KMOD_DRIVER + config_set_driver $DEFAULT_KMOD_DRIVER +} + +kmod_defaults_fs() +{ + config_reset + modprobe -r $DEFAULT_KMOD_FS + config_set_fs $DEFAULT_KMOD_FS + config_set_test_case_fs +} + +kmod_test_0001_driver() +{ + NAME='\000' + + kmod_defaults_driver + config_num_threads 1 + printf '\000' >"$DIR"/config_test_driver + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND +} + +kmod_test_0001_fs() +{ + NAME='\000' + + kmod_defaults_fs + config_num_threads 1 + printf '\000' >"$DIR"/config_test_fs + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} -EINVAL +} + +kmod_test_0001() +{ + kmod_test_0001_driver + kmod_test_0001_fs +} + +kmod_test_0002_driver() +{ + NAME="nope-$DEFAULT_KMOD_DRIVER" + + kmod_defaults_driver + config_set_driver $NAME + config_num_threads 1 + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} MODULE_NOT_FOUND +} + +kmod_test_0002_fs() +{ + NAME="nope-$DEFAULT_KMOD_FS" + + kmod_defaults_fs + config_set_fs $NAME + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} -EINVAL +} + +kmod_test_0002() +{ + kmod_test_0002_driver + kmod_test_0002_fs +} + +kmod_test_0003() +{ + kmod_defaults_fs + config_num_threads 1 + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} SUCCESS +} + +kmod_test_0004() +{ + kmod_defaults_fs + config_num_threads 2 + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} SUCCESS +} + +kmod_test_0005() +{ + kmod_defaults_driver + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} SUCCESS +} + +kmod_test_0006() +{ + kmod_defaults_fs + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} SUCCESS +} + +kmod_test_0007() +{ + kmod_test_0005 + kmod_test_0006 +} + +kmod_test_0008() +{ + kmod_defaults_driver + MODPROBE_LIMIT=$(config_get_modprobe_limit) + let EXTRA=$MODPROBE_LIMIT/6 + config_num_thread_limit_extra $EXTRA + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} SUCCESS +} + +kmod_test_0009() +{ + kmod_defaults_fs + MODPROBE_LIMIT=$(config_get_modprobe_limit) + let EXTRA=$MODPROBE_LIMIT/4 + config_num_thread_limit_extra $EXTRA + config_trigger ${FUNCNAME[0]} + config_expect_result ${FUNCNAME[0]} SUCCESS +} + +list_tests() +{ + echo "Test ID list:" + echo + echo "TEST_ID x NUM_TEST" + echo "TEST_ID: Test ID" + echo "NUM_TESTS: Number of recommended times to run the test" + echo + echo "0001 x $(get_test_count 0001) - Simple test - 1 thread for empty string" + echo "0002 x $(get_test_count 0002) - Simple test - 1 thread for modules/filesystems that do not exist" + echo "0003 x $(get_test_count 0003) - Simple test - 1 thread for get_fs_type() only" + echo "0004 x $(get_test_count 0004) - Simple test - 2 threads for get_fs_type() only" + echo "0005 x $(get_test_count 0005) - multithreaded tests with default setup - request_module() only" + echo "0006 x $(get_test_count 0006) - multithreaded tests with default setup - get_fs_type() only" + echo "0007 x $(get_test_count 0007) - multithreaded tests with default setup test request_module() and get_fs_type()" + echo "0008 x $(get_test_count 0008) - multithreaded - push kmod_concurrent over max_modprobes for request_module()" + echo "0009 x $(get_test_count 0009) - multithreaded - push kmod_concurrent over max_modprobes for get_fs_type()" +} + +usage() +{ + NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .) + let NUM_TESTS=$NUM_TESTS+1 + MAX_TEST=$(printf "%04d\n" $NUM_TESTS) + echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |" + echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>" + echo " [ all ] [ -h | --help ] [ -l ]" + echo "" + echo "Valid tests: 0001-$MAX_TEST" + echo "" + 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 " -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 + echo "If an error every occurs execution will immediately terminate." + echo "If you are adding a new test try using -w <test-ID> first to" + echo "make sure the test passes a series of tests." + echo + echo Example uses: + echo + echo "${TEST_NAME}.sh -- executes all tests" + echo "${TEST_NAME}.sh -t 0008 -- Executes test ID 0008 number of times is recomended" + echo "${TEST_NAME}.sh -w 0008 -- Watch test ID 0008 run until an error occurs" + echo "${TEST_NAME}.sh -s 0008 -- Run test ID 0008 once" + echo "${TEST_NAME}.sh -c 0008 3 -- Run test ID 0008 three times" + echo + list_tests + exit 1 +} + +function test_num() +{ + re='^[0-9]+$' + if ! [[ $1 =~ $re ]]; then + usage + fi +} + +function get_test_count() +{ + test_num $1 + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + LAST_TWO=${TEST_DATA#*:*} + echo ${LAST_TWO%:*} +} + +function get_test_enabled() +{ + test_num $1 + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + echo ${TEST_DATA#*:*:} +} + +function run_all_tests() +{ + for i in $ALL_TESTS ; do + TEST_ID=${i%:*:*} + ENABLED=$(get_test_enabled $TEST_ID) + TEST_COUNT=$(get_test_count $TEST_ID) + if [[ $ENABLED -eq "1" ]]; then + test_case $TEST_ID $TEST_COUNT + fi + done +} + +function watch_log() +{ + if [ $# -ne 3 ]; then + clear + fi + date + echo "Running test: $2 - run #$1" +} + +function watch_case() +{ + i=0 + while [ 1 ]; do + + if [ $# -eq 1 ]; then + test_num $1 + watch_log $i ${TEST_NAME}_test_$1 + ${TEST_NAME}_test_$1 + else + watch_log $i all + run_all_tests + fi + let i=$i+1 + done +} + +function test_case() +{ + NUM_TESTS=$DEFAULT_NUM_TESTS + if [ $# -eq 2 ]; then + NUM_TESTS=$2 + fi + + i=0 + while [ $i -lt $NUM_TESTS ]; do + test_num $1 + watch_log $i ${TEST_NAME}_test_$1 noclear + RUN_TEST=${TEST_NAME}_test_$1 + $RUN_TEST + let i=$i+1 + done +} + +function parse_args() +{ + if [ $# -eq 0 ]; then + run_all_tests + else + if [[ "$1" = "all" ]]; then + run_all_tests + elif [[ "$1" = "-w" ]]; then + shift + watch_case $@ + elif [[ "$1" = "-t" ]]; then + shift + test_num $1 + test_case $1 $(get_test_count $1) + elif [[ "$1" = "-c" ]]; then + shift + test_num $1 + test_num $2 + test_case $1 $2 + elif [[ "$1" = "-s" ]]; then + shift + test_case $1 1 + elif [[ "$1" = "-l" ]]; then + list_tests + elif [[ "$1" = "-h" || "$1" = "--help" ]]; then + usage + else + usage + fi + fi +} + +test_reqs +allow_user_defaults +load_req_mod + +trap "test_finish" EXIT + +parse_args $@ + +exit 0 diff --git a/tools/testing/selftests/kselftest.h b/tools/testing/selftests/kselftest.h index ef1c80d67ac7..1ae565ed9bf0 100644 --- a/tools/testing/selftests/kselftest.h +++ b/tools/testing/selftests/kselftest.h @@ -12,13 +12,15 @@ #include <stdlib.h> #include <unistd.h> +#include <stdarg.h> /* define kselftest exit codes */ #define KSFT_PASS 0 #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 { @@ -27,42 +29,154 @@ 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; +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_error; +} + static inline void ksft_inc_pass_cnt(void) { ksft_cnt.ksft_pass++; } 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) +{ + printf("TAP version 13\n"); +} static inline void ksft_print_cnts(void) { - printf("Pass: %d Fail: %d Xfail: %d Xpass: %d, Xskip: %d\n", + 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_xskip, ksft_cnt.ksft_error); + printf("1..%d\n", ksft_test_num()); +} + +static inline void ksft_print_msg(const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + printf("# "); + vprintf(msg, args); + va_end(args); +} + +static inline void ksft_test_result_pass(const char *msg, ...) +{ + va_list args; + + ksft_cnt.ksft_pass++; + + va_start(args, msg); + printf("ok %d ", ksft_test_num()); + vprintf(msg, args); + va_end(args); +} + +static inline void ksft_test_result_fail(const char *msg, ...) +{ + va_list args; + + ksft_cnt.ksft_fail++; + + va_start(args, msg); + printf("not ok %d ", ksft_test_num()); + vprintf(msg, args); + va_end(args); +} + +static inline void ksft_test_result_skip(const char *msg, ...) +{ + va_list args; + + ksft_cnt.ksft_xskip++; + + va_start(args, msg); + printf("ok %d # skip ", ksft_test_num()); + vprintf(msg, args); + 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(); exit(KSFT_PASS); } + static inline int ksft_exit_fail(void) { + printf("Bail out!\n"); + ksft_print_cnts(); exit(KSFT_FAIL); } + +static inline int ksft_exit_fail_msg(const char *msg, ...) +{ + va_list args; + + va_start(args, msg); + printf("Bail out! "); + vprintf(msg, args); + va_end(args); + + ksft_print_cnts(); + exit(KSFT_FAIL); +} + static inline int ksft_exit_xfail(void) { + ksft_print_cnts(); exit(KSFT_XFAIL); } + static inline int ksft_exit_xpass(void) { + ksft_print_cnts(); exit(KSFT_XPASS); } -static inline int ksft_exit_skip(void) + +static inline int ksft_exit_skip(const char *msg, ...) { + if (msg) { + va_list args; + + va_start(args, msg); + printf("1..%d # Skipped: ", ksft_test_num()); + vprintf(msg, args); + va_end(args); + } else { + ksft_print_cnts(); + } exit(KSFT_SKIP); } diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h new file mode 100644 index 000000000000..e81bd28bdd89 --- /dev/null +++ b/tools/testing/selftests/kselftest_harness.h @@ -0,0 +1,755 @@ +/* + * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by the GPLv2 license. + * + * kselftest_harness.h: simple C unit test helper. + * + * See documentation in Documentation/dev-tools/kselftest.rst + * + * API inspired by code.google.com/p/googletest + */ + +/** + * DOC: example + * + * .. code-block:: c + * + * #include "../kselftest_harness.h" + * + * TEST(standalone_test) { + * do_some_stuff; + * EXPECT_GT(10, stuff) { + * stuff_state_t state; + * enumerate_stuff_state(&state); + * TH_LOG("expectation failed with state: %s", state.msg); + * } + * more_stuff; + * ASSERT_NE(some_stuff, NULL) TH_LOG("how did it happen?!"); + * last_stuff; + * EXPECT_EQ(0, last_stuff); + * } + * + * FIXTURE(my_fixture) { + * mytype_t *data; + * int awesomeness_level; + * }; + * FIXTURE_SETUP(my_fixture) { + * self->data = mytype_new(); + * ASSERT_NE(NULL, self->data); + * } + * FIXTURE_TEARDOWN(my_fixture) { + * mytype_free(self->data); + * } + * TEST_F(my_fixture, data_is_good) { + * EXPECT_EQ(1, is_my_data_good(self->data)); + * } + * + * TEST_HARNESS_MAIN + */ + +#ifndef __KSELFTEST_HARNESS_H +#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> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> + + +/* Utilities exposed to the test definitions */ +#ifndef TH_LOG_STREAM +# define TH_LOG_STREAM stderr +#endif + +#ifndef TH_LOG_ENABLED +# define TH_LOG_ENABLED 1 +#endif + +/** + * TH_LOG(fmt, ...) + * + * @fmt: format string + * @...: optional arguments + * + * .. code-block:: c + * + * TH_LOG(format, ...) + * + * Optional debug logging function available for use in tests. + * Logging may be enabled or disabled by defining TH_LOG_ENABLED. + * 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) \ + __TH_LOG(fmt, ##__VA_ARGS__); \ +} while (0) + +/* Unconditional logger for internal use. */ +#define __TH_LOG(fmt, ...) \ + fprintf(TH_LOG_STREAM, "%s:%d:%s:" fmt "\n", \ + __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__) + +/** + * TEST(test_name) - Defines the test function and creates the registration + * stub + * + * @test_name: test name + * + * .. code-block:: c + * + * TEST(name) { implementation } + * + * Defines a test by name. + * Names must be unique and tests must not be run in parallel. The + * implementation containing block is a function and scoping should be treated + * as such. Returning early may be performed with a bare "return;" statement. + * + * EXPECT_* and ASSERT_* are valid in a TEST() { } context. + */ +#define TEST(test_name) __TEST_IMPL(test_name, -1) + +/** + * TEST_SIGNAL(test_name, signal) + * + * @test_name: test name + * @signal: signal number + * + * .. code-block:: c + * + * TEST_SIGNAL(name, signal) { implementation } + * + * Defines a test by name and the expected term signal. + * Names must be unique and tests must not be run in parallel. The + * implementation containing block is a function and scoping should be treated + * as such. Returning early may be performed with a bare "return;" statement. + * + * EXPECT_* and ASSERT_* are valid in a TEST() { } context. + */ +#define TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal) + +#define __TEST_IMPL(test_name, _signal) \ + static void test_name(struct __test_metadata *_metadata); \ + static struct __test_metadata _##test_name##_object = \ + { name: "global." #test_name, \ + fn: &test_name, termsig: _signal }; \ + static void __attribute__((constructor)) _register_##test_name(void) \ + { \ + __register_test(&_##test_name##_object); \ + } \ + static void test_name( \ + struct __test_metadata __attribute__((unused)) *_metadata) + +/** + * FIXTURE_DATA(datatype_name) - Wraps the struct name so we have one less + * argument to pass around + * + * @datatype_name: datatype name + * + * .. code-block:: c + * + * FIXTURE_DATA(datatype name) + * + * This call may be used when the type of the fixture data + * is needed. In general, this should not be needed unless + * the *self* is being passed to a helper directly. + */ +#define FIXTURE_DATA(datatype_name) struct _test_data_##datatype_name + +/** + * FIXTURE(fixture_name) - Called once per fixture to setup the data and + * register + * + * @fixture_name: fixture name + * + * .. code-block:: c + * + * FIXTURE(datatype name) { + * type property1; + * ... + * }; + * + * Defines the data provided to TEST_F()-defined tests as *self*. It should be + * populated and cleaned up using FIXTURE_SETUP() and FIXTURE_TEARDOWN(). + */ +#define FIXTURE(fixture_name) \ + static void __attribute__((constructor)) \ + _register_##fixture_name##_data(void) \ + { \ + __fixture_count++; \ + } \ + FIXTURE_DATA(fixture_name) + +/** + * FIXTURE_SETUP(fixture_name) - Prepares the setup function for the fixture. + * *_metadata* is included so that ASSERT_* work as a convenience + * + * @fixture_name: fixture name + * + * .. code-block:: c + * + * FIXTURE_SETUP(fixture name) { implementation } + * + * Populates the required "setup" function for a fixture. An instance of the + * datatype defined with FIXTURE_DATA() will be exposed as *self* for the + * implementation. + * + * ASSERT_* are valid for use in this context and will prempt the execution + * of any dependent fixture tests. + * + * A bare "return;" statement may be used to return early. + */ +#define FIXTURE_SETUP(fixture_name) \ + void fixture_name##_setup( \ + struct __test_metadata __attribute__((unused)) *_metadata, \ + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) +/** + * FIXTURE_TEARDOWN(fixture_name) + * + * @fixture_name: fixture name + * + * .. code-block:: c + * + * FIXTURE_TEARDOWN(fixture name) { implementation } + * + * Populates the required "teardown" function for a fixture. An instance of the + * datatype defined with FIXTURE_DATA() will be exposed as *self* for the + * implementation to clean up. + * + * A bare "return;" statement may be used to return early. + */ +#define FIXTURE_TEARDOWN(fixture_name) \ + void fixture_name##_teardown( \ + struct __test_metadata __attribute__((unused)) *_metadata, \ + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) + +/** + * TEST_F(fixture_name, test_name) - Emits test registration and helpers for + * fixture-based test cases + * + * @fixture_name: fixture name + * @test_name: test name + * + * .. code-block:: c + * + * TEST_F(fixture, name) { implementation } + * + * Defines a test that depends on a fixture (e.g., is part of a test case). + * Very similar to TEST() except that *self* is the setup instance of fixture's + * datatype exposed for use by the implementation. + */ +/* TODO(wad) register fixtures on dedicated test lists. */ +#define TEST_F(fixture_name, test_name) \ + __TEST_F_IMPL(fixture_name, test_name, -1) + +#define TEST_F_SIGNAL(fixture_name, test_name, signal) \ + __TEST_F_IMPL(fixture_name, test_name, signal) + +#define __TEST_F_IMPL(fixture_name, test_name, signal) \ + static void fixture_name##_##test_name( \ + struct __test_metadata *_metadata, \ + FIXTURE_DATA(fixture_name) *self); \ + static inline void wrapper_##fixture_name##_##test_name( \ + struct __test_metadata *_metadata) \ + { \ + /* fixture data is alloced, setup, and torn down per call. */ \ + FIXTURE_DATA(fixture_name) self; \ + memset(&self, 0, sizeof(FIXTURE_DATA(fixture_name))); \ + fixture_name##_setup(_metadata, &self); \ + /* Let setup failure terminate early. */ \ + if (!_metadata->passed) \ + return; \ + fixture_name##_##test_name(_metadata, &self); \ + fixture_name##_teardown(_metadata, &self); \ + } \ + static struct __test_metadata \ + _##fixture_name##_##test_name##_object = { \ + name: #fixture_name "." #test_name, \ + fn: &wrapper_##fixture_name##_##test_name, \ + termsig: signal, \ + }; \ + static void __attribute__((constructor)) \ + _register_##fixture_name##_##test_name(void) \ + { \ + __register_test(&_##fixture_name##_##test_name##_object); \ + } \ + static void fixture_name##_##test_name( \ + struct __test_metadata __attribute__((unused)) *_metadata, \ + FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) + +/** + * TEST_HARNESS_MAIN - Simple wrapper to run the test harness + * + * .. code-block:: c + * + * TEST_HARNESS_MAIN + * + * Use once to append a main() to the test file. + */ +#define TEST_HARNESS_MAIN \ + static void __attribute__((constructor)) \ + __constructor_order_last(void) \ + { \ + if (!__constructor_order) \ + __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; \ + } \ + int main(int argc, char **argv) { \ + return test_harness_run(argc, argv); \ + } + +/** + * DOC: operators + * + * Operators for use in TEST() and TEST_F(). + * ASSERT_* calls will stop test execution immediately. + * EXPECT_* calls will emit a failure warning, note it, and continue. + */ + +/** + * ASSERT_EQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_EQ(expected, measured): expected == measured + */ +#define ASSERT_EQ(expected, seen) \ + __EXPECT(expected, seen, ==, 1) + +/** + * ASSERT_NE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_NE(expected, measured): expected != measured + */ +#define ASSERT_NE(expected, seen) \ + __EXPECT(expected, seen, !=, 1) + +/** + * ASSERT_LT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_LT(expected, measured): expected < measured + */ +#define ASSERT_LT(expected, seen) \ + __EXPECT(expected, seen, <, 1) + +/** + * ASSERT_LE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_LE(expected, measured): expected <= measured + */ +#define ASSERT_LE(expected, seen) \ + __EXPECT(expected, seen, <=, 1) + +/** + * ASSERT_GT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_GT(expected, measured): expected > measured + */ +#define ASSERT_GT(expected, seen) \ + __EXPECT(expected, seen, >, 1) + +/** + * ASSERT_GE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_GE(expected, measured): expected >= measured + */ +#define ASSERT_GE(expected, seen) \ + __EXPECT(expected, seen, >=, 1) + +/** + * ASSERT_NULL(seen) + * + * @seen: measured value + * + * ASSERT_NULL(measured): NULL == measured + */ +#define ASSERT_NULL(seen) \ + __EXPECT(NULL, seen, ==, 1) + +/** + * ASSERT_TRUE(seen) + * + * @seen: measured value + * + * ASSERT_TRUE(measured): measured != 0 + */ +#define ASSERT_TRUE(seen) \ + ASSERT_NE(0, seen) + +/** + * ASSERT_FALSE(seen) + * + * @seen: measured value + * + * ASSERT_FALSE(measured): measured == 0 + */ +#define ASSERT_FALSE(seen) \ + ASSERT_EQ(0, seen) + +/** + * ASSERT_STREQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_STREQ(expected, measured): !strcmp(expected, measured) + */ +#define ASSERT_STREQ(expected, seen) \ + __EXPECT_STR(expected, seen, ==, 1) + +/** + * ASSERT_STRNE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * ASSERT_STRNE(expected, measured): strcmp(expected, measured) + */ +#define ASSERT_STRNE(expected, seen) \ + __EXPECT_STR(expected, seen, !=, 1) + +/** + * EXPECT_EQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_EQ(expected, measured): expected == measured + */ +#define EXPECT_EQ(expected, seen) \ + __EXPECT(expected, seen, ==, 0) + +/** + * EXPECT_NE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_NE(expected, measured): expected != measured + */ +#define EXPECT_NE(expected, seen) \ + __EXPECT(expected, seen, !=, 0) + +/** + * EXPECT_LT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_LT(expected, measured): expected < measured + */ +#define EXPECT_LT(expected, seen) \ + __EXPECT(expected, seen, <, 0) + +/** + * EXPECT_LE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_LE(expected, measured): expected <= measured + */ +#define EXPECT_LE(expected, seen) \ + __EXPECT(expected, seen, <=, 0) + +/** + * EXPECT_GT(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_GT(expected, measured): expected > measured + */ +#define EXPECT_GT(expected, seen) \ + __EXPECT(expected, seen, >, 0) + +/** + * EXPECT_GE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_GE(expected, measured): expected >= measured + */ +#define EXPECT_GE(expected, seen) \ + __EXPECT(expected, seen, >=, 0) + +/** + * EXPECT_NULL(seen) + * + * @seen: measured value + * + * EXPECT_NULL(measured): NULL == measured + */ +#define EXPECT_NULL(seen) \ + __EXPECT(NULL, seen, ==, 0) + +/** + * EXPECT_TRUE(seen) + * + * @seen: measured value + * + * EXPECT_TRUE(measured): 0 != measured + */ +#define EXPECT_TRUE(seen) \ + EXPECT_NE(0, seen) + +/** + * EXPECT_FALSE(seen) + * + * @seen: measured value + * + * EXPECT_FALSE(measured): 0 == measured + */ +#define EXPECT_FALSE(seen) \ + EXPECT_EQ(0, seen) + +/** + * EXPECT_STREQ(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_STREQ(expected, measured): !strcmp(expected, measured) + */ +#define EXPECT_STREQ(expected, seen) \ + __EXPECT_STR(expected, seen, ==, 0) + +/** + * EXPECT_STRNE(expected, seen) + * + * @expected: expected value + * @seen: measured value + * + * EXPECT_STRNE(expected, measured): strcmp(expected, measured) + */ +#define EXPECT_STRNE(expected, seen) \ + __EXPECT_STR(expected, seen, !=, 0) + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +/* Support an optional handler after and ASSERT_* or EXPECT_*. The approach is + * not thread-safe, but it should be fine in most sane test scenarios. + * + * Using __bail(), which optionally abort()s, is the easiest way to early + * return while still providing an optional block to the API consumer. + */ +#define OPTIONAL_HANDLER(_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; \ + __TH_LOG("Expected %s (%llu) %s %s (%llu)", \ + #_expected, __exp_print, #_t, \ + #_seen, __seen_print); \ + _metadata->passed = 0; \ + /* Ensure the optional handler is triggered */ \ + _metadata->trigger = 1; \ + } \ +} while (0); OPTIONAL_HANDLER(_assert) + +#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; \ + _metadata->trigger = 1; \ + } \ +} while (0); OPTIONAL_HANDLER(_assert) + +/* Contains all the information for test execution and status checking. */ +struct __test_metadata { + const char *name; + void (*fn)(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; +}; + +/* Storage for the (global) tests to be run. */ +static struct __test_metadata *__test_list; +static unsigned int __test_count; +static unsigned int __fixture_count; +static int __constructor_order; + +#define _CONSTRUCTOR_ORDER_FORWARD 1 +#define _CONSTRUCTOR_ORDER_BACKWARD -1 + +/* + * Since constructors are called in reverse order, reverse the test + * list so tests are run in source declaration order. + * https://gcc.gnu.org/onlinedocs/gccint/Initialization.html + * However, it seems not all toolchains do this correctly, so use + * __constructor_order to detect which direction is called first + * and adjust list building logic to get things running in the right + * direction. + */ +static inline void __register_test(struct __test_metadata *t) +{ + __test_count++; + /* Circular linked list where only prev is circular. */ + if (__test_list == NULL) { + __test_list = t; + t->next = NULL; + t->prev = t; + return; + } + if (__constructor_order == _CONSTRUCTOR_ORDER_FORWARD) { + t->next = NULL; + t->prev = __test_list->prev; + t->prev->next = t; + __test_list->prev = t; + } else { + t->next = __test_list; + t->next->prev = t; + t->prev = t; + __test_list = t; + } +} + +static inline int __bail(int for_realz, bool no_print, __u8 step) +{ + if (for_realz) { + if (no_print) + _exit(step); + abort(); + } + return 0; +} + +void __run_test(struct __test_metadata *t) +{ + pid_t child_pid; + int status; + + t->passed = 1; + t->trigger = 0; + printf("[ RUN ] %s\n", t->name); + child_pid = fork(); + if (child_pid < 0) { + printf("ERROR SPAWNING TEST CHILD\n"); + t->passed = 0; + } else if (child_pid == 0) { + t->fn(t); + /* 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; + 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; + if (WTERMSIG(status) == SIGABRT) { + fprintf(TH_LOG_STREAM, + "%s: Test terminated by assertion\n", + t->name); + } else if (WTERMSIG(status) == t->termsig) { + t->passed = 1; + } else { + fprintf(TH_LOG_STREAM, + "%s: Test terminated unexpectedly " + "by signal %d\n", + t->name, + WTERMSIG(status)); + } + } else { + fprintf(TH_LOG_STREAM, + "%s: Test ended in some other way [%u]\n", + t->name, + status); + } + } + printf("[ %4s ] %s\n", (t->passed ? "OK" : "FAIL"), t->name); +} + +static int test_harness_run(int __attribute__((unused)) argc, + char __attribute__((unused)) **argv) +{ + struct __test_metadata *t; + int ret = 0; + unsigned int count = 0; + unsigned int pass_count = 0; + + /* TODO(wad) add optional arguments similar to gtest. */ + printf("[==========] Running %u tests from %u test cases.\n", + __test_count, __fixture_count + 1); + for (t = __test_list; t; t = t->next) { + count++; + __run_test(t); + if (t->passed) + pass_count++; + else + ret = 1; + } + printf("[==========] %u / %u tests passed.\n", pass_count, count); + printf("[ %s ]\n", (ret ? "FAILED" : "PASSED")); + return ret; +} + +static void __attribute__((constructor)) __constructor_order_first(void) +{ + if (!__constructor_order) + __constructor_order = _CONSTRUCTOR_ORDER_FORWARD; +} + +#endif /* __KSELFTEST_HARNESS_H */ diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index 959273c3a52e..f65886af7c0c 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -6,20 +6,49 @@ ifeq (0,$(MAKELEVEL)) OUTPUT := $(shell pwd) endif +# The following are built by lib.mk common compile rules. +# TEST_CUSTOM_PROGS should be used by tests that require +# custom build rule and prevent common build rule use. +# TEST_PROGS are for test shell scripts. +# TEST_CUSTOM_PROGS and TEST_PROGS will be run by common run_tests +# and install targets. Common clean doesn't touch them. TEST_GEN_PROGS := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS)) +TEST_GEN_PROGS_EXTENDED := $(patsubst %,$(OUTPUT)/%,$(TEST_GEN_PROGS_EXTENDED)) 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 $$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 > /tmp/$$BASENAME_TEST 2>&1 && 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) +ifneq ($(KBUILD_SRC),) + @if [ "X$(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES)" != "X" ]; then + @rsync -aq $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT) + fi + @if [ "X$(TEST_PROGS)" != "X" ]; then + $(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(OUTPUT)/$(TEST_PROGS)) + else + $(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS)) + fi +else + $(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_PROGS)) +endif define INSTALL_RULE @if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then \ @@ -27,10 +56,10 @@ define INSTALL_RULE echo "rsync -a $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/"; \ rsync -a $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(INSTALL_PATH)/; \ fi - @if [ "X$(TEST_GEN_PROGS)$(TEST_GEN_PROGS_EXTENDED)$(TEST_GEN_FILES)" != "X" ]; then \ + @if [ "X$(TEST_GEN_PROGS)$(TEST_CUSTOM_PROGS)$(TEST_GEN_PROGS_EXTENDED)$(TEST_GEN_FILES)" != "X" ]; then \ mkdir -p ${INSTALL_PATH}; \ - echo "rsync -a $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/"; \ - rsync -a $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/; \ + echo "rsync -a $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/"; \ + rsync -a $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(INSTALL_PATH)/; \ fi endef @@ -42,15 +71,20 @@ else endif define EMIT_TESTS - @for TEST in $(TEST_GEN_PROGS) $(TEST_PROGS); do \ + @for TEST in $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) $(TEST_PROGS); do \ BASENAME_TEST=`basename $$TEST`; \ - echo "(./$$BASENAME_TEST && echo \"selftests: $$BASENAME_TEST [PASS]\") || echo \"selftests: $$BASENAME_TEST [FAIL]\""; \ + echo "(./$$BASENAME_TEST > /tmp/$$BASENAME_TEST 2>&1 && echo \"selftests: $$BASENAME_TEST [PASS]\") || echo \"selftests: $$BASENAME_TEST [FAIL]\""; \ done; endef emit_tests: $(EMIT_TESTS) +# define if isn't already. It is undefined in make O= case. +ifeq ($(RM),) +RM := rm -f +endif + define CLEAN $(RM) -r $(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED) $(TEST_GEN_FILES) $(EXTRA_CLEAN) endef @@ -58,6 +92,15 @@ endef clean: $(CLEAN) +# When make O= with kselftest target from main level +# the following aren't defined. +# +ifneq ($(KBUILD_SRC),) +LINK.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) +COMPILE.S = $(CC) $(ASFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c +LINK.S = $(CC) $(ASFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) +endif + $(OUTPUT)/%:%.c $(LINK.c) $^ $(LDLIBS) -o $@ diff --git a/tools/testing/selftests/lib/bitmap.sh b/tools/testing/selftests/lib/bitmap.sh index 2da187b6ddad..b073c22a3435 100755 --- a/tools/testing/selftests/lib/bitmap.sh +++ b/tools/testing/selftests/lib/bitmap.sh @@ -1,5 +1,9 @@ #!/bin/sh # Runs bitmap infrastructure tests using test_bitmap kernel module +if ! /sbin/modprobe -q -n test_bitmap; then + echo "bitmap: [SKIP]" + exit 77 +fi if /sbin/modprobe -q test_bitmap; then /sbin/modprobe -q -r test_bitmap diff --git a/tools/testing/selftests/lib/printf.sh b/tools/testing/selftests/lib/printf.sh index 4fdc70fe6980..cbf3b124bd94 100755 --- a/tools/testing/selftests/lib/printf.sh +++ b/tools/testing/selftests/lib/printf.sh @@ -1,5 +1,9 @@ #!/bin/sh # Runs printf infrastructure using test_printf kernel module +if ! /sbin/modprobe -q -n test_printf; then + echo "printf: [SKIP]" + exit 77 +fi if /sbin/modprobe -q test_printf; then /sbin/modprobe -q -r test_printf diff --git a/tools/testing/selftests/membarrier/membarrier_test.c b/tools/testing/selftests/membarrier/membarrier_test.c index 535f0fef4d0b..21399fcf1a59 100644 --- a/tools/testing/selftests/membarrier/membarrier_test.c +++ b/tools/testing/selftests/membarrier/membarrier_test.c @@ -7,56 +7,63 @@ #include "../kselftest.h" -enum test_membarrier_status { - TEST_MEMBARRIER_PASS = 0, - TEST_MEMBARRIER_FAIL, - TEST_MEMBARRIER_SKIP, -}; - static int sys_membarrier(int cmd, int flags) { return syscall(__NR_membarrier, cmd, flags); } -static enum test_membarrier_status test_membarrier_cmd_fail(void) +static int test_membarrier_cmd_fail(void) { int cmd = -1, flags = 0; if (sys_membarrier(cmd, flags) != -1) { - printf("membarrier: Wrong command should fail but passed.\n"); - return TEST_MEMBARRIER_FAIL; + ksft_exit_fail_msg( + "sys membarrier invalid command test: command = %d, flags = %d. Should fail, but passed\n", + cmd, flags); } - return TEST_MEMBARRIER_PASS; + + ksft_test_result_pass( + "sys membarrier invalid command test: command = %d, flags = %d. Failed as expected\n", + cmd, flags); + return 0; } -static enum test_membarrier_status test_membarrier_flags_fail(void) +static int test_membarrier_flags_fail(void) { int cmd = MEMBARRIER_CMD_QUERY, flags = 1; if (sys_membarrier(cmd, flags) != -1) { - printf("membarrier: Wrong flags should fail but passed.\n"); - return TEST_MEMBARRIER_FAIL; + ksft_exit_fail_msg( + "sys membarrier MEMBARRIER_CMD_QUERY invalid flags test: flags = %d. Should fail, but passed\n", + flags); } - return TEST_MEMBARRIER_PASS; + + ksft_test_result_pass( + "sys membarrier MEMBARRIER_CMD_QUERY invalid flags test: flags = %d. Failed as expected\n", + flags); + return 0; } -static enum test_membarrier_status test_membarrier_success(void) +static int test_membarrier_success(void) { int cmd = MEMBARRIER_CMD_SHARED, flags = 0; + const char *test_name = "sys membarrier MEMBARRIER_CMD_SHARED\n"; if (sys_membarrier(cmd, flags) != 0) { - printf("membarrier: Executing MEMBARRIER_CMD_SHARED failed. %s.\n", - strerror(errno)); - return TEST_MEMBARRIER_FAIL; + ksft_exit_fail_msg( + "sys membarrier MEMBARRIER_CMD_SHARED test: flags = %d\n", + flags); } - printf("membarrier: MEMBARRIER_CMD_SHARED success.\n"); - return TEST_MEMBARRIER_PASS; + ksft_test_result_pass( + "sys membarrier MEMBARRIER_CMD_SHARED test: flags = %d\n", + flags); + return 0; } -static enum test_membarrier_status test_membarrier(void) +static int test_membarrier(void) { - enum test_membarrier_status status; + int status; status = test_membarrier_cmd_fail(); if (status) @@ -67,52 +74,38 @@ static enum test_membarrier_status test_membarrier(void) status = test_membarrier_success(); if (status) return status; - return TEST_MEMBARRIER_PASS; + return 0; } -static enum test_membarrier_status test_membarrier_query(void) +static int test_membarrier_query(void) { int flags = 0, ret; - printf("membarrier MEMBARRIER_CMD_QUERY "); ret = sys_membarrier(MEMBARRIER_CMD_QUERY, flags); if (ret < 0) { - printf("failed. %s.\n", strerror(errno)); - switch (errno) { - case ENOSYS: + if (errno == ENOSYS) { /* * It is valid to build a kernel with * CONFIG_MEMBARRIER=n. However, this skips the tests. */ - return TEST_MEMBARRIER_SKIP; - case EINVAL: - default: - return TEST_MEMBARRIER_FAIL; + ksft_exit_skip( + "sys membarrier (CONFIG_MEMBARRIER) is disabled.\n"); } + ksft_exit_fail_msg("sys_membarrier() failed\n"); } - if (!(ret & MEMBARRIER_CMD_SHARED)) { - printf("command MEMBARRIER_CMD_SHARED is not supported.\n"); - return TEST_MEMBARRIER_FAIL; - } - printf("syscall available.\n"); - return TEST_MEMBARRIER_PASS; + if (!(ret & MEMBARRIER_CMD_SHARED)) + ksft_exit_fail_msg("sys_membarrier is not supported.\n"); + + ksft_test_result_pass("sys_membarrier available\n"); + return 0; } int main(int argc, char **argv) { - switch (test_membarrier_query()) { - case TEST_MEMBARRIER_FAIL: - return ksft_exit_fail(); - case TEST_MEMBARRIER_SKIP: - return ksft_exit_skip(); - } - switch (test_membarrier()) { - case TEST_MEMBARRIER_FAIL: - return ksft_exit_fail(); - case TEST_MEMBARRIER_SKIP: - return ksft_exit_skip(); - } + ksft_print_header(); + + test_membarrier_query(); + test_membarrier(); - printf("membarrier: tests done!\n"); - return ksft_exit_pass(); + ksft_exit_pass(); } diff --git a/tools/testing/selftests/memfd/Makefile b/tools/testing/selftests/memfd/Makefile index 79891d033de1..bc9d02d615da 100644 --- a/tools/testing/selftests/memfd/Makefile +++ b/tools/testing/selftests/memfd/Makefile @@ -3,11 +3,11 @@ 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) -fuse_mnt: LDFLAGS += $(shell pkg-config fuse --libs) include ../lib.mk +$(OUTPUT)/fuse_mnt: LDLIBS += $(shell pkg-config fuse --libs) 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); - - /* verify MFD_ALLOW_SEALING | MFD_CLOEXEC is allowed */ - fd = mfd_assert_new("", 0, MFD_ALLOW_SEALING | MFD_CLOEXEC); - 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); + } 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 100755 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/memory-hotplug/mem-on-off-test.sh b/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh index 6cddde0b96f8..35025ce9ca66 100755 --- a/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh +++ b/tools/testing/selftests/memory-hotplug/mem-on-off-test.sh @@ -22,6 +22,11 @@ prerequisite() echo $msg memory hotplug is not supported >&2 exit 0 fi + + if ! grep -q 1 $SYSFS/devices/system/memory/memory*/removable; then + echo $msg no hot-pluggable memory >&2 + exit 0 + fi } # @@ -39,7 +44,7 @@ hotpluggable_memory() done } -hotplaggable_offline_memory() +hotpluggable_offline_memory() { hotpluggable_memory offline } @@ -75,9 +80,12 @@ online_memory_expect_success() if ! online_memory $memory; then echo $FUNCNAME $memory: unexpected fail >&2 + return 1 elif ! memory_is_online $memory; then echo $FUNCNAME $memory: unexpected offline >&2 + return 1 fi + return 0 } online_memory_expect_fail() @@ -86,9 +94,12 @@ online_memory_expect_fail() if online_memory $memory 2> /dev/null; then echo $FUNCNAME $memory: unexpected success >&2 + return 1 elif ! memory_is_offline $memory; then echo $FUNCNAME $memory: unexpected online >&2 + return 1 fi + return 0 } offline_memory_expect_success() @@ -97,9 +108,12 @@ offline_memory_expect_success() if ! offline_memory $memory; then echo $FUNCNAME $memory: unexpected fail >&2 + return 1 elif ! memory_is_offline $memory; then echo $FUNCNAME $memory: unexpected offline >&2 + return 1 fi + return 0 } offline_memory_expect_fail() @@ -108,14 +122,18 @@ offline_memory_expect_fail() if offline_memory $memory 2> /dev/null; then echo $FUNCNAME $memory: unexpected success >&2 + return 1 elif ! memory_is_online $memory; then echo $FUNCNAME $memory: unexpected offline >&2 + return 1 fi + return 0 } error=-12 priority=0 ratio=10 +retval=0 while getopts e:hp:r: opt; do case $opt in @@ -131,6 +149,10 @@ while getopts e:hp:r: opt; do ;; r) ratio=$OPTARG + if [ "$ratio" -gt 100 ] || [ "$ratio" -lt 0 ]; then + echo "The percentage should be an integer within 0~100 range" + exit 1 + fi ;; esac done @@ -143,35 +165,58 @@ fi prerequisite echo "Test scope: $ratio% hotplug memory" -echo -e "\t online all hotplug memory in offline state" -echo -e "\t offline $ratio% hotplug memory in online state" -echo -e "\t online all hotplug memory in offline state" # # Online all hot-pluggable memory # -for memory in `hotplaggable_offline_memory`; do - echo offline-online $memory - online_memory_expect_success $memory -done +hotpluggable_num=`hotpluggable_offline_memory | wc -l` +echo -e "\t online all hot-pluggable memory in offline state:" +if [ "$hotpluggable_num" -gt 0 ]; then + for memory in `hotpluggable_offline_memory`; do + echo "offline->online memory$memory" + if ! online_memory_expect_success $memory; then + retval=1 + fi + done +else + echo -e "\t\t SKIPPED - no hot-pluggable memory in offline state" +fi # # Offline $ratio percent of hot-pluggable memory # +hotpluggable_num=`hotpluggable_online_memory | wc -l` +target=`echo "a=$hotpluggable_num*$ratio; if ( a%100 ) a/100+1 else a/100" | bc` +echo -e "\t offline $ratio% hot-pluggable memory in online state" +echo -e "\t trying to offline $target out of $hotpluggable_num memory block(s):" for memory in `hotpluggable_online_memory`; do - if [ $((RANDOM % 100)) -lt $ratio ]; then - echo online-offline $memory - offline_memory_expect_success $memory + if [ "$target" -gt 0 ]; then + echo "online->offline memory$memory" + if offline_memory_expect_success $memory; then + target=$(($target - 1)) + fi fi done +if [ "$target" -gt 0 ]; then + retval=1 + echo -e "\t\t FAILED - unable to offline some memory blocks, device busy?" +fi # # Online all hot-pluggable memory again # -for memory in `hotplaggable_offline_memory`; do - echo offline-online $memory - online_memory_expect_success $memory -done +hotpluggable_num=`hotpluggable_offline_memory | wc -l` +echo -e "\t online all hot-pluggable memory in offline state:" +if [ "$hotpluggable_num" -gt 0 ]; then + for memory in `hotpluggable_offline_memory`; do + echo "offline->online memory$memory" + if ! online_memory_expect_success $memory; then + retval=1 + fi + done +else + echo -e "\t\t SKIPPED - no hot-pluggable memory in offline state" +fi # # Test with memory notifier error injection @@ -189,15 +234,16 @@ prerequisite_extra() if [ ! -d "$DEBUGFS" ]; then echo $msg debugfs is not mounted >&2 - exit 0 + exit $retval fi if [ ! -d $NOTIFIER_ERR_INJECT_DIR ]; then echo $msg memory-notifier-error-inject module is not available >&2 - exit 0 + exit $retval fi } +echo -e "\t Test with memory notifier error injection" prerequisite_extra # @@ -214,7 +260,7 @@ done # Test memory hot-add error handling (offline => online) # echo $error > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error -for memory in `hotplaggable_offline_memory`; do +for memory in `hotpluggable_offline_memory`; do online_memory_expect_fail $memory done @@ -222,7 +268,7 @@ done # Online all hot-pluggable memory # echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_ONLINE/error -for memory in `hotplaggable_offline_memory`; do +for memory in `hotpluggable_offline_memory`; do online_memory_expect_success $memory done @@ -236,3 +282,5 @@ done echo 0 > $NOTIFIER_ERR_INJECT_DIR/actions/MEM_GOING_OFFLINE/error /sbin/modprobe -q -r memory-notifier-error-inject + +exit $retval diff --git a/tools/testing/selftests/mqueue/Makefile b/tools/testing/selftests/mqueue/Makefile index 79a664aeb8d7..152823b6cb21 100644 --- a/tools/testing/selftests/mqueue/Makefile +++ b/tools/testing/selftests/mqueue/Makefile @@ -5,8 +5,8 @@ TEST_GEN_PROGS := mq_open_tests mq_perf_tests include ../lib.mk override define RUN_TESTS - @./mq_open_tests /test1 || echo "selftests: mq_open_tests [FAIL]" - @./mq_perf_tests || echo "selftests: mq_perf_tests [FAIL]" + @$(OUTPUT)/mq_open_tests /test1 || echo "selftests: mq_open_tests [FAIL]" + @$(OUTPUT)/mq_perf_tests || echo "selftests: mq_perf_tests [FAIL]" endef override define EMIT_TESTS diff --git a/tools/testing/selftests/net/.gitignore b/tools/testing/selftests/net/.gitignore index afe109e5508a..c612d6e38c62 100644 --- a/tools/testing/selftests/net/.gitignore +++ b/tools/testing/selftests/net/.gitignore @@ -1,3 +1,4 @@ +msg_zerocopy socket psock_fanout psock_tpacket @@ -5,3 +6,4 @@ reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa reuseport_dualstack +reuseaddr_conflict diff --git a/tools/testing/selftests/net/Makefile b/tools/testing/selftests/net/Makefile index 35cbb4cba410..d86bca991f45 100644 --- a/tools/testing/selftests/net/Makefile +++ b/tools/testing/selftests/net/Makefile @@ -3,13 +3,12 @@ CFLAGS = -Wall -Wl,--no-as-needed -O2 -g CFLAGS += -I../../../../usr/include/ -reuseport_bpf_numa: LDFLAGS += -lnuma - -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 += psock_fanout psock_tpacket msg_zerocopy +TEST_GEN_PROGS = reuseport_bpf reuseport_bpf_cpu reuseport_bpf_numa +TEST_GEN_PROGS += reuseport_dualstack reuseaddr_conflict include ../lib.mk +$(OUTPUT)/reuseport_bpf_numa: LDFLAGS += -lnuma diff --git a/tools/testing/selftests/net/msg_zerocopy.c b/tools/testing/selftests/net/msg_zerocopy.c new file mode 100644 index 000000000000..3ab6ec403905 --- /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 5 +#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/netdevice.sh b/tools/testing/selftests/net/netdevice.sh index 4e00568d70c2..90cb903c3381 100755 --- a/tools/testing/selftests/net/netdevice.sh +++ b/tools/testing/selftests/net/netdevice.sh @@ -178,7 +178,7 @@ if [ "$(id -u)" -ne 0 ];then exit 0 fi -ip -Version 2>/dev/null >/dev/null +ip link show 2>/dev/null >/dev/null if [ $? -ne 0 ];then echo "SKIP: Could not run test without the ip tool" exit 0 diff --git a/tools/testing/selftests/net/reuseaddr_conflict.c b/tools/testing/selftests/net/reuseaddr_conflict.c new file mode 100644 index 000000000000..7c5b12664b03 --- /dev/null +++ b/tools/testing/selftests/net/reuseaddr_conflict.c @@ -0,0 +1,114 @@ +/* + * Test for the regression introduced by + * + * b9470c27607b ("inet: kill smallest_size and smallest_port") + * + * If we open an ipv4 socket on a port with reuseaddr we shouldn't reset the tb + * when we open the ipv6 conterpart, which is what was happening previously. + */ +#include <errno.h> +#include <error.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <stdbool.h> +#include <stdio.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> + +#define PORT 9999 + +int open_port(int ipv6, int any) +{ + int fd = -1; + int reuseaddr = 1; + int v6only = 1; + int addrlen; + int ret = -1; + struct sockaddr *addr; + int family = ipv6 ? AF_INET6 : AF_INET; + + struct sockaddr_in6 addr6 = { + .sin6_family = AF_INET6, + .sin6_port = htons(PORT), + .sin6_addr = in6addr_any + }; + struct sockaddr_in addr4 = { + .sin_family = AF_INET, + .sin_port = htons(PORT), + .sin_addr.s_addr = any ? htonl(INADDR_ANY) : inet_addr("127.0.0.1"), + }; + + + if (ipv6) { + addr = (struct sockaddr*)&addr6; + addrlen = sizeof(addr6); + } else { + addr = (struct sockaddr*)&addr4; + addrlen = sizeof(addr4); + } + + if ((fd = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0) { + perror("socket"); + goto out; + } + + if (ipv6 && setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&v6only, + sizeof(v6only)) < 0) { + perror("setsockopt IPV6_V6ONLY"); + goto out; + } + + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, + sizeof(reuseaddr)) < 0) { + perror("setsockopt SO_REUSEADDR"); + goto out; + } + + if (bind(fd, addr, addrlen) < 0) { + perror("bind"); + goto out; + } + + if (any) + return fd; + + if (listen(fd, 1) < 0) { + perror("listen"); + goto out; + } + return fd; +out: + close(fd); + return ret; +} + +int main(void) +{ + int listenfd; + int fd1, fd2; + + fprintf(stderr, "Opening 127.0.0.1:%d\n", PORT); + listenfd = open_port(0, 0); + if (listenfd < 0) + error(1, errno, "Couldn't open listen socket"); + fprintf(stderr, "Opening INADDR_ANY:%d\n", PORT); + fd1 = open_port(0, 1); + if (fd1 >= 0) + error(1, 0, "Was allowed to create an ipv4 reuseport on a already bound non-reuseport socket"); + fprintf(stderr, "Opening in6addr_any:%d\n", PORT); + fd1 = open_port(1, 1); + if (fd1 < 0) + error(1, errno, "Couldn't open ipv6 reuseport"); + fprintf(stderr, "Opening INADDR_ANY:%d\n", PORT); + fd2 = open_port(0, 1); + if (fd2 >= 0) + error(1, 0, "Was allowed to create an ipv4 reuseport on a already bound non-reuseport socket"); + close(fd1); + fprintf(stderr, "Opening INADDR_ANY:%d after closing ipv6 socket\n", PORT); + fd1 = open_port(0, 1); + if (fd1 >= 0) + error(1, 0, "Was allowed to create an ipv4 reuseport on an already bound non-reuseport socket with no ipv6"); + fprintf(stderr, "Success"); + return 0; +} 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..dd4162fc0419 --- /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 13f5198ba0ee..5fc7ad359e21 100755 --- a/tools/testing/selftests/ntb/ntb_test.sh +++ b/tools/testing/selftests/ntb/ntb_test.sh @@ -18,6 +18,7 @@ LIST_DEVS=FALSE DEBUGFS=${DEBUGFS-/sys/kernel/debug} +DB_BITMASK=0x7FFF PERF_RUN_ORDER=32 MAX_MW_SIZE=0 RUN_DMA_TESTS= @@ -38,6 +39,7 @@ function show_help() echo "be highly recommended." echo echo "Options:" + echo " -b BITMASK doorbell clear bitmask for ntb_tool" echo " -C don't cleanup ntb modules on exit" echo " -d run dma tests" echo " -h show this help message" @@ -52,8 +54,9 @@ function show_help() function parse_args() { OPTIND=0 - while getopts "Cdhlm:r:p:w:" opt; do + while getopts "b:Cdhlm:r:p:w:" opt; do case "$opt" in + b) DB_BITMASK=${OPTARG} ;; C) DONT_CLEANUP=1 ;; d) RUN_DMA_TESTS=1 ;; h) show_help; exit 0 ;; @@ -85,6 +88,10 @@ set -e function _modprobe() { modprobe "$@" + + if [[ "$REMOTE_HOST" != "" ]]; then + ssh "$REMOTE_HOST" modprobe "$@" + fi } function split_remote() @@ -154,7 +161,7 @@ function doorbell_test() echo "Running db tests on: $(basename $LOC) / $(basename $REM)" - write_file "c 0xFFFFFFFF" "$REM/db" + write_file "c $DB_BITMASK" "$REM/db" for ((i=1; i <= 8; i++)); do let DB=$(read_file "$REM/db") || true @@ -326,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/benchmarks/context_switch.c b/tools/testing/selftests/powerpc/benchmarks/context_switch.c index 778f5fbfd784..f4241339edd2 100644 --- a/tools/testing/selftests/powerpc/benchmarks/context_switch.c +++ b/tools/testing/selftests/powerpc/benchmarks/context_switch.c @@ -258,9 +258,14 @@ static unsigned long xchg(unsigned long *p, unsigned long val) return __atomic_exchange_n(p, val, __ATOMIC_SEQ_CST); } +static int processes; + static int mutex_lock(unsigned long *m) { int c; + int flags = FUTEX_WAIT; + if (!processes) + flags |= FUTEX_PRIVATE_FLAG; c = cmpxchg(m, 0, 1); if (!c) @@ -270,7 +275,7 @@ static int mutex_lock(unsigned long *m) c = xchg(m, 2); while (c) { - sys_futex(m, FUTEX_WAIT, 2, NULL, NULL, 0); + sys_futex(m, flags, 2, NULL, NULL, 0); c = xchg(m, 2); } @@ -279,12 +284,16 @@ static int mutex_lock(unsigned long *m) static int mutex_unlock(unsigned long *m) { + int flags = FUTEX_WAKE; + if (!processes) + flags |= FUTEX_PRIVATE_FLAG; + if (*m == 2) *m = 0; else if (xchg(m, 0) == 1) return 0; - sys_futex(m, FUTEX_WAKE, 1, NULL, NULL, 0); + sys_futex(m, flags, 1, NULL, NULL, 0); return 0; } @@ -293,26 +302,32 @@ static unsigned long *m1, *m2; static void futex_setup(int cpu1, int cpu2) { - int shmid; - void *shmaddr; + if (!processes) { + static unsigned long _m1, _m2; + m1 = &_m1; + m2 = &_m2; + } else { + int shmid; + void *shmaddr; - shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W); - if (shmid < 0) { - perror("shmget"); - exit(1); - } + shmid = shmget(IPC_PRIVATE, getpagesize(), SHM_R | SHM_W); + if (shmid < 0) { + perror("shmget"); + exit(1); + } - shmaddr = shmat(shmid, NULL, 0); - if (shmaddr == (char *)-1) { - perror("shmat"); - shmctl(shmid, IPC_RMID, NULL); - exit(1); - } + shmaddr = shmat(shmid, NULL, 0); + if (shmaddr == (char *)-1) { + perror("shmat"); + shmctl(shmid, IPC_RMID, NULL); + exit(1); + } - shmctl(shmid, IPC_RMID, NULL); + shmctl(shmid, IPC_RMID, NULL); - m1 = shmaddr; - m2 = shmaddr + sizeof(*m1); + m1 = shmaddr; + m2 = shmaddr + sizeof(*m1); + } *m1 = 0; *m2 = 0; @@ -352,8 +367,6 @@ static struct actions futex_actions = { .thread2 = futex_thread2, }; -static int processes; - static struct option options[] = { { "test", required_argument, 0, 't' }, { "process", no_argument, &processes, 1 }, 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/configcheck.sh b/tools/testing/selftests/rcutorture/bin/configcheck.sh index eee31e261bf7..70fca318a82b 100755 --- a/tools/testing/selftests/rcutorture/bin/configcheck.sh +++ b/tools/testing/selftests/rcutorture/bin/configcheck.sh @@ -27,7 +27,7 @@ cat $1 > $T/.config cat $2 | sed -e 's/\(.*\)=n/# \1 is not set/' -e 's/^#CHECK#//' | awk ' -BEGIN { +{ print "if grep -q \"" $0 "\" < '"$T/.config"'"; print "then"; print "\t:"; 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 00cb0db2643d..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 3b3c1b693ee1..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 @@ -296,10 +305,7 @@ if test -d .git then git status >> $resdir/$ds/testid.txt git rev-parse HEAD >> $resdir/$ds/testid.txt - if ! git diff HEAD > $T/git-diff 2>&1 - then - cp $T/git-diff $resdir/$ds - fi + git diff HEAD >> $resdir/$ds/testid.txt fi ___EOF___ awk < $T/cfgcpu.pack \ @@ -327,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 @@ -362,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; @@ -380,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/CFLIST b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST index a3a1a05a2b5c..6a0b9f69faad 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/CFLIST +++ b/tools/testing/selftests/rcutorture/configs/rcu/CFLIST @@ -9,6 +9,8 @@ TREE08 TREE09 SRCU-N SRCU-P +SRCU-t +SRCU-u TINY01 TINY02 TASKS01 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N index 1a087c3c8bb8..2da8b49589a0 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N @@ -5,4 +5,4 @@ CONFIG_HOTPLUG_CPU=y CONFIG_PREEMPT_NONE=y CONFIG_PREEMPT_VOLUNTARY=n CONFIG_PREEMPT=n -CONFIG_RCU_EXPERT=y +#CHECK#CONFIG_RCU_EXPERT=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P index 4837430a71c0..ab7ccd38232b 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P @@ -2,7 +2,11 @@ CONFIG_RCU_TRACE=n CONFIG_SMP=y CONFIG_NR_CPUS=8 CONFIG_HOTPLUG_CPU=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_FANOUT=2 +CONFIG_RCU_FANOUT_LEAF=2 CONFIG_PREEMPT_NONE=n CONFIG_PREEMPT_VOLUNTARY=n CONFIG_PREEMPT=y -#CHECK#CONFIG_RCU_EXPERT=n +CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_PROVE_LOCKING=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-t b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-t new file mode 100644 index 000000000000..6c78022c8cd8 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-t @@ -0,0 +1,10 @@ +CONFIG_SMP=n +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=n +CONFIG_PREEMPT=n +#CHECK#CONFIG_TINY_SRCU=y +CONFIG_RCU_TRACE=n +CONFIG_DEBUG_LOCK_ALLOC=n +CONFIG_DEBUG_OBJECTS_RCU_HEAD=n +CONFIG_DEBUG_ATOMIC_SLEEP=y +#CHECK#CONFIG_PREEMPT_COUNT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-t.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-t.boot new file mode 100644 index 000000000000..238bfe3bd0cc --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-t.boot @@ -0,0 +1 @@ +rcutorture.torture_type=srcu diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u new file mode 100644 index 000000000000..c15ada821e45 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u @@ -0,0 +1,10 @@ +CONFIG_SMP=n +CONFIG_PREEMPT_NONE=y +CONFIG_PREEMPT_VOLUNTARY=n +CONFIG_PREEMPT=n +#CHECK#CONFIG_TINY_SRCU=y +CONFIG_RCU_TRACE=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/SRCU-u.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u.boot new file mode 100644 index 000000000000..84a7d51b7481 --- /dev/null +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-u.boot @@ -0,0 +1 @@ +rcutorture.torture_type=srcud diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TINY02 b/tools/testing/selftests/rcutorture/configs/rcu/TINY02 index a59f7686e219..d8674264318d 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TINY02 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TINY02 @@ -6,10 +6,9 @@ CONFIG_PREEMPT=n CONFIG_HZ_PERIODIC=y CONFIG_NO_HZ_IDLE=n CONFIG_NO_HZ_FULL=n -CONFIG_RCU_TRACE=y CONFIG_PROVE_LOCKING=y -CONFIG_PROVE_RCU_REPEATEDLY=y #CHECK#CONFIG_PROVE_RCU=y CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y -CONFIG_PREEMPT_COUNT=y +CONFIG_DEBUG_ATOMIC_SLEEP=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 b/tools/testing/selftests/rcutorture/configs/rcu/TREE01 index 359cb258f639..b5b53973c01e 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01 @@ -10,12 +10,9 @@ CONFIG_RCU_FAST_NO_HZ=y CONFIG_RCU_TRACE=y CONFIG_HOTPLUG_CPU=y CONFIG_MAXSMP=y +CONFIG_CPUMASK_OFFSTACK=y CONFIG_RCU_NOCB_CPU=y -CONFIG_RCU_NOCB_CPU_ZERO=y CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_RCU_BOOST=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot index adc3abc82fb8..9f3a4d28e508 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE01.boot @@ -1 +1,5 @@ -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 +rcu_nocbs=0 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02 b/tools/testing/selftests/rcutorture/configs/rcu/TREE02 index c1ab5926568b..35e639e39366 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE02 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE02 @@ -18,9 +18,6 @@ CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=n CONFIG_RCU_BOOST=n -CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y +CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 b/tools/testing/selftests/rcutorture/configs/rcu/TREE03 index 3b93ee544e70..2dc31b16e506 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03 @@ -14,9 +14,5 @@ CONFIG_RCU_FANOUT_LEAF=2 CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_RCU_BOOST=y -CONFIG_RCU_KTHREAD_PRIO=2 CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot index 120c0c88d100..5d2cc0bd50a0 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE03.boot @@ -1 +1,5 @@ rcutorture.onoff_interval=1 rcutorture.onoff_holdoff=30 +rcutree.gp_preinit_delay=3 +rcutree.gp_init_delay=3 +rcutree.gp_cleanup_delay=3 +rcutree.kthread_prio=2 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 b/tools/testing/selftests/rcutorture/configs/rcu/TREE04 index 5af758e783c7..27d22695d64c 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE04 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE04 @@ -15,11 +15,7 @@ CONFIG_SUSPEND=n CONFIG_HIBERNATION=n CONFIG_RCU_FANOUT=4 CONFIG_RCU_FANOUT_LEAF=3 -CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y CONFIG_RCU_EQS_DEBUG=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE05 b/tools/testing/selftests/rcutorture/configs/rcu/TREE05 index d4cdc0d74e16..2dde0d9964e3 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE05 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE05 @@ -13,12 +13,8 @@ CONFIG_HOTPLUG_CPU=y CONFIG_RCU_FANOUT=6 CONFIG_RCU_FANOUT_LEAF=6 CONFIG_RCU_NOCB_CPU=y -CONFIG_RCU_NOCB_CPU_NONE=y CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y #CHECK#CONFIG_PROVE_RCU=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot index 15b3e1a86f74..c7fd050dfcd9 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE05.boot @@ -1,2 +1,5 @@ rcutorture.torture_type=sched rcupdate.rcu_self_test_sched=1 +rcutree.gp_preinit_delay=3 +rcutree.gp_init_delay=3 +rcutree.gp_cleanup_delay=3 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE06 b/tools/testing/selftests/rcutorture/configs/rcu/TREE06 index 4cb02bd28f08..05a4eec3f27b 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE06 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE06 @@ -18,8 +18,6 @@ CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=y CONFIG_PROVE_LOCKING=y #CHECK#CONFIG_PROVE_RCU=y +CONFIG_DEBUG_OBJECTS=y CONFIG_DEBUG_OBJECTS_RCU_HEAD=y CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot index dd90f28ed700..ad18b52a2cad 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE06.boot @@ -2,3 +2,6 @@ rcupdate.rcu_self_test=1 rcupdate.rcu_self_test_bh=1 rcupdate.rcu_self_test_sched=1 rcutree.rcu_fanout_exact=1 +rcutree.gp_preinit_delay=3 +rcutree.gp_init_delay=3 +rcutree.gp_cleanup_delay=3 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 b/tools/testing/selftests/rcutorture/configs/rcu/TREE07 index b12a3ea1867e..0f4759f4232e 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE07 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE07 @@ -1,6 +1,5 @@ CONFIG_SMP=y CONFIG_NR_CPUS=16 -CONFIG_CPUMASK_OFFSTACK=y CONFIG_PREEMPT_NONE=y CONFIG_PREEMPT_VOLUNTARY=n CONFIG_PREEMPT=n @@ -9,16 +8,11 @@ CONFIG_HZ_PERIODIC=n CONFIG_NO_HZ_IDLE=n CONFIG_NO_HZ_FULL=y CONFIG_NO_HZ_FULL_ALL=n -CONFIG_NO_HZ_FULL_SYSIDLE=y CONFIG_RCU_FAST_NO_HZ=n CONFIG_RCU_TRACE=y CONFIG_HOTPLUG_CPU=y CONFIG_RCU_FANOUT=2 CONFIG_RCU_FANOUT_LEAF=2 -CONFIG_RCU_NOCB_CPU=n CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n CONFIG_RCU_EXPERT=y -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP=y -CONFIG_RCU_TORTURE_TEST_SLOW_INIT=y -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT=y diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08 b/tools/testing/selftests/rcutorture/configs/rcu/TREE08 index 099cc63c6a3b..fb1c763c10c5 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08 @@ -15,7 +15,6 @@ CONFIG_HIBERNATION=n CONFIG_RCU_FANOUT=3 CONFIG_RCU_FANOUT_LEAF=2 CONFIG_RCU_NOCB_CPU=y -CONFIG_RCU_NOCB_CPU_ALL=y CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_PROVE_LOCKING=n CONFIG_RCU_BOOST=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T b/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T deleted file mode 100644 index 2ad13f0d29cc..000000000000 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08-T +++ /dev/null @@ -1,21 +0,0 @@ -CONFIG_SMP=y -CONFIG_NR_CPUS=16 -CONFIG_PREEMPT_NONE=n -CONFIG_PREEMPT_VOLUNTARY=n -CONFIG_PREEMPT=y -#CHECK#CONFIG_PREEMPT_RCU=y -CONFIG_HZ_PERIODIC=n -CONFIG_NO_HZ_IDLE=y -CONFIG_NO_HZ_FULL=n -CONFIG_RCU_FAST_NO_HZ=n -CONFIG_RCU_TRACE=y -CONFIG_HOTPLUG_CPU=n -CONFIG_SUSPEND=n -CONFIG_HIBERNATION=n -CONFIG_RCU_FANOUT=3 -CONFIG_RCU_FANOUT_LEAF=2 -CONFIG_RCU_NOCB_CPU=y -CONFIG_RCU_NOCB_CPU_ALL=y -CONFIG_DEBUG_LOCK_ALLOC=n -CONFIG_RCU_BOOST=n -CONFIG_DEBUG_OBJECTS_RCU_HEAD=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot b/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot index fb066dc82769..1bd8efc4141e 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/TREE08.boot @@ -2,3 +2,4 @@ rcutorture.torture_type=sched rcupdate.rcu_self_test=1 rcupdate.rcu_self_test_sched=1 rcutree.rcu_fanout_exact=1 +rcu_nocbs=0-7 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T b/tools/testing/selftests/rcutorture/configs/rcuperf/TINY index 917d2517b5b5..fb05ef5279b4 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TREE02-T +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/TINY @@ -1,21 +1,16 @@ -CONFIG_SMP=y -CONFIG_NR_CPUS=8 -CONFIG_PREEMPT_NONE=n +CONFIG_SMP=n +CONFIG_PREEMPT_NONE=y CONFIG_PREEMPT_VOLUNTARY=n -CONFIG_PREEMPT=y -#CHECK#CONFIG_PREEMPT_RCU=y +CONFIG_PREEMPT=n +#CHECK#CONFIG_TINY_RCU=y CONFIG_HZ_PERIODIC=n CONFIG_NO_HZ_IDLE=y CONFIG_NO_HZ_FULL=n CONFIG_RCU_FAST_NO_HZ=n -CONFIG_RCU_TRACE=y -CONFIG_HOTPLUG_CPU=n -CONFIG_SUSPEND=n -CONFIG_HIBERNATION=n -CONFIG_RCU_FANOUT=3 -CONFIG_RCU_FANOUT_LEAF=3 CONFIG_RCU_NOCB_CPU=n -CONFIG_DEBUG_LOCK_ALLOC=y +CONFIG_DEBUG_LOCK_ALLOC=n CONFIG_PROVE_LOCKING=n CONFIG_RCU_BOOST=n CONFIG_DEBUG_OBJECTS_RCU_HEAD=n +CONFIG_RCU_EXPERT=y +CONFIG_RCU_TRACE=y diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/TREE b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE index a312f671a29a..721cfda76ab2 100644 --- a/tools/testing/selftests/rcutorture/configs/rcuperf/TREE +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE @@ -7,7 +7,6 @@ CONFIG_HZ_PERIODIC=n CONFIG_NO_HZ_IDLE=y CONFIG_NO_HZ_FULL=n CONFIG_RCU_FAST_NO_HZ=n -CONFIG_RCU_TRACE=n CONFIG_HOTPLUG_CPU=n CONFIG_SUSPEND=n CONFIG_HIBERNATION=n diff --git a/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 index 985fb170d13c..7629f5dd73b2 100644 --- a/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 +++ b/tools/testing/selftests/rcutorture/configs/rcuperf/TREE54 @@ -8,7 +8,6 @@ CONFIG_HZ_PERIODIC=n CONFIG_NO_HZ_IDLE=y CONFIG_NO_HZ_FULL=n CONFIG_RCU_FAST_NO_HZ=n -CONFIG_RCU_TRACE=n CONFIG_HOTPLUG_CPU=n CONFIG_SUSPEND=n CONFIG_HIBERNATION=n diff --git a/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt b/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt index 24396ae8355b..a75b16991a92 100644 --- a/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt +++ b/tools/testing/selftests/rcutorture/doc/TINY_RCU.txt @@ -18,7 +18,6 @@ CONFIG_PROVE_RCU In common code tested by TREE_RCU test cases. -CONFIG_NO_HZ_FULL_SYSIDLE CONFIG_RCU_NOCB_CPU Meaningless for TINY_RCU. diff --git a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt index 364801b1a230..af6fca03602f 100644 --- a/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt +++ b/tools/testing/selftests/rcutorture/doc/TREE_RCU-kconfig.txt @@ -9,28 +9,20 @@ CONFIG_DEBUG_OBJECTS_RCU_HEAD -- Do one. CONFIG_HOTPLUG_CPU -- Do half. (Every second.) CONFIG_HZ_PERIODIC -- Do one. CONFIG_NO_HZ_IDLE -- Do those not otherwise specified. (Groups of two.) -CONFIG_NO_HZ_FULL -- Do two, one with CONFIG_NO_HZ_FULL_SYSIDLE. -CONFIG_NO_HZ_FULL_SYSIDLE -- Do one. +CONFIG_NO_HZ_FULL -- Do two, one with partial CPU enablement. CONFIG_PREEMPT -- Do half. (First three and #8.) CONFIG_PROVE_LOCKING -- Do several, covering CONFIG_DEBUG_LOCK_ALLOC=y and not. CONFIG_PROVE_RCU -- Hardwired to CONFIG_PROVE_LOCKING. -CONFIG_PROVE_RCU_REPEATEDLY -- Do one. CONFIG_RCU_BOOST -- one of PREEMPT_RCU. -CONFIG_RCU_KTHREAD_PRIO -- set to 2 for _BOOST testing. CONFIG_RCU_FANOUT -- Cover hierarchy, but overlap with others. CONFIG_RCU_FANOUT_LEAF -- Do one non-default. -CONFIG_RCU_FAST_NO_HZ -- Do one, but not with CONFIG_RCU_NOCB_CPU_ALL. -CONFIG_RCU_NOCB_CPU -- Do three, see below. -CONFIG_RCU_NOCB_CPU_ALL -- Do one. -CONFIG_RCU_NOCB_CPU_NONE -- Do one. -CONFIG_RCU_NOCB_CPU_ZERO -- Do one. +CONFIG_RCU_FAST_NO_HZ -- Do one, but not with all nohz_full CPUs. +CONFIG_RCU_NOCB_CPU -- Do three, one with no rcu_nocbs CPUs, one with + rcu_nocbs=0, and one with all rcu_nocbs CPUs. CONFIG_RCU_TRACE -- Do half. CONFIG_SMP -- Need one !SMP for PREEMPT_RCU. CONFIG_RCU_EXPERT=n -- Do a few, but these have to be vanilla configurations. CONFIG_RCU_EQS_DEBUG -- Do at least one for CONFIG_NO_HZ_FULL and not. -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP -- Do for all but a couple TREE scenarios. -CONFIG_RCU_TORTURE_TEST_SLOW_INIT -- Do for all but a couple TREE scenarios. -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT -- Do for all but a couple TREE scenarios. RCU-bh: Do one with PREEMPT and one with !PREEMPT. RCU-sched: Do one with PREEMPT but not BOOST. @@ -52,10 +44,6 @@ CONFIG_64BIT Used only to check CONFIG_RCU_FANOUT value, inspection suffices. -CONFIG_NO_HZ_FULL_SYSIDLE_SMALL - - Defer until Frederic uses this. - CONFIG_PREEMPT_COUNT CONFIG_PREEMPT_RCU @@ -78,30 +66,16 @@ CONFIG_RCU_TORTURE_TEST_RUNNABLE Always used in KVM testing. -CONFIG_RCU_TORTURE_TEST_SLOW_PREINIT_DELAY -CONFIG_RCU_TORTURE_TEST_SLOW_INIT_DELAY -CONFIG_RCU_TORTURE_TEST_SLOW_CLEANUP_DELAY - - Inspection suffices, ignore. - CONFIG_PREEMPT_RCU CONFIG_TREE_RCU CONFIG_TINY_RCU +CONFIG_TASKS_RCU These are controlled by CONFIG_PREEMPT and/or CONFIG_SMP. -CONFIG_SPARSE_RCU_POINTER - - Makes sense only for sparse runs, not for kernel builds. - CONFIG_SRCU -CONFIG_TASKS_RCU Selected by CONFIG_RCU_TORTURE_TEST, so cannot disable. -CONFIG_RCU_TRACE - - Implied by CONFIG_RCU_TRACE for Tree RCU. - boot parameters ignored: TBD diff --git a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk index 8ff89043d0a9..c9e8bc5082a7 100755 --- a/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk +++ b/tools/testing/selftests/rcutorture/formal/srcu-cbmc/modify_srcu.awk @@ -1,4 +1,4 @@ -#!/bin/awk -f +#!/usr/bin/awk -f # Modify SRCU for formal verification. The first argument should be srcu.h and # the second should be srcu.c. Outputs modified srcu.h and srcu.c into the diff --git a/tools/testing/selftests/seccomp/Makefile b/tools/testing/selftests/seccomp/Makefile index 5fa6fd2246b1..553d870b4ca9 100644 --- a/tools/testing/selftests/seccomp/Makefile +++ b/tools/testing/selftests/seccomp/Makefile @@ -1,6 +1,16 @@ -TEST_GEN_PROGS := seccomp_bpf -CFLAGS += -Wl,-no-as-needed -Wall -LDFLAGS += -lpthread +all: include ../lib.mk +.PHONY: all clean + +BINARIES := seccomp_bpf seccomp_benchmark +CFLAGS += -Wl,-no-as-needed -Wall + +seccomp_bpf: seccomp_bpf.c ../kselftest_harness.h + $(CC) $(CFLAGS) $(LDFLAGS) -lpthread $< -o $@ + +TEST_PROGS += $(BINARIES) +EXTRA_CLEAN := $(BINARIES) + +all: $(BINARIES) diff --git a/tools/testing/selftests/seccomp/seccomp_benchmark.c b/tools/testing/selftests/seccomp/seccomp_benchmark.c new file mode 100644 index 000000000000..5838c8697ec3 --- /dev/null +++ b/tools/testing/selftests/seccomp/seccomp_benchmark.c @@ -0,0 +1,99 @@ +/* + * Strictly speaking, this is not a test. But it can report during test + * runs so relative performace can be measured. + */ +#define _GNU_SOURCE +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <linux/filter.h> +#include <linux/seccomp.h> +#include <sys/prctl.h> +#include <sys/syscall.h> +#include <sys/types.h> + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +unsigned long long timing(clockid_t clk_id, unsigned long long samples) +{ + pid_t pid, ret; + unsigned long long i; + struct timespec start, finish; + + pid = getpid(); + assert(clock_gettime(clk_id, &start) == 0); + for (i = 0; i < samples; i++) { + ret = syscall(__NR_getpid); + assert(pid == ret); + } + assert(clock_gettime(clk_id, &finish) == 0); + + i = finish.tv_sec - start.tv_sec; + i *= 1000000000; + i += finish.tv_nsec - start.tv_nsec; + + printf("%lu.%09lu - %lu.%09lu = %llu\n", + finish.tv_sec, finish.tv_nsec, + start.tv_sec, start.tv_nsec, + i); + + return i; +} + +unsigned long long calibrate(void) +{ + unsigned long long i; + + printf("Calibrating reasonable sample size...\n"); + + for (i = 5; ; i++) { + unsigned long long samples = 1 << i; + + /* Find something that takes more than 5 seconds to run. */ + if (timing(CLOCK_REALTIME, samples) / 1000000000ULL > 5) + return samples; + } +} + +int main(int argc, char *argv[]) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + long ret; + unsigned long long samples; + unsigned long long native, filtered; + + if (argc > 1) + samples = strtoull(argv[1], NULL, 0); + else + samples = calibrate(); + + printf("Benchmarking %llu samples...\n", samples); + + native = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; + printf("getpid native: %llu ns\n", native); + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + assert(ret == 0); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); + assert(ret == 0); + + filtered = timing(CLOCK_PROCESS_CPUTIME_ID, samples) / samples; + printf("getpid RET_ALLOW: %llu ns\n", filtered); + + printf("Estimated seccomp overhead per syscall: %llu ns\n", + filtered - native); + + if (filtered == native) + printf("Trying running again with more samples.\n"); + + return 0; +} diff --git a/tools/testing/selftests/seccomp/seccomp_bpf.c b/tools/testing/selftests/seccomp/seccomp_bpf.c index 03f1fa495d74..24dbf634e2dd 100644 --- a/tools/testing/selftests/seccomp/seccomp_bpf.c +++ b/tools/testing/selftests/seccomp/seccomp_bpf.c @@ -6,10 +6,18 @@ */ #include <sys/types.h> -#include <asm/siginfo.h> -#define __have_siginfo_t 1 -#define __have_sigval_t 1 -#define __have_sigevent_t 1 + +/* + * glibc 2.26 and later have SIGSYS in siginfo_t. Before that, + * we need to use the kernel's siginfo.h file and trick glibc + * into accepting it. + */ +#if !__GLIBC_PREREQ(2, 26) +# include <asm/siginfo.h> +# define __have_siginfo_t 1 +# define __have_sigval_t 1 +# define __have_sigevent_t 1 +#endif #include <errno.h> #include <linux/filter.h> @@ -37,7 +45,7 @@ #include <unistd.h> #include <sys/syscall.h> -#include "test_harness.h" +#include "../kselftest_harness.h" #ifndef PR_SET_PTRACER # define PR_SET_PTRACER 0x59616d61 @@ -68,17 +76,7 @@ #define SECCOMP_MODE_FILTER 2 #endif -#ifndef SECCOMP_RET_KILL -#define SECCOMP_RET_KILL 0x00000000U /* kill the task immediately */ -#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ -#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ -#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ -#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ - -/* Masks for the return value sections. */ -#define SECCOMP_RET_ACTION 0x7fff0000U -#define SECCOMP_RET_DATA 0x0000ffffU - +#ifndef SECCOMP_RET_ALLOW struct seccomp_data { int nr; __u32 arch; @@ -87,6 +85,70 @@ struct seccomp_data { }; #endif +#ifndef SECCOMP_RET_KILL_PROCESS +#define SECCOMP_RET_KILL_PROCESS 0x80000000U /* kill the process */ +#define SECCOMP_RET_KILL_THREAD 0x00000000U /* kill the thread */ +#endif +#ifndef SECCOMP_RET_KILL +#define SECCOMP_RET_KILL SECCOMP_RET_KILL_THREAD +#define SECCOMP_RET_TRAP 0x00030000U /* disallow and force a SIGSYS */ +#define SECCOMP_RET_ERRNO 0x00050000U /* returns an errno */ +#define SECCOMP_RET_TRACE 0x7ff00000U /* pass to a tracer or disallow */ +#define SECCOMP_RET_ALLOW 0x7fff0000U /* allow */ +#endif +#ifndef SECCOMP_RET_LOG +#define SECCOMP_RET_LOG 0x7ffc0000U /* allow after logging */ +#endif + +#ifndef __NR_seccomp +# if defined(__i386__) +# define __NR_seccomp 354 +# elif defined(__x86_64__) +# define __NR_seccomp 317 +# elif defined(__arm__) +# define __NR_seccomp 383 +# elif defined(__aarch64__) +# define __NR_seccomp 277 +# elif defined(__hppa__) +# define __NR_seccomp 338 +# elif defined(__powerpc__) +# define __NR_seccomp 358 +# elif defined(__s390__) +# define __NR_seccomp 348 +# else +# warning "seccomp syscall number unknown for this architecture" +# define __NR_seccomp 0xffff +# endif +#endif + +#ifndef SECCOMP_SET_MODE_STRICT +#define SECCOMP_SET_MODE_STRICT 0 +#endif + +#ifndef SECCOMP_SET_MODE_FILTER +#define SECCOMP_SET_MODE_FILTER 1 +#endif + +#ifndef SECCOMP_GET_ACTION_AVAIL +#define SECCOMP_GET_ACTION_AVAIL 2 +#endif + +#ifndef SECCOMP_FILTER_FLAG_TSYNC +#define SECCOMP_FILTER_FLAG_TSYNC 1 +#endif + +#ifndef SECCOMP_FILTER_FLAG_LOG +#define SECCOMP_FILTER_FLAG_LOG 2 +#endif + +#ifndef seccomp +int seccomp(unsigned int op, unsigned int flags, void *args) +{ + errno = 0; + return syscall(__NR_seccomp, op, flags, args); +} +#endif + #if __BYTE_ORDER == __LITTLE_ENDIAN #define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n])) #elif __BYTE_ORDER == __BIG_ENDIAN @@ -107,7 +169,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) @@ -136,7 +198,7 @@ TEST(no_new_privs_support) } } -/* Tests kernel support by checking for a copy_from_user() fault on * NULL. */ +/* Tests kernel support by checking for a copy_from_user() fault on NULL. */ TEST(mode_filter_support) { long ret; @@ -342,6 +404,28 @@ TEST(empty_prog) EXPECT_EQ(EINVAL, errno); } +TEST(log_all) +{ + struct sock_filter filter[] = { + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG), + }; + struct sock_fprog prog = { + .len = (unsigned short)ARRAY_SIZE(filter), + .filter = filter, + }; + long ret; + pid_t parent = getppid(); + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); + ASSERT_EQ(0, ret); + + /* getppid() should succeed and be logged (no check for logging) */ + EXPECT_EQ(parent, syscall(__NR_getppid)); +} + TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS) { struct sock_filter filter[] = { @@ -520,6 +604,117 @@ TEST_SIGNAL(KILL_one_arg_six, SIGSYS) close(fd); } +/* This is a thread task to die via seccomp filter violation. */ +void *kill_thread(void *data) +{ + bool die = (bool)data; + + if (die) { + prctl(PR_GET_SECCOMP, 0, 0, 0, 0); + return (void *)SIBLING_EXIT_FAILURE; + } + + return (void *)SIBLING_EXIT_UNKILLED; +} + +/* Prepare a thread that will kill itself or both of us. */ +void kill_thread_or_group(struct __test_metadata *_metadata, bool kill_process) +{ + pthread_t thread; + void *status; + /* Kill only when calling __NR_prctl. */ + struct sock_filter filter_thread[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_THREAD), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog_thread = { + .len = (unsigned short)ARRAY_SIZE(filter_thread), + .filter = filter_thread, + }; + struct sock_filter filter_process[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL_PROCESS), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog prog_process = { + .len = (unsigned short)ARRAY_SIZE(filter_process), + .filter = filter_process, + }; + + ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { + TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!"); + } + + ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, + kill_process ? &prog_process : &prog_thread)); + + /* + * Add the KILL_THREAD rule again to make sure that the KILL_PROCESS + * flag cannot be downgraded by a new filter. + */ + ASSERT_EQ(0, seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog_thread)); + + /* Start a thread that will exit immediately. */ + ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)false)); + ASSERT_EQ(0, pthread_join(thread, &status)); + ASSERT_EQ(SIBLING_EXIT_UNKILLED, (unsigned long)status); + + /* Start a thread that will die immediately. */ + ASSERT_EQ(0, pthread_create(&thread, NULL, kill_thread, (void *)true)); + ASSERT_EQ(0, pthread_join(thread, &status)); + ASSERT_NE(SIBLING_EXIT_FAILURE, (unsigned long)status); + + /* + * If we get here, only the spawned thread died. Let the parent know + * the whole process didn't die (i.e. this thread, the spawner, + * stayed running). + */ + exit(42); +} + +TEST(KILL_thread) +{ + int status; + pid_t child_pid; + + child_pid = fork(); + ASSERT_LE(0, child_pid); + if (child_pid == 0) { + kill_thread_or_group(_metadata, false); + _exit(38); + } + + ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); + + /* If only the thread was killed, we'll see exit 42. */ + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(42, WEXITSTATUS(status)); +} + +TEST(KILL_process) +{ + int status; + pid_t child_pid; + + child_pid = fork(); + ASSERT_LE(0, child_pid); + if (child_pid == 0) { + kill_thread_or_group(_metadata, true); + _exit(38); + } + + ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0)); + + /* If the entire process was killed, we'll see SIGSYS. */ + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(SIGSYS, WTERMSIG(status)); +} + /* TODO(wad) add 64-bit versus 32-bit arg tests. */ TEST(arg_out_of_range) { @@ -541,26 +736,30 @@ TEST(arg_out_of_range) EXPECT_EQ(EINVAL, errno); } +#define ERRNO_FILTER(name, errno) \ + struct sock_filter _read_filter_##name[] = { \ + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, \ + offsetof(struct seccomp_data, nr)), \ + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1), \ + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | errno), \ + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), \ + }; \ + struct sock_fprog prog_##name = { \ + .len = (unsigned short)ARRAY_SIZE(_read_filter_##name), \ + .filter = _read_filter_##name, \ + } + +/* Make sure basic errno values are correctly passed through a filter. */ TEST(ERRNO_valid) { - struct sock_filter filter[] = { - BPF_STMT(BPF_LD|BPF_W|BPF_ABS, - offsetof(struct seccomp_data, nr)), - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1), - BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | E2BIG), - BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), - }; - struct sock_fprog prog = { - .len = (unsigned short)ARRAY_SIZE(filter), - .filter = filter, - }; + ERRNO_FILTER(valid, E2BIG); long ret; pid_t parent = getppid(); ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); ASSERT_EQ(0, ret); - ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_valid); ASSERT_EQ(0, ret); EXPECT_EQ(parent, syscall(__NR_getppid)); @@ -568,26 +767,17 @@ TEST(ERRNO_valid) EXPECT_EQ(E2BIG, errno); } +/* Make sure an errno of zero is correctly handled by the arch code. */ TEST(ERRNO_zero) { - struct sock_filter filter[] = { - BPF_STMT(BPF_LD|BPF_W|BPF_ABS, - offsetof(struct seccomp_data, nr)), - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1), - BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 0), - BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), - }; - struct sock_fprog prog = { - .len = (unsigned short)ARRAY_SIZE(filter), - .filter = filter, - }; + ERRNO_FILTER(zero, 0); long ret; pid_t parent = getppid(); ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); ASSERT_EQ(0, ret); - ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_zero); ASSERT_EQ(0, ret); EXPECT_EQ(parent, syscall(__NR_getppid)); @@ -595,26 +785,21 @@ TEST(ERRNO_zero) EXPECT_EQ(0, read(0, NULL, 0)); } +/* + * The SECCOMP_RET_DATA mask is 16 bits wide, but errno is smaller. + * This tests that the errno value gets capped correctly, fixed by + * 580c57f10768 ("seccomp: cap SECCOMP_RET_ERRNO data to MAX_ERRNO"). + */ TEST(ERRNO_capped) { - struct sock_filter filter[] = { - BPF_STMT(BPF_LD|BPF_W|BPF_ABS, - offsetof(struct seccomp_data, nr)), - BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1), - BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 4096), - BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), - }; - struct sock_fprog prog = { - .len = (unsigned short)ARRAY_SIZE(filter), - .filter = filter, - }; + ERRNO_FILTER(capped, 4096); long ret; pid_t parent = getppid(); ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); ASSERT_EQ(0, ret); - ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_capped); ASSERT_EQ(0, ret); EXPECT_EQ(parent, syscall(__NR_getppid)); @@ -622,6 +807,37 @@ TEST(ERRNO_capped) EXPECT_EQ(4095, errno); } +/* + * Filters are processed in reverse order: last applied is executed first. + * Since only the SECCOMP_RET_ACTION mask is tested for return values, the + * SECCOMP_RET_DATA mask results will follow the most recently applied + * matching filter return (and not the lowest or highest value). + */ +TEST(ERRNO_order) +{ + ERRNO_FILTER(first, 11); + ERRNO_FILTER(second, 13); + ERRNO_FILTER(third, 12); + long ret; + pid_t parent = getppid(); + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_first); + ASSERT_EQ(0, ret); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_second); + ASSERT_EQ(0, ret); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog_third); + ASSERT_EQ(0, ret); + + EXPECT_EQ(parent, syscall(__NR_getppid)); + EXPECT_EQ(-1, read(0, NULL, 0)); + EXPECT_EQ(12, errno); +} + FIXTURE_DATA(TRAP) { struct sock_fprog prog; }; @@ -676,7 +892,7 @@ TEST_F_SIGNAL(TRAP, ign, SIGSYS) syscall(__NR_getpid); } -static struct siginfo TRAP_info; +static siginfo_t TRAP_info; static volatile int TRAP_nr; static void TRAP_action(int nr, siginfo_t *info, void *void_context) { @@ -735,6 +951,7 @@ TEST_F(TRAP, handler) FIXTURE_DATA(precedence) { struct sock_fprog allow; + struct sock_fprog log; struct sock_fprog trace; struct sock_fprog error; struct sock_fprog trap; @@ -746,6 +963,13 @@ FIXTURE_SETUP(precedence) struct sock_filter allow_insns[] = { BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), }; + struct sock_filter log_insns[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_LOG), + }; struct sock_filter trace_insns[] = { BPF_STMT(BPF_LD|BPF_W|BPF_ABS, offsetof(struct seccomp_data, nr)), @@ -782,6 +1006,7 @@ FIXTURE_SETUP(precedence) memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \ self->_x.len = (unsigned short)ARRAY_SIZE(_x##_insns) FILTER_ALLOC(allow); + FILTER_ALLOC(log); FILTER_ALLOC(trace); FILTER_ALLOC(error); FILTER_ALLOC(trap); @@ -792,6 +1017,7 @@ FIXTURE_TEARDOWN(precedence) { #define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter) FILTER_FREE(allow); + FILTER_FREE(log); FILTER_FREE(trace); FILTER_FREE(error); FILTER_FREE(trap); @@ -809,6 +1035,8 @@ TEST_F(precedence, allow_ok) ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -833,6 +1061,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest, SIGSYS) ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -864,6 +1094,8 @@ TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS) ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap); @@ -885,6 +1117,8 @@ TEST_F_SIGNAL(precedence, trap_is_second, SIGSYS) ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -910,6 +1144,8 @@ TEST_F_SIGNAL(precedence, trap_is_second_in_any_order, SIGSYS) ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -931,6 +1167,8 @@ TEST_F(precedence, errno_is_third) ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); @@ -949,6 +1187,8 @@ TEST_F(precedence, errno_is_third_in_any_order) ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error); ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); @@ -971,6 +1211,8 @@ TEST_F(precedence, trace_is_fourth) ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace); ASSERT_EQ(0, ret); /* Should work just fine. */ @@ -992,12 +1234,54 @@ TEST_F(precedence, trace_is_fourth_in_any_order) ASSERT_EQ(0, ret); ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); /* Should work just fine. */ EXPECT_EQ(parent, syscall(__NR_getppid)); /* No ptracer */ EXPECT_EQ(-1, syscall(__NR_getpid)); } +TEST_F(precedence, log_is_fifth) +{ + pid_t mypid, parent; + long ret; + + mypid = getpid(); + parent = getppid(); + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); + ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); + /* Should work just fine. */ + EXPECT_EQ(parent, syscall(__NR_getppid)); + /* Should also work just fine */ + EXPECT_EQ(mypid, syscall(__NR_getpid)); +} + +TEST_F(precedence, log_is_fifth_in_any_order) +{ + pid_t mypid, parent; + long ret; + + mypid = getpid(); + parent = getppid(); + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->log); + ASSERT_EQ(0, ret); + ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow); + ASSERT_EQ(0, ret); + /* Should work just fine. */ + EXPECT_EQ(parent, syscall(__NR_getppid)); + /* Should also work just fine */ + EXPECT_EQ(mypid, syscall(__NR_getpid)); +} + #ifndef PTRACE_O_TRACESECCOMP #define PTRACE_O_TRACESECCOMP 0x00000080 #endif @@ -1262,6 +1546,13 @@ TEST_F(TRACE_poke, getpid_runs_normally) # error "Do not know how to find your architecture's registers and syscalls" #endif +/* When the syscall return can't be changed, stub out the tests for it. */ +#ifdef SYSCALL_NUM_RET_SHARE_REG +# define EXPECT_SYSCALL_RETURN(val, action) EXPECT_EQ(-1, action) +#else +# define EXPECT_SYSCALL_RETURN(val, action) EXPECT_EQ(val, action) +#endif + /* Use PTRACE_GETREGS and PTRACE_SETREGS when available. This is useful for * architectures without HAVE_ARCH_TRACEHOOK (e.g. User-mode Linux). */ @@ -1310,7 +1601,7 @@ void change_syscall(struct __test_metadata *_metadata, iov.iov_len = sizeof(regs); ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov); #endif - EXPECT_EQ(0, ret); + EXPECT_EQ(0, ret) {} #if defined(__x86_64__) || defined(__i386__) || defined(__powerpc__) || \ defined(__s390__) || defined(__hppa__) @@ -1357,7 +1648,7 @@ void change_syscall(struct __test_metadata *_metadata, #ifdef SYSCALL_NUM_RET_SHARE_REG TH_LOG("Can't modify syscall return on this architecture"); #else - regs.SYSCALL_RET = 1; + regs.SYSCALL_RET = EPERM; #endif #ifdef HAVE_GETREGS @@ -1426,6 +1717,8 @@ void tracer_ptrace(struct __test_metadata *_metadata, pid_t tracee, if (nr == __NR_getpid) change_syscall(_metadata, tracee, __NR_getppid); + if (nr == __NR_open) + change_syscall(_metadata, tracee, -1); } FIXTURE_DATA(TRACE_syscall) { @@ -1480,6 +1773,28 @@ FIXTURE_TEARDOWN(TRACE_syscall) free(self->prog.filter); } +TEST_F(TRACE_syscall, ptrace_syscall_redirected) +{ + /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */ + teardown_trace_fixture(_metadata, self->tracer); + self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL, + true); + + /* Tracer will redirect getpid to getppid. */ + EXPECT_NE(self->mypid, syscall(__NR_getpid)); +} + +TEST_F(TRACE_syscall, ptrace_syscall_dropped) +{ + /* Swap SECCOMP_RET_TRACE tracer for PTRACE_SYSCALL tracer. */ + teardown_trace_fixture(_metadata, self->tracer); + self->tracer = setup_trace_fixture(_metadata, tracer_ptrace, NULL, + true); + + /* Tracer should skip the open syscall, resulting in EPERM. */ + EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_open)); +} + TEST_F(TRACE_syscall, syscall_allowed) { long ret; @@ -1520,13 +1835,8 @@ TEST_F(TRACE_syscall, syscall_dropped) ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0); ASSERT_EQ(0, ret); -#ifdef SYSCALL_NUM_RET_SHARE_REG - /* gettid has been skipped */ - EXPECT_EQ(-1, syscall(__NR_gettid)); -#else /* gettid has been skipped and an altered return value stored. */ - EXPECT_EQ(1, syscall(__NR_gettid)); -#endif + EXPECT_SYSCALL_RETURN(EPERM, syscall(__NR_gettid)); EXPECT_NE(self->mytid, syscall(__NR_gettid)); } @@ -1557,6 +1867,7 @@ TEST_F(TRACE_syscall, skip_after_RET_TRACE) ASSERT_EQ(0, ret); /* Tracer will redirect getpid to getppid, and we should see EPERM. */ + errno = 0; EXPECT_EQ(-1, syscall(__NR_getpid)); EXPECT_EQ(EPERM, errno); } @@ -1654,47 +1965,6 @@ TEST_F_SIGNAL(TRACE_syscall, kill_after_ptrace, SIGSYS) EXPECT_NE(self->mypid, syscall(__NR_getpid)); } -#ifndef __NR_seccomp -# if defined(__i386__) -# define __NR_seccomp 354 -# elif defined(__x86_64__) -# define __NR_seccomp 317 -# elif defined(__arm__) -# define __NR_seccomp 383 -# elif defined(__aarch64__) -# define __NR_seccomp 277 -# elif defined(__hppa__) -# define __NR_seccomp 338 -# elif defined(__powerpc__) -# define __NR_seccomp 358 -# elif defined(__s390__) -# define __NR_seccomp 348 -# else -# warning "seccomp syscall number unknown for this architecture" -# define __NR_seccomp 0xffff -# endif -#endif - -#ifndef SECCOMP_SET_MODE_STRICT -#define SECCOMP_SET_MODE_STRICT 0 -#endif - -#ifndef SECCOMP_SET_MODE_FILTER -#define SECCOMP_SET_MODE_FILTER 1 -#endif - -#ifndef SECCOMP_FILTER_FLAG_TSYNC -#define SECCOMP_FILTER_FLAG_TSYNC 1 -#endif - -#ifndef seccomp -int seccomp(unsigned int op, unsigned int flags, void *args) -{ - errno = 0; - return syscall(__NR_seccomp, op, flags, args); -} -#endif - TEST(seccomp_syscall) { struct sock_filter filter[] = { @@ -1783,6 +2053,67 @@ TEST(seccomp_syscall_mode_lock) } } +/* + * Test detection of known and unknown filter flags. Userspace needs to be able + * to check if a filter flag is supported by the current kernel and a good way + * of doing that is by attempting to enter filter mode, with the flag bit in + * question set, and a NULL pointer for the _args_ parameter. EFAULT indicates + * that the flag is valid and EINVAL indicates that the flag is invalid. + */ +TEST(detect_seccomp_filter_flags) +{ + unsigned int flags[] = { SECCOMP_FILTER_FLAG_TSYNC, + SECCOMP_FILTER_FLAG_LOG }; + unsigned int flag, all_flags; + int i; + long ret; + + /* Test detection of known-good filter flags */ + for (i = 0, all_flags = 0; i < ARRAY_SIZE(flags); i++) { + flag = flags[i]; + ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL); + ASSERT_NE(ENOSYS, errno) { + TH_LOG("Kernel does not support seccomp syscall!"); + } + EXPECT_EQ(-1, ret); + EXPECT_EQ(EFAULT, errno) { + TH_LOG("Failed to detect that a known-good filter flag (0x%X) is supported!", + flag); + } + + all_flags |= flag; + } + + /* Test detection of all known-good filter flags */ + ret = seccomp(SECCOMP_SET_MODE_FILTER, all_flags, NULL); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EFAULT, errno) { + TH_LOG("Failed to detect that all known-good filter flags (0x%X) are supported!", + all_flags); + } + + /* Test detection of an unknown filter flag */ + flag = -1; + ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno) { + TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported!", + flag); + } + + /* + * Test detection of an unknown filter flag that may simply need to be + * added to this test + */ + flag = flags[ARRAY_SIZE(flags) - 1] << 1; + ret = seccomp(SECCOMP_SET_MODE_FILTER, flag, NULL); + EXPECT_EQ(-1, ret); + EXPECT_EQ(EINVAL, errno) { + TH_LOG("Failed to detect that an unknown filter flag (0x%X) is unsupported! Does a new flag need to be added to this test?", + flag); + } +} + TEST(TSYNC_first) { struct sock_filter filter[] = { @@ -1822,6 +2153,23 @@ struct tsync_sibling { struct __test_metadata *metadata; }; +/* + * To avoid joining joined threads (which is not allowed by Bionic), + * make sure we both successfully join and clear the tid to skip a + * later join attempt during fixture teardown. Any remaining threads + * will be directly killed during teardown. + */ +#define PTHREAD_JOIN(tid, status) \ + do { \ + int _rc = pthread_join(tid, status); \ + if (_rc) { \ + TH_LOG("pthread_join of tid %u failed: %d\n", \ + (unsigned int)tid, _rc); \ + } else { \ + tid = 0; \ + } \ + } while (0) + FIXTURE_DATA(TSYNC) { struct sock_fprog root_prog, apply_prog; struct tsync_sibling sibling[TSYNC_SIBLINGS]; @@ -1890,14 +2238,14 @@ FIXTURE_TEARDOWN(TSYNC) for ( ; sib < self->sibling_count; ++sib) { struct tsync_sibling *s = &self->sibling[sib]; - void *status; if (!s->tid) continue; - if (pthread_kill(s->tid, 0)) { - pthread_cancel(s->tid); - pthread_join(s->tid, &status); - } + /* + * If a thread is still running, it may be stuck, so hit + * it over the head really hard. + */ + pthread_kill(s->tid, 9); } pthread_mutex_destroy(&self->mutex); pthread_cond_destroy(&self->cond); @@ -1987,9 +2335,9 @@ TEST_F(TSYNC, siblings_fail_prctl) pthread_mutex_unlock(&self->mutex); /* Ensure diverging sibling failed to call prctl. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(SIBLING_EXIT_FAILURE, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); } @@ -2029,9 +2377,9 @@ TEST_F(TSYNC, two_siblings_with_ancestor) } pthread_mutex_unlock(&self->mutex); /* Ensure they are both killed and don't exit cleanly. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(0x0, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(0x0, (long)status); } @@ -2055,9 +2403,9 @@ TEST_F(TSYNC, two_sibling_want_nnp) pthread_mutex_unlock(&self->mutex); /* Ensure they are both upset about lacking nnp. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status); } @@ -2095,9 +2443,9 @@ TEST_F(TSYNC, two_siblings_with_no_filter) pthread_mutex_unlock(&self->mutex); /* Ensure they are both killed and don't exit cleanly. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(0x0, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(0x0, (long)status); } @@ -2140,9 +2488,9 @@ TEST_F(TSYNC, two_siblings_with_one_divergence) pthread_mutex_unlock(&self->mutex); /* Ensure they are both unkilled. */ - pthread_join(self->sibling[0].tid, &status); + PTHREAD_JOIN(self->sibling[0].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); - pthread_join(self->sibling[1].tid, &status); + PTHREAD_JOIN(self->sibling[1].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); } @@ -2199,7 +2547,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) TH_LOG("cond broadcast non-zero"); } pthread_mutex_unlock(&self->mutex); - pthread_join(self->sibling[sib].tid, &status); + PTHREAD_JOIN(self->sibling[sib].tid, &status); EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status); /* Poll for actual task death. pthread_join doesn't guarantee it. */ while (!kill(self->sibling[sib].system_tid, 0)) @@ -2224,7 +2572,7 @@ TEST_F(TSYNC, two_siblings_not_under_filter) TH_LOG("cond broadcast non-zero"); } pthread_mutex_unlock(&self->mutex); - pthread_join(self->sibling[sib].tid, &status); + PTHREAD_JOIN(self->sibling[sib].tid, &status); EXPECT_EQ(0, (long)status); /* Poll for actual task death. pthread_join doesn't guarantee it. */ while (!kill(self->sibling[sib].system_tid, 0)) @@ -2404,6 +2752,99 @@ TEST(syscall_restart) _metadata->passed = 0; } +TEST_SIGNAL(filter_flag_log, SIGSYS) +{ + struct sock_filter allow_filter[] = { + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_filter kill_filter[] = { + BPF_STMT(BPF_LD|BPF_W|BPF_ABS, + offsetof(struct seccomp_data, nr)), + BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL), + BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW), + }; + struct sock_fprog allow_prog = { + .len = (unsigned short)ARRAY_SIZE(allow_filter), + .filter = allow_filter, + }; + struct sock_fprog kill_prog = { + .len = (unsigned short)ARRAY_SIZE(kill_filter), + .filter = kill_filter, + }; + long ret; + pid_t parent = getppid(); + + ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); + ASSERT_EQ(0, ret); + + /* Verify that the FILTER_FLAG_LOG flag isn't accepted in strict mode */ + ret = seccomp(SECCOMP_SET_MODE_STRICT, SECCOMP_FILTER_FLAG_LOG, + &allow_prog); + ASSERT_NE(ENOSYS, errno) { + TH_LOG("Kernel does not support seccomp syscall!"); + } + EXPECT_NE(0, ret) { + TH_LOG("Kernel accepted FILTER_FLAG_LOG flag in strict mode!"); + } + EXPECT_EQ(EINVAL, errno) { + TH_LOG("Kernel returned unexpected errno for FILTER_FLAG_LOG flag in strict mode!"); + } + + /* Verify that a simple, permissive filter can be added with no flags */ + ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &allow_prog); + EXPECT_EQ(0, ret); + + /* See if the same filter can be added with the FILTER_FLAG_LOG flag */ + ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG, + &allow_prog); + ASSERT_NE(EINVAL, errno) { + TH_LOG("Kernel does not support the FILTER_FLAG_LOG flag!"); + } + EXPECT_EQ(0, ret); + + /* Ensure that the kill filter works with the FILTER_FLAG_LOG flag */ + ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_LOG, + &kill_prog); + EXPECT_EQ(0, ret); + + EXPECT_EQ(parent, syscall(__NR_getppid)); + /* getpid() should never return. */ + EXPECT_EQ(0, syscall(__NR_getpid)); +} + +TEST(get_action_avail) +{ + __u32 actions[] = { SECCOMP_RET_KILL_THREAD, SECCOMP_RET_TRAP, + SECCOMP_RET_ERRNO, SECCOMP_RET_TRACE, + SECCOMP_RET_LOG, SECCOMP_RET_ALLOW }; + __u32 unknown_action = 0x10000000U; + int i; + long ret; + + ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &actions[0]); + ASSERT_NE(ENOSYS, errno) { + TH_LOG("Kernel does not support seccomp syscall!"); + } + ASSERT_NE(EINVAL, errno) { + TH_LOG("Kernel does not support SECCOMP_GET_ACTION_AVAIL operation!"); + } + EXPECT_EQ(ret, 0); + + for (i = 0; i < ARRAY_SIZE(actions); i++) { + ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &actions[i]); + EXPECT_EQ(ret, 0) { + TH_LOG("Expected action (0x%X) not available!", + actions[i]); + } + } + + /* Check that an unknown action is handled properly (EOPNOTSUPP) */ + ret = seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &unknown_action); + EXPECT_EQ(ret, -1); + EXPECT_EQ(errno, EOPNOTSUPP); +} + /* * TODO: * - add microbenchmarks @@ -2412,6 +2853,8 @@ TEST(syscall_restart) * - endianness checking when appropriate * - 64-bit arg prodding * - arch value testing (x86 modes especially) + * - verify that FILTER_FLAG_LOG filters generate log messages + * - verify that RET_LOG generates log messages * - ... */ diff --git a/tools/testing/selftests/seccomp/test_harness.h b/tools/testing/selftests/seccomp/test_harness.h deleted file mode 100644 index a786c69c7584..000000000000 --- a/tools/testing/selftests/seccomp/test_harness.h +++ /dev/null @@ -1,535 +0,0 @@ -/* - * Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by the GPLv2 license. - * - * test_harness.h: simple C unit test helper. - * - * Usage: - * #include "test_harness.h" - * TEST(standalone_test) { - * do_some_stuff; - * EXPECT_GT(10, stuff) { - * stuff_state_t state; - * enumerate_stuff_state(&state); - * TH_LOG("expectation failed with state: %s", state.msg); - * } - * more_stuff; - * ASSERT_NE(some_stuff, NULL) TH_LOG("how did it happen?!"); - * last_stuff; - * EXPECT_EQ(0, last_stuff); - * } - * - * FIXTURE(my_fixture) { - * mytype_t *data; - * int awesomeness_level; - * }; - * FIXTURE_SETUP(my_fixture) { - * self->data = mytype_new(); - * ASSERT_NE(NULL, self->data); - * } - * FIXTURE_TEARDOWN(my_fixture) { - * mytype_free(self->data); - * } - * TEST_F(my_fixture, data_is_good) { - * EXPECT_EQ(1, is_my_data_good(self->data)); - * } - * - * TEST_HARNESS_MAIN - * - * API inspired by code.google.com/p/googletest - */ -#ifndef TEST_HARNESS_H_ -#define TEST_HARNESS_H_ - -#define _GNU_SOURCE -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <unistd.h> - -/* All exported functionality should be declared through this macro. */ -#define TEST_API(x) _##x - -/* - * Exported APIs - */ - -/* TEST(name) { implementation } - * Defines a test by name. - * Names must be unique and tests must not be run in parallel. The - * implementation containing block is a function and scoping should be treated - * as such. Returning early may be performed with a bare "return;" statement. - * - * EXPECT_* and ASSERT_* are valid in a TEST() { } context. - */ -#define TEST TEST_API(TEST) - -/* TEST_SIGNAL(name, signal) { implementation } - * Defines a test by name and the expected term signal. - * Names must be unique and tests must not be run in parallel. The - * implementation containing block is a function and scoping should be treated - * as such. Returning early may be performed with a bare "return;" statement. - * - * EXPECT_* and ASSERT_* are valid in a TEST() { } context. - */ -#define TEST_SIGNAL TEST_API(TEST_SIGNAL) - -/* FIXTURE(datatype name) { - * type property1; - * ... - * }; - * Defines the data provided to TEST_F()-defined tests as |self|. It should be - * populated and cleaned up using FIXTURE_SETUP and FIXTURE_TEARDOWN. - */ -#define FIXTURE TEST_API(FIXTURE) - -/* FIXTURE_DATA(datatype name) - * This call may be used when the type of the fixture data - * is needed. In general, this should not be needed unless - * the |self| is being passed to a helper directly. - */ -#define FIXTURE_DATA TEST_API(FIXTURE_DATA) - -/* FIXTURE_SETUP(fixture name) { implementation } - * Populates the required "setup" function for a fixture. An instance of the - * datatype defined with _FIXTURE_DATA will be exposed as |self| for the - * implementation. - * - * ASSERT_* are valid for use in this context and will prempt the execution - * of any dependent fixture tests. - * - * A bare "return;" statement may be used to return early. - */ -#define FIXTURE_SETUP TEST_API(FIXTURE_SETUP) - -/* FIXTURE_TEARDOWN(fixture name) { implementation } - * Populates the required "teardown" function for a fixture. An instance of the - * datatype defined with _FIXTURE_DATA will be exposed as |self| for the - * implementation to clean up. - * - * A bare "return;" statement may be used to return early. - */ -#define FIXTURE_TEARDOWN TEST_API(FIXTURE_TEARDOWN) - -/* TEST_F(fixture, name) { implementation } - * Defines a test that depends on a fixture (e.g., is part of a test case). - * Very similar to TEST() except that |self| is the setup instance of fixture's - * datatype exposed for use by the implementation. - */ -#define TEST_F TEST_API(TEST_F) - -#define TEST_F_SIGNAL TEST_API(TEST_F_SIGNAL) - -/* Use once to append a main() to the test file. E.g., - * TEST_HARNESS_MAIN - */ -#define TEST_HARNESS_MAIN TEST_API(TEST_HARNESS_MAIN) - -/* - * Operators for use in TEST and TEST_F. - * ASSERT_* calls will stop test execution immediately. - * EXPECT_* calls will emit a failure warning, note it, and continue. - */ - -/* ASSERT_EQ(expected, measured): expected == measured */ -#define ASSERT_EQ TEST_API(ASSERT_EQ) -/* ASSERT_NE(expected, measured): expected != measured */ -#define ASSERT_NE TEST_API(ASSERT_NE) -/* ASSERT_LT(expected, measured): expected < measured */ -#define ASSERT_LT TEST_API(ASSERT_LT) -/* ASSERT_LE(expected, measured): expected <= measured */ -#define ASSERT_LE TEST_API(ASSERT_LE) -/* ASSERT_GT(expected, measured): expected > measured */ -#define ASSERT_GT TEST_API(ASSERT_GT) -/* ASSERT_GE(expected, measured): expected >= measured */ -#define ASSERT_GE TEST_API(ASSERT_GE) -/* ASSERT_NULL(measured): NULL == measured */ -#define ASSERT_NULL TEST_API(ASSERT_NULL) -/* ASSERT_TRUE(measured): measured != 0 */ -#define ASSERT_TRUE TEST_API(ASSERT_TRUE) -/* ASSERT_FALSE(measured): measured == 0 */ -#define ASSERT_FALSE TEST_API(ASSERT_FALSE) -/* ASSERT_STREQ(expected, measured): !strcmp(expected, measured) */ -#define ASSERT_STREQ TEST_API(ASSERT_STREQ) -/* ASSERT_STRNE(expected, measured): strcmp(expected, measured) */ -#define ASSERT_STRNE TEST_API(ASSERT_STRNE) -/* EXPECT_EQ(expected, measured): expected == measured */ -#define EXPECT_EQ TEST_API(EXPECT_EQ) -/* EXPECT_NE(expected, measured): expected != measured */ -#define EXPECT_NE TEST_API(EXPECT_NE) -/* EXPECT_LT(expected, measured): expected < measured */ -#define EXPECT_LT TEST_API(EXPECT_LT) -/* EXPECT_LE(expected, measured): expected <= measured */ -#define EXPECT_LE TEST_API(EXPECT_LE) -/* EXPECT_GT(expected, measured): expected > measured */ -#define EXPECT_GT TEST_API(EXPECT_GT) -/* EXPECT_GE(expected, measured): expected >= measured */ -#define EXPECT_GE TEST_API(EXPECT_GE) -/* EXPECT_NULL(measured): NULL == measured */ -#define EXPECT_NULL TEST_API(EXPECT_NULL) -/* EXPECT_TRUE(measured): 0 != measured */ -#define EXPECT_TRUE TEST_API(EXPECT_TRUE) -/* EXPECT_FALSE(measured): 0 == measured */ -#define EXPECT_FALSE TEST_API(EXPECT_FALSE) -/* EXPECT_STREQ(expected, measured): !strcmp(expected, measured) */ -#define EXPECT_STREQ TEST_API(EXPECT_STREQ) -/* EXPECT_STRNE(expected, measured): strcmp(expected, measured) */ -#define EXPECT_STRNE TEST_API(EXPECT_STRNE) - -/* TH_LOG(format, ...) - * Optional debug logging function available for use in tests. - * Logging may be enabled or disabled by defining TH_LOG_ENABLED. - * E.g., #define TH_LOG_ENABLED 1 - * If no definition is provided, logging is enabled by default. - */ -#define TH_LOG TEST_API(TH_LOG) - -/* - * Internal implementation. - * - */ - -/* Utilities exposed to the test definitions */ -#ifndef TH_LOG_STREAM -# define TH_LOG_STREAM stderr -#endif - -#ifndef TH_LOG_ENABLED -# define TH_LOG_ENABLED 1 -#endif - -#define _TH_LOG(fmt, ...) do { \ - if (TH_LOG_ENABLED) \ - __TH_LOG(fmt, ##__VA_ARGS__); \ -} while (0) - -/* Unconditional logger for internal use. */ -#define __TH_LOG(fmt, ...) \ - fprintf(TH_LOG_STREAM, "%s:%d:%s:" fmt "\n", \ - __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__) - -/* Defines the test function and creates the registration stub. */ -#define _TEST(test_name) __TEST_IMPL(test_name, -1) - -#define _TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal) - -#define __TEST_IMPL(test_name, _signal) \ - static void test_name(struct __test_metadata *_metadata); \ - static struct __test_metadata _##test_name##_object = \ - { name: "global." #test_name, \ - fn: &test_name, termsig: _signal }; \ - static void __attribute__((constructor)) _register_##test_name(void) \ - { \ - __register_test(&_##test_name##_object); \ - } \ - static void test_name( \ - struct __test_metadata __attribute__((unused)) *_metadata) - -/* Wraps the struct name so we have one less argument to pass around. */ -#define _FIXTURE_DATA(fixture_name) struct _test_data_##fixture_name - -/* Called once per fixture to setup the data and register. */ -#define _FIXTURE(fixture_name) \ - static void __attribute__((constructor)) \ - _register_##fixture_name##_data(void) \ - { \ - __fixture_count++; \ - } \ - _FIXTURE_DATA(fixture_name) - -/* Prepares the setup function for the fixture. |_metadata| is included - * so that ASSERT_* work as a convenience. - */ -#define _FIXTURE_SETUP(fixture_name) \ - void fixture_name##_setup( \ - struct __test_metadata __attribute__((unused)) *_metadata, \ - _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) -#define _FIXTURE_TEARDOWN(fixture_name) \ - void fixture_name##_teardown( \ - struct __test_metadata __attribute__((unused)) *_metadata, \ - _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) - -/* Emits test registration and helpers for fixture-based test - * cases. - * TODO(wad) register fixtures on dedicated test lists. - */ -#define _TEST_F(fixture_name, test_name) \ - __TEST_F_IMPL(fixture_name, test_name, -1) - -#define _TEST_F_SIGNAL(fixture_name, test_name, signal) \ - __TEST_F_IMPL(fixture_name, test_name, signal) - -#define __TEST_F_IMPL(fixture_name, test_name, signal) \ - static void fixture_name##_##test_name( \ - struct __test_metadata *_metadata, \ - _FIXTURE_DATA(fixture_name) *self); \ - static inline void wrapper_##fixture_name##_##test_name( \ - struct __test_metadata *_metadata) \ - { \ - /* fixture data is alloced, setup, and torn down per call. */ \ - _FIXTURE_DATA(fixture_name) self; \ - memset(&self, 0, sizeof(_FIXTURE_DATA(fixture_name))); \ - fixture_name##_setup(_metadata, &self); \ - /* Let setup failure terminate early. */ \ - if (!_metadata->passed) \ - return; \ - fixture_name##_##test_name(_metadata, &self); \ - fixture_name##_teardown(_metadata, &self); \ - } \ - static struct __test_metadata \ - _##fixture_name##_##test_name##_object = { \ - name: #fixture_name "." #test_name, \ - fn: &wrapper_##fixture_name##_##test_name, \ - termsig: signal, \ - }; \ - static void __attribute__((constructor)) \ - _register_##fixture_name##_##test_name(void) \ - { \ - __register_test(&_##fixture_name##_##test_name##_object); \ - } \ - static void fixture_name##_##test_name( \ - struct __test_metadata __attribute__((unused)) *_metadata, \ - _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self) - -/* Exports a simple wrapper to run the test harness. */ -#define _TEST_HARNESS_MAIN \ - static void __attribute__((constructor)) \ - __constructor_order_last(void) \ - { \ - if (!__constructor_order) \ - __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; \ - } \ - int main(int argc, char **argv) { \ - return test_harness_run(argc, argv); \ - } - -#define _ASSERT_EQ(_expected, _seen) \ - __EXPECT(_expected, _seen, ==, 1) -#define _ASSERT_NE(_expected, _seen) \ - __EXPECT(_expected, _seen, !=, 1) -#define _ASSERT_LT(_expected, _seen) \ - __EXPECT(_expected, _seen, <, 1) -#define _ASSERT_LE(_expected, _seen) \ - __EXPECT(_expected, _seen, <=, 1) -#define _ASSERT_GT(_expected, _seen) \ - __EXPECT(_expected, _seen, >, 1) -#define _ASSERT_GE(_expected, _seen) \ - __EXPECT(_expected, _seen, >=, 1) -#define _ASSERT_NULL(_seen) \ - __EXPECT(NULL, _seen, ==, 1) - -#define _ASSERT_TRUE(_seen) \ - _ASSERT_NE(0, _seen) -#define _ASSERT_FALSE(_seen) \ - _ASSERT_EQ(0, _seen) -#define _ASSERT_STREQ(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, ==, 1) -#define _ASSERT_STRNE(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, !=, 1) - -#define _EXPECT_EQ(_expected, _seen) \ - __EXPECT(_expected, _seen, ==, 0) -#define _EXPECT_NE(_expected, _seen) \ - __EXPECT(_expected, _seen, !=, 0) -#define _EXPECT_LT(_expected, _seen) \ - __EXPECT(_expected, _seen, <, 0) -#define _EXPECT_LE(_expected, _seen) \ - __EXPECT(_expected, _seen, <=, 0) -#define _EXPECT_GT(_expected, _seen) \ - __EXPECT(_expected, _seen, >, 0) -#define _EXPECT_GE(_expected, _seen) \ - __EXPECT(_expected, _seen, >=, 0) - -#define _EXPECT_NULL(_seen) \ - __EXPECT(NULL, _seen, ==, 0) -#define _EXPECT_TRUE(_seen) \ - _EXPECT_NE(0, _seen) -#define _EXPECT_FALSE(_seen) \ - _EXPECT_EQ(0, _seen) - -#define _EXPECT_STREQ(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, ==, 0) -#define _EXPECT_STRNE(_expected, _seen) \ - __EXPECT_STR(_expected, _seen, !=, 0) - -#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) - -/* Support an optional handler after and ASSERT_* or EXPECT_*. The approach is - * not thread-safe, but it should be fine in most sane test scenarios. - * - * Using __bail(), which optionally abort()s, is the easiest way to early - * return while still providing an optional block to the API consumer. - */ -#define OPTIONAL_HANDLER(_assert) \ - for (; _metadata->trigger; _metadata->trigger = __bail(_assert)) - -#define __EXPECT(_expected, _seen, _t, _assert) do { \ - /* Avoid multiple evaluation of the cases */ \ - __typeof__(_expected) __exp = (_expected); \ - __typeof__(_seen) __seen = (_seen); \ - if (!(__exp _t __seen)) { \ - unsigned long long __exp_print = (uintptr_t)__exp; \ - unsigned long long __seen_print = (uintptr_t)__seen; \ - __TH_LOG("Expected %s (%llu) %s %s (%llu)", \ - #_expected, __exp_print, #_t, \ - #_seen, __seen_print); \ - _metadata->passed = 0; \ - /* Ensure the optional handler is triggered */ \ - _metadata->trigger = 1; \ - } \ -} while (0); OPTIONAL_HANDLER(_assert) - -#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \ - const char *__exp = (_expected); \ - const char *__seen = (_seen); \ - if (!(strcmp(__exp, __seen) _t 0)) { \ - __TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \ - _metadata->passed = 0; \ - _metadata->trigger = 1; \ - } \ -} while (0); OPTIONAL_HANDLER(_assert) - -/* Contains all the information for test execution and status checking. */ -struct __test_metadata { - const char *name; - void (*fn)(struct __test_metadata *); - int termsig; - int passed; - int trigger; /* extra handler after the evaluation */ - struct __test_metadata *prev, *next; -}; - -/* Storage for the (global) tests to be run. */ -static struct __test_metadata *__test_list; -static unsigned int __test_count; -static unsigned int __fixture_count; -static int __constructor_order; - -#define _CONSTRUCTOR_ORDER_FORWARD 1 -#define _CONSTRUCTOR_ORDER_BACKWARD -1 - -/* - * Since constructors are called in reverse order, reverse the test - * list so tests are run in source declaration order. - * https://gcc.gnu.org/onlinedocs/gccint/Initialization.html - * However, it seems not all toolchains do this correctly, so use - * __constructor_order to detect which direction is called first - * and adjust list building logic to get things running in the right - * direction. - */ -static inline void __register_test(struct __test_metadata *t) -{ - __test_count++; - /* Circular linked list where only prev is circular. */ - if (__test_list == NULL) { - __test_list = t; - t->next = NULL; - t->prev = t; - return; - } - if (__constructor_order == _CONSTRUCTOR_ORDER_FORWARD) { - t->next = NULL; - t->prev = __test_list->prev; - t->prev->next = t; - __test_list->prev = t; - } else { - t->next = __test_list; - t->next->prev = t; - t->prev = t; - __test_list = t; - } -} - -static inline int __bail(int for_realz) -{ - if (for_realz) - abort(); - return 0; -} - -void __run_test(struct __test_metadata *t) -{ - pid_t child_pid; - int status; - - t->passed = 1; - t->trigger = 0; - printf("[ RUN ] %s\n", t->name); - child_pid = fork(); - if (child_pid < 0) { - printf("ERROR SPAWNING TEST CHILD\n"); - t->passed = 0; - } else if (child_pid == 0) { - t->fn(t); - _exit(t->passed); - } else { - /* TODO(wad) add timeout support. */ - waitpid(child_pid, &status, 0); - if (WIFEXITED(status)) { - 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 (WIFSIGNALED(status)) { - t->passed = 0; - if (WTERMSIG(status) == SIGABRT) { - fprintf(TH_LOG_STREAM, - "%s: Test terminated by assertion\n", - t->name); - } else if (WTERMSIG(status) == t->termsig) { - t->passed = 1; - } else { - fprintf(TH_LOG_STREAM, - "%s: Test terminated unexpectedly " - "by signal %d\n", - t->name, - WTERMSIG(status)); - } - } else { - fprintf(TH_LOG_STREAM, - "%s: Test ended in some other way [%u]\n", - t->name, - status); - } - } - printf("[ %4s ] %s\n", (t->passed ? "OK" : "FAIL"), t->name); -} - -static int test_harness_run(int __attribute__((unused)) argc, - char __attribute__((unused)) **argv) -{ - struct __test_metadata *t; - int ret = 0; - unsigned int count = 0; - unsigned int pass_count = 0; - - /* TODO(wad) add optional arguments similar to gtest. */ - printf("[==========] Running %u tests from %u test cases.\n", - __test_count, __fixture_count + 1); - for (t = __test_list; t; t = t->next) { - count++; - __run_test(t); - if (t->passed) - pass_count++; - else - ret = 1; - } - printf("[==========] %u / %u tests passed.\n", pass_count, count); - printf("[ %s ]\n", (ret ? "FAILED" : "PASSED")); - return ret; -} - -static void __attribute__((constructor)) __constructor_order_first(void) -{ - if (!__constructor_order) - __constructor_order = _CONSTRUCTOR_ORDER_FORWARD; -} - -#endif /* TEST_HARNESS_H_ */ diff --git a/tools/testing/selftests/sigaltstack/sas.c b/tools/testing/selftests/sigaltstack/sas.c index ccd07343d418..97bb150837df 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 @@ -37,12 +39,15 @@ void my_usr1(int sig, siginfo_t *si, void *u) stack_t stk; struct stk_data *p; +#if __s390x__ + register unsigned long sp asm("%15"); +#else register unsigned long sp asm("sp"); +#endif 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 +55,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 +80,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 +96,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 +107,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 +118,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 +142,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 +152,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 +162,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 +174,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/size/get_size.c b/tools/testing/selftests/size/get_size.c index 2d1af7cca463..d4b59ab979a0 100644 --- a/tools/testing/selftests/size/get_size.c +++ b/tools/testing/selftests/size/get_size.c @@ -75,26 +75,31 @@ void _start(void) int ccode; struct sysinfo info; unsigned long used; + static const char *test_name = " get runtime memory use\n"; - print("Testing system size.\n"); - print("1..1\n"); + print("TAP version 13\n"); + print("# Testing system size.\n"); ccode = sysinfo(&info); if (ccode < 0) { - print("not ok 1 get runtime memory use\n"); - print("# could not get sysinfo\n"); + print("not ok 1"); + print(test_name); + print(" ---\n reason: \"could not get sysinfo\"\n ...\n"); _exit(ccode); } + print("ok 1"); + print(test_name); + /* ignore cache complexities for now */ used = info.totalram - info.freeram - info.bufferram; - print_k_value("ok 1 get runtime memory use # size = ", used, - info.mem_unit); - print("# System runtime memory report (units in Kilobytes):\n"); - print_k_value("# Total: ", info.totalram, info.mem_unit); - print_k_value("# Free: ", info.freeram, info.mem_unit); - print_k_value("# Buffer: ", info.bufferram, info.mem_unit); - print_k_value("# In use: ", used, info.mem_unit); + print(" ---\n"); + print_k_value(" Total: ", info.totalram, info.mem_unit); + print_k_value(" Free: ", info.freeram, info.mem_unit); + print_k_value(" Buffer: ", info.bufferram, info.mem_unit); + print_k_value(" In use: ", used, info.mem_unit); + print(" ...\n"); + print("1..1\n"); _exit(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/Makefile b/tools/testing/selftests/sync/Makefile index 4981c6b6d050..8e04d0afcbd7 100644 --- a/tools/testing/selftests/sync/Makefile +++ b/tools/testing/selftests/sync/Makefile @@ -2,12 +2,16 @@ CFLAGS += -O2 -g -std=gnu89 -pthread -Wall -Wextra CFLAGS += -I../../../../usr/include/ LDFLAGS += -pthread -TEST_PROGS = sync_test - -all: $(TEST_PROGS) +.PHONY: all clean include ../lib.mk +# lib.mk TEST_CUSTOM_PROGS var is for custom tests that need special +# build rules. lib.mk will run and install them. + +TEST_CUSTOM_PROGS := $(OUTPUT)/sync_test +all: $(TEST_CUSTOM_PROGS) + OBJS = sync_test.o sync.o TESTS += sync_alloc.o @@ -18,6 +22,16 @@ TESTS += sync_stress_parallelism.o TESTS += sync_stress_consumer.o TESTS += sync_stress_merge.o -sync_test: $(OBJS) $(TESTS) +OBJS := $(patsubst %,$(OUTPUT)/%,$(OBJS)) +TESTS := $(patsubst %,$(OUTPUT)/%,$(TESTS)) + +$(TEST_CUSTOM_PROGS): $(TESTS) $(OBJS) + $(CC) -o $(TEST_CUSTOM_PROGS) $(OBJS) $(TESTS) $(CFLAGS) $(LDFLAGS) + +$(OBJS): $(OUTPUT)/%.o: %.c + $(CC) -c $^ -o $@ + +$(TESTS): $(OUTPUT)/%.o: %.c + $(CC) -c $^ -o $@ -EXTRA_CLEAN := sync_test $(OBJS) $(TESTS) +EXTRA_CLEAN := $(TEST_CUSTOM_PROGS) $(OBJS) $(TESTS) diff --git a/tools/testing/selftests/sync/sync_test.c b/tools/testing/selftests/sync/sync_test.c index 9ea08d9f0b13..7f7938263c5c 100644 --- a/tools/testing/selftests/sync/sync_test.c +++ b/tools/testing/selftests/sync/sync_test.c @@ -29,51 +29,85 @@ #include <unistd.h> #include <stdlib.h> #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 void sync_api_supported(void) +{ + struct stat sbuf; + int ret; + + 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; + + 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/Makefile b/tools/testing/selftests/sysctl/Makefile index b3c33e071f10..95c320b354e8 100644 --- a/tools/testing/selftests/sysctl/Makefile +++ b/tools/testing/selftests/sysctl/Makefile @@ -4,8 +4,7 @@ # No binaries, but make sure arg-less "make" doesn't trigger "run_tests". all: -TEST_PROGS := run_numerictests run_stringtests -TEST_FILES := common_tests +TEST_PROGS := sysctl.sh include ../lib.mk diff --git a/tools/testing/selftests/sysctl/common_tests b/tools/testing/selftests/sysctl/common_tests deleted file mode 100644 index 17d534b1b7b4..000000000000 --- a/tools/testing/selftests/sysctl/common_tests +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/sh - -TEST_FILE=$(mktemp) - -echo "== Testing sysctl behavior against ${TARGET} ==" - -set_orig() -{ - echo "${ORIG}" > "${TARGET}" -} - -set_test() -{ - echo "${TEST_STR}" > "${TARGET}" -} - -verify() -{ - local seen - seen=$(cat "$1") - if [ "${seen}" != "${TEST_STR}" ]; then - return 1 - fi - return 0 -} - -trap 'set_orig; rm -f "${TEST_FILE}"' EXIT - -rc=0 - -echo -n "Writing test file ... " -echo "${TEST_STR}" > "${TEST_FILE}" -if ! verify "${TEST_FILE}"; then - echo "FAIL" >&2 - exit 1 -else - echo "ok" -fi - -echo -n "Checking sysctl is not set to test value ... " -if verify "${TARGET}"; then - echo "FAIL" >&2 - exit 1 -else - echo "ok" -fi - -echo -n "Writing sysctl from shell ... " -set_test -if ! verify "${TARGET}"; then - echo "FAIL" >&2 - exit 1 -else - echo "ok" -fi - -echo -n "Resetting sysctl to original value ... " -set_orig -if verify "${TARGET}"; then - echo "FAIL" >&2 - exit 1 -else - echo "ok" -fi - -# Now that we've validated the sanity of "set_test" and "set_orig", -# we can use those functions to set starting states before running -# specific behavioral tests. - -echo -n "Writing entire sysctl in single write ... " -set_orig -dd if="${TEST_FILE}" of="${TARGET}" bs=4096 2>/dev/null -if ! verify "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Writing middle of sysctl after synchronized seek ... " -set_test -dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 skip=1 2>/dev/null -if ! verify "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Writing beyond end of sysctl ... " -set_orig -dd if="${TEST_FILE}" of="${TARGET}" bs=20 seek=2 2>/dev/null -if verify "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Writing sysctl with multiple long writes ... " -set_orig -(perl -e 'print "A" x 50;'; echo "${TEST_STR}") | \ - dd of="${TARGET}" bs=50 2>/dev/null -if verify "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi diff --git a/tools/testing/selftests/sysctl/config b/tools/testing/selftests/sysctl/config new file mode 100644 index 000000000000..6ca14800d755 --- /dev/null +++ b/tools/testing/selftests/sysctl/config @@ -0,0 +1 @@ +CONFIG_TEST_SYSCTL=y diff --git a/tools/testing/selftests/sysctl/run_numerictests b/tools/testing/selftests/sysctl/run_numerictests deleted file mode 100755 index 8510f93f2d14..000000000000 --- a/tools/testing/selftests/sysctl/run_numerictests +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -SYSCTL="/proc/sys" -TARGET="${SYSCTL}/vm/swappiness" -ORIG=$(cat "${TARGET}") -TEST_STR=$(( $ORIG + 1 )) - -. ./common_tests - -exit $rc diff --git a/tools/testing/selftests/sysctl/run_stringtests b/tools/testing/selftests/sysctl/run_stringtests deleted file mode 100755 index 90a9293d520c..000000000000 --- a/tools/testing/selftests/sysctl/run_stringtests +++ /dev/null @@ -1,77 +0,0 @@ -#!/bin/sh - -SYSCTL="/proc/sys" -TARGET="${SYSCTL}/kernel/domainname" -ORIG=$(cat "${TARGET}") -TEST_STR="Testing sysctl" - -. ./common_tests - -# Only string sysctls support seeking/appending. -MAXLEN=65 - -echo -n "Writing entire sysctl in short writes ... " -set_orig -dd if="${TEST_FILE}" of="${TARGET}" bs=1 2>/dev/null -if ! verify "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Writing middle of sysctl after unsynchronized seek ... " -set_test -dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 2>/dev/null -if verify "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Checking sysctl maxlen is at least $MAXLEN ... " -set_orig -perl -e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \ - dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null -if ! grep -q B "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Checking sysctl keeps original string on overflow append ... " -set_orig -perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \ - dd of="${TARGET}" bs=$(( MAXLEN - 1 )) 2>/dev/null -if grep -q B "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Checking sysctl stays NULL terminated on write ... " -set_orig -perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \ - dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null -if grep -q B "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -echo -n "Checking sysctl stays NULL terminated on overwrite ... " -set_orig -perl -e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \ - dd of="${TARGET}" bs=$(( $MAXLEN + 1 )) 2>/dev/null -if grep -q B "${TARGET}"; then - echo "FAIL" >&2 - rc=1 -else - echo "ok" -fi - -exit $rc diff --git a/tools/testing/selftests/sysctl/sysctl.sh b/tools/testing/selftests/sysctl/sysctl.sh new file mode 100755 index 000000000000..ec232c3cfcaa --- /dev/null +++ b/tools/testing/selftests/sysctl/sysctl.sh @@ -0,0 +1,774 @@ +#!/bin/bash +# Copyright (C) 2017 Luis R. Rodriguez <mcgrof@kernel.org> +# +# 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; or, when distributed separately from the Linux kernel or +# when incorporated into other software packages, subject to the following +# license: +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of copyleft-next (version 0.3.1 or later) as published +# at http://copyleft-next.org/. + +# This performs a series tests against the proc sysctl interface. + +TEST_NAME="sysctl" +TEST_DRIVER="test_${TEST_NAME}" +TEST_DIR=$(dirname $0) +TEST_FILE=$(mktemp) + +# This represents +# +# TEST_ID:TEST_COUNT:ENABLED +# +# TEST_ID: is the test id number +# TEST_COUNT: number of times we should run the test +# ENABLED: 1 if enabled, 0 otherwise +# +# Once these are enabled please leave them as-is. Write your own test, +# we have tons of space. +ALL_TESTS="0001:1:1" +ALL_TESTS="$ALL_TESTS 0002:1:1" +ALL_TESTS="$ALL_TESTS 0003:1:1" +ALL_TESTS="$ALL_TESTS 0004:1:1" +ALL_TESTS="$ALL_TESTS 0005:3:1" + +test_modprobe() +{ + if [ ! -d $DIR ]; then + echo "$0: $DIR not present" >&2 + echo "You must have the following enabled in your kernel:" >&2 + cat $TEST_DIR/config >&2 + exit 1 + fi +} + +function allow_user_defaults() +{ + if [ -z $DIR ]; then + DIR="/sys/module/test_sysctl/" + fi + if [ -z $DEFAULT_NUM_TESTS ]; then + DEFAULT_NUM_TESTS=50 + fi + if [ -z $SYSCTL ]; then + SYSCTL="/proc/sys/debug/test_sysctl" + fi + if [ -z $PROD_SYSCTL ]; then + PROD_SYSCTL="/proc/sys" + fi + if [ -z $WRITES_STRICT ]; then + WRITES_STRICT="${PROD_SYSCTL}/kernel/sysctl_writes_strict" + fi +} + +function check_production_sysctl_writes_strict() +{ + echo -n "Checking production write strict setting ... " + if [ ! -e ${WRITES_STRICT} ]; then + echo "FAIL, but skip in case of old kernel" >&2 + else + old_strict=$(cat ${WRITES_STRICT}) + if [ "$old_strict" = "1" ]; then + echo "ok" + else + echo "FAIL, strict value is 0 but force to 1 to continue" >&2 + echo "1" > ${WRITES_STRICT} + fi + fi + + if [ -z $PAGE_SIZE ]; then + PAGE_SIZE=$(getconf PAGESIZE) + fi + if [ -z $MAX_DIGITS ]; then + MAX_DIGITS=$(($PAGE_SIZE/8)) + fi + if [ -z $INT_MAX ]; then + INT_MAX=$(getconf INT_MAX) + fi + if [ -z $UINT_MAX ]; then + UINT_MAX=$(getconf UINT_MAX) + fi +} + +test_reqs() +{ + uid=$(id -u) + if [ $uid -ne 0 ]; then + echo $msg must be run as root >&2 + exit 0 + fi + + if ! which perl 2> /dev/null > /dev/null; then + echo "$0: You need perl installed" + exit 1 + fi + if ! which getconf 2> /dev/null > /dev/null; then + echo "$0: You need getconf installed" + exit 1 + fi + if ! which diff 2> /dev/null > /dev/null; then + echo "$0: You need diff installed" + exit 1 + fi +} + +function load_req_mod() +{ + trap "test_modprobe" EXIT + + if [ ! -d $DIR ]; then + modprobe $TEST_DRIVER + if [ $? -ne 0 ]; then + exit + fi + fi +} + +reset_vals() +{ + VAL="" + TRIGGER=$(basename ${TARGET}) + case "$TRIGGER" in + int_0001) + VAL="60" + ;; + int_0002) + VAL="1" + ;; + uint_0001) + VAL="314" + ;; + string_0001) + VAL="(none)" + ;; + *) + ;; + esac + echo -n $VAL > $TARGET +} + +set_orig() +{ + if [ ! -z $TARGET ]; then + echo "${ORIG}" > "${TARGET}" + fi +} + +set_test() +{ + echo "${TEST_STR}" > "${TARGET}" +} + +verify() +{ + local seen + seen=$(cat "$1") + if [ "${seen}" != "${TEST_STR}" ]; then + return 1 + fi + return 0 +} + +verify_diff_w() +{ + echo "$TEST_STR" | diff -q -w -u - $1 + return $? +} + +test_rc() +{ + if [[ $rc != 0 ]]; then + echo "Failed test, return value: $rc" >&2 + exit $rc + fi +} + +test_finish() +{ + set_orig + rm -f "${TEST_FILE}" + + if [ ! -z ${old_strict} ]; then + echo ${old_strict} > ${WRITES_STRICT} + fi + exit $rc +} + +run_numerictests() +{ + echo "== Testing sysctl behavior against ${TARGET} ==" + + rc=0 + + echo -n "Writing test file ... " + echo "${TEST_STR}" > "${TEST_FILE}" + if ! verify "${TEST_FILE}"; then + echo "FAIL" >&2 + exit 1 + else + echo "ok" + fi + + echo -n "Checking sysctl is not set to test value ... " + if verify "${TARGET}"; then + echo "FAIL" >&2 + exit 1 + else + echo "ok" + fi + + echo -n "Writing sysctl from shell ... " + set_test + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + exit 1 + else + echo "ok" + fi + + echo -n "Resetting sysctl to original value ... " + set_orig + if verify "${TARGET}"; then + echo "FAIL" >&2 + exit 1 + else + echo "ok" + fi + + # Now that we've validated the sanity of "set_test" and "set_orig", + # we can use those functions to set starting states before running + # specific behavioral tests. + + echo -n "Writing entire sysctl in single write ... " + set_orig + dd if="${TEST_FILE}" of="${TARGET}" bs=4096 2>/dev/null + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Writing middle of sysctl after synchronized seek ... " + set_test + dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 skip=1 2>/dev/null + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Writing beyond end of sysctl ... " + set_orig + dd if="${TEST_FILE}" of="${TARGET}" bs=20 seek=2 2>/dev/null + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Writing sysctl with multiple long writes ... " + set_orig + (perl -e 'print "A" x 50;'; echo "${TEST_STR}") | \ + dd of="${TARGET}" bs=50 2>/dev/null + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc +} + +# Your test must accept digits 3 and 4 to use this +run_limit_digit() +{ + echo -n "Checking ignoring spaces up to PAGE_SIZE works on write ..." + reset_vals + + LIMIT=$((MAX_DIGITS -1)) + TEST_STR="3" + (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \ + dd of="${TARGET}" 2>/dev/null + + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Checking passing PAGE_SIZE of spaces fails on write ..." + reset_vals + + LIMIT=$((MAX_DIGITS)) + TEST_STR="4" + (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \ + dd of="${TARGET}" 2>/dev/null + + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc +} + +# You are using an int +run_limit_digit_int() +{ + echo -n "Testing INT_MAX works ..." + reset_vals + TEST_STR="$INT_MAX" + echo -n $TEST_STR > $TARGET + + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing INT_MAX + 1 will fail as expected..." + reset_vals + let TEST_STR=$INT_MAX+1 + echo -n $TEST_STR > $TARGET 2> /dev/null + + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing negative values will work as expected..." + reset_vals + TEST_STR="-3" + echo -n $TEST_STR > $TARGET 2> /dev/null + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc +} + +# You used an int array +run_limit_digit_int_array() +{ + echo -n "Testing array works as expected ... " + TEST_STR="4 3 2 1" + echo -n $TEST_STR > $TARGET + + if ! verify_diff_w "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing skipping trailing array elements works ... " + # Do not reset_vals, carry on the values from the last test. + # If we only echo in two digits the last two are left intact + TEST_STR="100 101" + echo -n $TEST_STR > $TARGET + # After we echo in, to help diff we need to set on TEST_STR what + # we expect the result to be. + TEST_STR="100 101 2 1" + + if ! verify_diff_w "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing PAGE_SIZE limit on array works ... " + # Do not reset_vals, carry on the values from the last test. + # Even if you use an int array, you are still restricted to + # MAX_DIGITS, this is a known limitation. Test limit works. + LIMIT=$((MAX_DIGITS -1)) + TEST_STR="9" + (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \ + dd of="${TARGET}" 2>/dev/null + + TEST_STR="9 101 2 1" + if ! verify_diff_w "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing exceeding PAGE_SIZE limit fails as expected ... " + # Do not reset_vals, carry on the values from the last test. + # Now go over limit. + LIMIT=$((MAX_DIGITS)) + TEST_STR="7" + (perl -e 'print " " x '$LIMIT';'; echo "${TEST_STR}") | \ + dd of="${TARGET}" 2>/dev/null + + TEST_STR="7 101 2 1" + if verify_diff_w "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc +} + +# You are using an unsigned int +run_limit_digit_uint() +{ + echo -n "Testing UINT_MAX works ..." + reset_vals + TEST_STR="$UINT_MAX" + echo -n $TEST_STR > $TARGET + + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing UINT_MAX + 1 will fail as expected..." + reset_vals + TEST_STR=$(($UINT_MAX+1)) + echo -n $TEST_STR > $TARGET 2> /dev/null + + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc + + echo -n "Testing negative values will not work as expected ..." + reset_vals + TEST_STR="-3" + echo -n $TEST_STR > $TARGET 2> /dev/null + + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + test_rc +} + +run_stringtests() +{ + echo -n "Writing entire sysctl in short writes ... " + set_orig + dd if="${TEST_FILE}" of="${TARGET}" bs=1 2>/dev/null + if ! verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Writing middle of sysctl after unsynchronized seek ... " + set_test + dd if="${TEST_FILE}" of="${TARGET}" bs=1 seek=1 2>/dev/null + if verify "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Checking sysctl maxlen is at least $MAXLEN ... " + set_orig + perl -e 'print "A" x ('"${MAXLEN}"'-2), "B";' | \ + dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null + if ! grep -q B "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Checking sysctl keeps original string on overflow append ... " + set_orig + perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \ + dd of="${TARGET}" bs=$(( MAXLEN - 1 )) 2>/dev/null + if grep -q B "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Checking sysctl stays NULL terminated on write ... " + set_orig + perl -e 'print "A" x ('"${MAXLEN}"'-1), "B";' | \ + dd of="${TARGET}" bs="${MAXLEN}" 2>/dev/null + if grep -q B "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + echo -n "Checking sysctl stays NULL terminated on overwrite ... " + set_orig + perl -e 'print "A" x ('"${MAXLEN}"'-1), "BB";' | \ + dd of="${TARGET}" bs=$(( $MAXLEN + 1 )) 2>/dev/null + if grep -q B "${TARGET}"; then + echo "FAIL" >&2 + rc=1 + else + echo "ok" + fi + + test_rc +} + +sysctl_test_0001() +{ + TARGET="${SYSCTL}/int_0001" + reset_vals + ORIG=$(cat "${TARGET}") + TEST_STR=$(( $ORIG + 1 )) + + run_numerictests + run_limit_digit +} + +sysctl_test_0002() +{ + TARGET="${SYSCTL}/string_0001" + reset_vals + ORIG=$(cat "${TARGET}") + TEST_STR="Testing sysctl" + # Only string sysctls support seeking/appending. + MAXLEN=65 + + run_numerictests + run_stringtests +} + +sysctl_test_0003() +{ + TARGET="${SYSCTL}/int_0002" + reset_vals + ORIG=$(cat "${TARGET}") + TEST_STR=$(( $ORIG + 1 )) + + run_numerictests + run_limit_digit + run_limit_digit_int +} + +sysctl_test_0004() +{ + TARGET="${SYSCTL}/uint_0001" + reset_vals + ORIG=$(cat "${TARGET}") + TEST_STR=$(( $ORIG + 1 )) + + run_numerictests + run_limit_digit + run_limit_digit_uint +} + +sysctl_test_0005() +{ + TARGET="${SYSCTL}/int_0003" + reset_vals + ORIG=$(cat "${TARGET}") + + run_limit_digit_int_array +} + +list_tests() +{ + echo "Test ID list:" + echo + echo "TEST_ID x NUM_TEST" + echo "TEST_ID: Test ID" + echo "NUM_TESTS: Number of recommended times to run the test" + echo + echo "0001 x $(get_test_count 0001) - tests proc_dointvec_minmax()" + echo "0002 x $(get_test_count 0002) - tests proc_dostring()" + echo "0003 x $(get_test_count 0003) - tests proc_dointvec()" + echo "0004 x $(get_test_count 0004) - tests proc_douintvec()" + echo "0005 x $(get_test_count 0005) - tests proc_douintvec() array" +} + +test_reqs + +usage() +{ + NUM_TESTS=$(grep -o ' ' <<<"$ALL_TESTS" | grep -c .) + let NUM_TESTS=$NUM_TESTS+1 + MAX_TEST=$(printf "%04d\n" $NUM_TESTS) + echo "Usage: $0 [ -t <4-number-digit> ] | [ -w <4-number-digit> ] |" + echo " [ -s <4-number-digit> ] | [ -c <4-number-digit> <test- count>" + echo " [ all ] [ -h | --help ] [ -l ]" + echo "" + echo "Valid tests: 0001-$MAX_TEST" + echo "" + 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 " -l List all test ID list" + echo " -h|--help Help" + echo + echo "If an error every occurs execution will immediately terminate." + echo "If you are adding a new test try using -w <test-ID> first to" + echo "make sure the test passes a series of tests." + echo + echo Example uses: + echo + echo "$TEST_NAME.sh -- executes all tests" + echo "$TEST_NAME.sh -t 0002 -- Executes test ID 0002 number of times is recomended" + echo "$TEST_NAME.sh -w 0002 -- Watch test ID 0002 run until an error occurs" + echo "$TEST_NAME.sh -s 0002 -- Run test ID 0002 once" + echo "$TEST_NAME.sh -c 0002 3 -- Run test ID 0002 three times" + echo + list_tests + exit 1 +} + +function test_num() +{ + re='^[0-9]+$' + if ! [[ $1 =~ $re ]]; then + usage + fi +} + +function get_test_count() +{ + test_num $1 + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + LAST_TWO=${TEST_DATA#*:*} + echo ${LAST_TWO%:*} +} + +function get_test_enabled() +{ + test_num $1 + TEST_DATA=$(echo $ALL_TESTS | awk '{print $'$1'}') + echo ${TEST_DATA#*:*:} +} + +function run_all_tests() +{ + for i in $ALL_TESTS ; do + TEST_ID=${i%:*:*} + ENABLED=$(get_test_enabled $TEST_ID) + TEST_COUNT=$(get_test_count $TEST_ID) + if [[ $ENABLED -eq "1" ]]; then + test_case $TEST_ID $TEST_COUNT + fi + done +} + +function watch_log() +{ + if [ $# -ne 3 ]; then + clear + fi + date + echo "Running test: $2 - run #$1" +} + +function watch_case() +{ + i=0 + while [ 1 ]; do + + if [ $# -eq 1 ]; then + test_num $1 + watch_log $i ${TEST_NAME}_test_$1 + ${TEST_NAME}_test_$1 + else + watch_log $i all + run_all_tests + fi + let i=$i+1 + done +} + +function test_case() +{ + NUM_TESTS=$DEFAULT_NUM_TESTS + if [ $# -eq 2 ]; then + NUM_TESTS=$2 + fi + + i=0 + while [ $i -lt $NUM_TESTS ]; do + test_num $1 + watch_log $i ${TEST_NAME}_test_$1 noclear + RUN_TEST=${TEST_NAME}_test_$1 + $RUN_TEST + let i=$i+1 + done +} + +function parse_args() +{ + if [ $# -eq 0 ]; then + run_all_tests + else + if [[ "$1" = "all" ]]; then + run_all_tests + elif [[ "$1" = "-w" ]]; then + shift + watch_case $@ + elif [[ "$1" = "-t" ]]; then + shift + test_num $1 + test_case $1 $(get_test_count $1) + elif [[ "$1" = "-c" ]]; then + shift + test_num $1 + test_num $2 + test_case $1 $2 + elif [[ "$1" = "-s" ]]; then + shift + test_case $1 1 + elif [[ "$1" = "-l" ]]; then + list_tests + elif [[ "$1" = "-h" || "$1" = "--help" ]]; then + usage + else + usage + fi + fi +} + +test_reqs +allow_user_defaults +check_production_sysctl_writes_strict +load_req_mod + +trap "test_finish" EXIT + +parse_args $@ + +exit 0 diff --git a/tools/testing/selftests/tc-testing/.gitignore b/tools/testing/selftests/tc-testing/.gitignore new file mode 100644 index 000000000000..c18dd8d83cee --- /dev/null +++ b/tools/testing/selftests/tc-testing/.gitignore @@ -0,0 +1 @@ +__pycache__/ diff --git a/tools/testing/selftests/tc-testing/README b/tools/testing/selftests/tc-testing/README new file mode 100644 index 000000000000..970ff294fec8 --- /dev/null +++ b/tools/testing/selftests/tc-testing/README @@ -0,0 +1,102 @@ +tdc - Linux Traffic Control (tc) unit testing suite + +Author: Lucas Bates - lucasb@mojatatu.com + +tdc is a Python script to load tc unit tests from a separate JSON file and +execute them inside a network namespace dedicated to the task. + + +REQUIREMENTS +------------ + +* Minimum Python version of 3.4. Earlier 3.X versions may work but are not + guaranteed. + +* The kernel must have network namespace support + +* The kernel must have veth support available, as a veth pair is created + prior to running the tests. + +* All tc-related features must be built in or available as modules. + To check what is required in current setup run: + ./tdc.py -c + + Note: + In the current release, tdc run will abort due to a failure in setup or + teardown commands - which includes not being able to run a test simply + because the kernel did not support a specific feature. (This will be + handled in a future version - the current workaround is to run the tests + on specific test categories that your kernel supports) + + +BEFORE YOU RUN +-------------- + +The path to the tc executable that will be most commonly tested can be defined +in the tdc_config.py file. Find the 'TC' entry in the NAMES dictionary and +define the path. + +If you need to test a different tc executable on the fly, you can do so by +using the -p option when running tdc: + ./tdc.py -p /path/to/tc + + +RUNNING TDC +----------- + +To use tdc, root privileges are required. tdc will not run otherwise. + +All tests are executed inside a network namespace to prevent conflicts +within the host. + +Running tdc without any arguments will run all tests. Refer to the section +on command line arguments for more information, or run: + ./tdc.py -h + +tdc will list the test names as they are being run, and print a summary in +TAP (Test Anything Protocol) format when they are done. If tests fail, +output captured from the failing test will be printed immediately following +the failed test in the TAP output. + + +USER-DEFINED CONSTANTS +---------------------- + +The tdc_config.py file contains multiple values that can be altered to suit +your needs. Any value in the NAMES dictionary can be altered without affecting +the tests to be run. These values are used in the tc commands that will be +executed as part of the test. More will be added as test cases require. + +Example: + $TC qdisc add dev $DEV1 ingress + + +COMMAND LINE ARGUMENTS +---------------------- + +Run tdc.py -h to see the full list of available arguments. + +-p PATH Specify the tc executable located at PATH to be used on this + test run +-c Show the available test case categories in this test file +-c CATEGORY Run only tests that belong to CATEGORY +-f FILE Read test cases from the JSON file named FILE +-l [CATEGORY] List all test cases in the JSON file. If CATEGORY is + specified, list test cases matching that category. +-s ID Show the test case matching ID +-e ID Execute the test case identified by ID +-i Generate unique ID numbers for test cases with no existing + ID number + + +ACKNOWLEDGEMENTS +---------------- + +Thanks to: + +Jamal Hadi Salim, for providing valuable test cases +Keara Leibovitz, who wrote the CLI test driver that I used as a base for the + first version of the tc testing suite. This work was presented at + Netdev 1.2 Tokyo in October 2016. +Samir Hussain, for providing help while I dove into Python for the first time + and being a second eye for this code. diff --git a/tools/testing/selftests/tc-testing/TODO.txt b/tools/testing/selftests/tc-testing/TODO.txt new file mode 100644 index 000000000000..6a266d811a78 --- /dev/null +++ b/tools/testing/selftests/tc-testing/TODO.txt @@ -0,0 +1,10 @@ +tc Testing Suite To-Do list: + +- Determine what tc features are supported in the kernel. If features are not + present, prevent the related categories from running. + +- Add support for multiple versions of tc to run successively + +- Improve error messages when tdc aborts its run + +- Allow tdc to write its results to file diff --git a/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt b/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt new file mode 100644 index 000000000000..4e09257bc443 --- /dev/null +++ b/tools/testing/selftests/tc-testing/creating-testcases/AddingTestCases.txt @@ -0,0 +1,69 @@ +tdc - Adding test cases for tdc + +Author: Lucas Bates - lucasb@mojatatu.com + +ADDING TEST CASES +----------------- + +User-defined tests should be added by defining a separate JSON file. This +will help prevent conflicts when updating the repository. Refer to +template.json for the required JSON format for test cases. + +Include the 'id' field, but do not assign a value. Running tdc with the -i +option will generate a unique ID for that test case. + +tdc will recursively search the 'tc' subdirectory for .json files. Any +test case files you create in these directories will automatically be included. +If you wish to store your custom test cases elsewhere, be sure to run tdc +with the -f argument and the path to your file. + +Be aware of required escape characters in the JSON data - particularly when +defining the match pattern. Refer to the tctests.json file for examples when +in doubt. + + +TEST CASE STRUCTURE +------------------- + +Each test case has required data: + +id: A unique alphanumeric value to identify a particular test case +name: Descriptive name that explains the command under test +category: A list of single-word descriptions covering what the command + under test is testing. Example: filter, actions, u32, gact, etc. +setup: The list of commands required to ensure the command under test + succeeds. For example: if testing a filter, the command to create + the qdisc would appear here. +cmdUnderTest: The tc command being tested itself. +expExitCode: The code returned by the command under test upon its termination. + tdc will compare this value against the actual returned value. +verifyCmd: The tc command to be run to verify successful execution. + For example: if the command under test creates a gact action, + verifyCmd should be "$TC actions show action gact" +matchPattern: A regular expression to be applied against the output of the + verifyCmd to prove the command under test succeeded. This pattern + should be as specific as possible so that a false positive is not + matched. +matchCount: How many times the regex in matchPattern should match. A value + of 0 is acceptable. +teardown: The list of commands to clean up after the test is completed. + The environment should be returned to the same state as when + this test was started: qdiscs deleted, actions flushed, etc. + + +SETUP/TEARDOWN ERRORS +--------------------- + +If an error is detected during the setup/teardown process, execution of the +tests will immediately stop with an error message and the namespace in which +the tests are run will be destroyed. This is to prevent inaccurate results +in the test cases. + +Repeated failures of the setup/teardown may indicate a problem with the test +case, or possibly even a bug in one of the commands that are not being tested. + +It's possible to include acceptable exit codes with the setup/teardown command +so that it doesn't halt the script for an error that doesn't matter. Turn the +individual command into a list, with the command being first, followed by all +acceptable exit codes for the command. + diff --git a/tools/testing/selftests/tc-testing/creating-testcases/template.json b/tools/testing/selftests/tc-testing/creating-testcases/template.json new file mode 100644 index 000000000000..87971744bdd4 --- /dev/null +++ b/tools/testing/selftests/tc-testing/creating-testcases/template.json @@ -0,0 +1,40 @@ +[ + { + "id": "", + "name": "", + "category": [ + "", + "" + ], + "setup": [ + "" + ], + "cmdUnderTest": "", + "expExitCode": "", + "verifyCmd": "", + "matchPattern": "", + "matchCount": "", + "teardown": [ + "" + ] + }, + { + "id": "", + "name": "", + "category": [ + "", + "" + ], + "setup": [ + "" + ], + "cmdUnderTest": "", + "expExitCode": "", + "verifyCmd": "", + "matchPattern": "", + "matchCount": "", + "teardown": [ + "" + ] + } +] diff --git a/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json b/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json new file mode 100644 index 000000000000..6973bdc5b5bf --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/actions/tests.json @@ -0,0 +1,1165 @@ +[ + { + "id": "e89a", + "name": "Add valid pass action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pass index 8", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pass.*index 8 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "a02c", + "name": "Add valid pipe action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pipe index 6", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pipe.*index 6 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "feef", + "name": "Add valid reclassify action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action reclassify index 5", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action reclassify.*index 5 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "8a7a", + "name": "Add valid drop action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action drop index 30", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 30 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "9a52", + "name": "Add valid continue action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action continue index 432", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action continue.*index 432 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "d700", + "name": "Add invalid action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action pump index 386", + "expExitCode": "255", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action.*index 386 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "9215", + "name": "Add action with duplicate index", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pipe index 15" + ], + "cmdUnderTest": "$TC actions add action drop index 15", + "expExitCode": "255", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 15 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "798e", + "name": "Add action with index exceeding 32-bit maximum", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action drop index 4294967296", + "expExitCode": "255", + "verifyCmd": "actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 4294967296 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "22be", + "name": "Add action with index at 32-bit maximum", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action drop index 4294967295", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 4294967295 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "ac2a", + "name": "List actions", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action reclassify index 101", + "$TC actions add action reclassify index 102", + "$TC actions add action reclassify index 103", + "$TC actions add action reclassify index 104", + "$TC actions add action reclassify index 105" + ], + "cmdUnderTest": "$TC actions list action gact", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action reclassify", + "matchCount": "5", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "63ec", + "name": "Delete pass action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pass index 1" + ], + "cmdUnderTest": "$TC actions del action gact index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pass.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "46be", + "name": "Delete pipe action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action pipe index 9" + ], + "cmdUnderTest": "$TC actions del action gact index 9", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action pipe.*index 9 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "2e08", + "name": "Delete reclassify action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action reclassify index 65536" + ], + "cmdUnderTest": "$TC actions del action gact index 65536", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action reclassify.*index 65536 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "99c4", + "name": "Delete drop action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action drop index 16" + ], + "cmdUnderTest": "$TC actions del action gact index 16", + "expExitCode": "0", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action drop.*index 16 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "fb6b", + "name": "Delete continue action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action continue index 32" + ], + "cmdUnderTest": "$TC actions del action gact index 32", + "expExitCode": "0", + "verifyCmd": "actions list action gact", + "matchPattern": "action order [0-9]*: gact action continue.*index 32 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "0eb3", + "name": "Delete non-existent action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions del action gact index 2", + "expExitCode": "255", + "verifyCmd": "$TC actions list action gact", + "matchPattern": "action order [0-9]*: gact action", + "matchCount": "0", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "5124", + "name": "Add mirred mirror to egress action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress mirror index 1 dev lo", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Mirror to device lo\\).*index 1 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "6fb4", + "name": "Add mirred redirect to egress action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress redirect index 2 dev lo action pipe", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Redirect to device lo\\).*index 2 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "ba38", + "name": "Get mirred actions", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress mirror index 1 dev lo", + "$TC actions add action mirred egress redirect index 2 dev lo" + ], + "cmdUnderTest": "$TC actions show action mirred", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "[Mirror|Redirect] to device lo", + "matchCount": "2", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "d7c0", + "name": "Add invalid mirred direction", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred inbound mirror index 20 dev lo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(.*to device lo\\).*index 20 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "e213", + "name": "Add invalid mirred action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress remirror index 20 dev lo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress.*to device lo\\).*index 20 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "2d89", + "name": "Add mirred action with invalid device", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action mirred egress mirror index 20 dev eltoh", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(.*to device eltoh\\).*index 20 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "300b", + "name": "Add mirred action with duplicate index", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress redirect index 15 dev lo" + ], + "cmdUnderTest": "$TC actions add action mirred egress mirror index 15 dev lo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(.*to device lo\\).*index 15 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "a70e", + "name": "Delete mirred mirror action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress mirror index 5 dev lo" + ], + "cmdUnderTest": "$TC actions del action mirred index 5", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Mirror to device lo\\).*index 5 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "3fb3", + "name": "Delete mirred redirect action", + "category": [ + "actions", + "mirred" + ], + "setup": [ + [ + "$TC actions flush action mirred", + 0, + 1, + 255 + ], + "$TC actions add action mirred egress redirect index 5 dev lo" + ], + "cmdUnderTest": "$TC actions del action mirred index 5", + "expExitCode": "0", + "verifyCmd": "$TC actions list action mirred", + "matchPattern": "action order [0-9]*: mirred \\(Egress Redirect to device lo\\).*index 5 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action mirred" + ] + }, + { + "id": "b078", + "name": "Add simple action", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action simple sdata \"A triumph\" index 60", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <A triumph>.*index 60 ref", + "matchCount": "1", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "6d4c", + "name": "Add simple action with duplicate index", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Aruba\" index 4" + ], + "cmdUnderTest": "$TC actions add action simple sdata \"Jamaica\" index 4", + "expExitCode": "255", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <Jamaica>.*ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "2542", + "name": "List simple actions", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Rock\"", + "$TC actions add action simple sdata \"Paper\"", + "$TC actions add action simple sdata \"Scissors\" index 98" + ], + "cmdUnderTest": "$TC actions list action simple", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <[A-Z][a-z]*>", + "matchCount": "3", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "ea67", + "name": "Delete simple action", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Blinkenlights\" index 1" + ], + "cmdUnderTest": "$TC actions delete action simple index 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <Blinkenlights>.*index 1 ref", + "matchCount": "0", + "teardown": [ + "$TC actions flush action simple" + ] + }, + { + "id": "8ff1", + "name": "Flush simple actions", + "category": [ + "actions", + "simple" + ], + "setup": [ + [ + "$TC actions flush action simple", + 0, + 1, + 255 + ], + "$TC actions add action simple sdata \"Kirk\"", + "$TC actions add action simple sdata \"Spock\" index 50", + "$TC actions add action simple sdata \"McCoy\" index 9" + ], + "cmdUnderTest": "$TC actions flush action simple", + "expExitCode": "0", + "verifyCmd": "$TC actions list action simple", + "matchPattern": "action order [0-9]*: Simple <[A-Z][a-z]*>", + "matchCount": "0", + "teardown": [ + "" + ] + }, + { + "id": "6236", + "name": "Add skbedit action with valid mark", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 1", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit mark 1", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "407b", + "name": "Add skbedit action with invalid mark", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 666777888999", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit mark", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "081d", + "name": "Add skbedit action with priority", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit prio 99", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit priority :99", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "cc37", + "name": "Add skbedit action with invalid priority", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit prio foo", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit priority", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "3c95", + "name": "Add skbedit action with queue_mapping", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit queue_mapping 909", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit queue_mapping 909", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "985c", + "name": "Add skbedit action with invalid queue_mapping", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit queue_mapping 67000", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit queue_mapping", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "224f", + "name": "Add skbedit action with ptype host", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype host", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit ptype host", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "d1a3", + "name": "Add skbedit action with ptype otherhost", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype otherhost", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit ptype otherhost", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "b9c6", + "name": "Add skbedit action with invalid ptype", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit ptype openair", + "expExitCode": "255", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit ptype openair", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "5172", + "name": "List skbedit actions", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ], + "$TC actions add action skbedit ptype otherhost", + "$TC actions add action skbedit ptype broadcast", + "$TC actions add action skbedit mark 59", + "$TC actions add action skbedit mark 409" + ], + "cmdUnderTest": "$TC actions list action skbedit", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit", + "matchCount": "4", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "a6d6", + "name": "Add skbedit action with index", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ] + ], + "cmdUnderTest": "$TC actions add action skbedit mark 808 index 4040404040", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "index 4040404040", + "matchCount": "1", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "38f3", + "name": "Delete skbedit action", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + [ + "$TC actions flush action skbedit", + 0, + 1, + 255 + ], + "$TC actions add action skbedit mark 42 index 9009" + ], + "cmdUnderTest": "$TC actions del action skbedit index 9009", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit mark 42", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "ce97", + "name": "Flush skbedit actions", + "category": [ + "actions", + "skbedit" + ], + "setup": [ + "$TC actions add action skbedit mark 500", + "$TC actions add action skbedit mark 501", + "$TC actions add action skbedit mark 502", + "$TC actions add action skbedit mark 503", + "$TC actions add action skbedit mark 504", + "$TC actions add action skbedit mark 505", + "$TC actions add action skbedit mark 506" + ], + "cmdUnderTest": "$TC actions flush action skbedit", + "expExitCode": "0", + "verifyCmd": "$TC actions list action skbedit", + "matchPattern": "action order [0-9]*: skbedit", + "matchCount": "0", + "teardown": [ + "$TC actions flush action skbedit" + ] + }, + { + "id": "f02c", + "name": "Replace gact action", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action drop index 10", + "$TC actions add action drop index 12" + ], + "cmdUnderTest": "$TC actions replace action ok index 12", + "expExitCode": "0", + "verifyCmd": "$TC actions ls action gact", + "matchPattern": "action order [0-9]*: gact action pass", + "matchCount": "1", + "teardown": [ + "$TC actions flush action gact" + ] + }, + { + "id": "525f", + "name": "Get gact action by index", + "category": [ + "actions", + "gact" + ], + "setup": [ + [ + "$TC actions flush action gact", + 0, + 1, + 255 + ], + "$TC actions add action drop index 3900800700" + ], + "cmdUnderTest": "$TC actions get action gact index 3900800700", + "expExitCode": "0", + "verifyCmd": "$TC actions get action gact index 3900800700", + "matchPattern": "index 3900800700", + "matchCount": "1", + "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/tc-testing/tc-tests/filters/tests.json b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json new file mode 100644 index 000000000000..c727b96a59b0 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tc-tests/filters/tests.json @@ -0,0 +1,21 @@ +[ + { + "id": "e9a3", + "name": "Add u32 with source match", + "category": [ + "filter", + "u32" + ], + "setup": [ + "$TC qdisc add dev $DEV1 ingress" + ], + "cmdUnderTest": "$TC filter add dev $DEV1 parent ffff: protocol ip prio 1 u32 match ip src 127.0.0.1/32 flowid 1:1 action ok", + "expExitCode": "0", + "verifyCmd": "$TC filter show dev $DEV1 parent ffff:", + "matchPattern": "match 7f000002/ffffffff at 12", + "matchCount": "0", + "teardown": [ + "$TC qdisc del dev $DEV1 ingress" + ] + } +]
\ No newline at end of file diff --git a/tools/testing/selftests/tc-testing/tdc.py b/tools/testing/selftests/tc-testing/tdc.py new file mode 100755 index 000000000000..cd61b7844c0d --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc.py @@ -0,0 +1,413 @@ +#!/usr/bin/env python3 + +""" +tdc.py - Linux tc (Traffic Control) unit test driver + +Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> +""" + +import re +import os +import sys +import argparse +import json +import subprocess +from collections import OrderedDict +from string import Template + +from tdc_config import * +from tdc_helper import * + + +USE_NS = True + + +def replace_keywords(cmd): + """ + For a given executable command, substitute any known + variables contained within NAMES with the correct values + """ + tcmd = Template(cmd) + subcmd = tcmd.safe_substitute(NAMES) + return subcmd + + +def exec_cmd(command, nsonly=True): + """ + Perform any required modifications on an executable command, then run + it in a subprocess and return the results. + """ + if (USE_NS and nsonly): + command = 'ip netns exec $NS ' + command + + if '$' in command: + command = replace_keywords(command) + + proc = subprocess.Popen(command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (rawout, serr) = proc.communicate() + + if proc.returncode != 0: + foutput = serr.decode("utf-8") + else: + foutput = rawout.decode("utf-8") + + proc.stdout.close() + proc.stderr.close() + return proc, foutput + + +def prepare_env(cmdlist): + """ + Execute the setup/teardown commands for a test case. Optionally + terminate test execution if the command fails. + """ + for cmdinfo in cmdlist: + if (type(cmdinfo) == list): + exit_codes = cmdinfo[1:] + cmd = cmdinfo[0] + else: + exit_codes = [0] + cmd = cmdinfo + + if (len(cmd) == 0): + continue + + (proc, foutput) = exec_cmd(cmd) + + if proc.returncode not in exit_codes: + print + print("Could not execute:") + print(cmd) + print("\nError message:") + print(foutput) + print("\nAborting test run.") + ns_destroy() + exit(1) + + +def test_runner(filtered_tests): + """ + Driver function for the unit tests. + + Prints information about the tests being run, executes the setup and + teardown commands and the command under test itself. Also determines + success/failure based on the information in the test case and generates + TAP output accordingly. + """ + testlist = filtered_tests + tcount = len(testlist) + index = 1 + tap = str(index) + ".." + str(tcount) + "\n" + + for tidx in testlist: + result = True + tresult = "" + print("Test " + tidx["id"] + ": " + tidx["name"]) + prepare_env(tidx["setup"]) + (p, procout) = exec_cmd(tidx["cmdUnderTest"]) + exit_code = p.returncode + + if (exit_code != int(tidx["expExitCode"])): + result = False + print("exit:", exit_code, int(tidx["expExitCode"])) + print(procout) + else: + match_pattern = re.compile(str(tidx["matchPattern"]), re.DOTALL) + (p, procout) = exec_cmd(tidx["verifyCmd"]) + match_index = re.findall(match_pattern, procout) + if len(match_index) != int(tidx["matchCount"]): + result = False + + if result == True: + tresult += "ok " + else: + tresult += "not ok " + tap += tresult + str(index) + " " + tidx["id"] + " " + tidx["name"] + "\n" + + if result == False: + tap += procout + + prepare_env(tidx["teardown"]) + index += 1 + + return tap + + +def ns_create(): + """ + Create the network namespace in which the tests will be run and set up + the required network devices for it. + """ + if (USE_NS): + cmd = 'ip netns add $NS' + exec_cmd(cmd, False) + cmd = 'ip link add $DEV0 type veth peer name $DEV1' + exec_cmd(cmd, False) + cmd = 'ip link set $DEV1 netns $NS' + exec_cmd(cmd, False) + cmd = 'ip link set $DEV0 up' + exec_cmd(cmd, False) + cmd = 'ip -s $NS link set $DEV1 up' + exec_cmd(cmd, False) + + +def ns_destroy(): + """ + Destroy the network namespace for testing (and any associated network + devices as well) + """ + if (USE_NS): + cmd = 'ip netns delete $NS' + exec_cmd(cmd, False) + + +def has_blank_ids(idlist): + """ + Search the list for empty ID fields and return true/false accordingly. + """ + return not(all(k for k in idlist)) + + +def load_from_file(filename): + """ + Open the JSON file containing the test cases and return them as an + ordered dictionary object. + """ + with open(filename) as test_data: + testlist = json.load(test_data, object_pairs_hook=OrderedDict) + idlist = get_id_list(testlist) + if (has_blank_ids(idlist)): + for k in testlist: + k['filename'] = filename + return testlist + + +def args_parse(): + """ + Create the argument parser. + """ + parser = argparse.ArgumentParser(description='Linux TC unit tests') + return parser + + +def set_args(parser): + """ + Set the command line arguments for tdc. + """ + parser.add_argument('-p', '--path', type=str, + help='The full path to the tc executable to use') + parser.add_argument('-c', '--category', type=str, nargs='?', const='+c', + help='Run tests only from the specified category, or if no category is specified, list known categories.') + parser.add_argument('-f', '--file', type=str, + help='Run tests from the specified file') + parser.add_argument('-l', '--list', type=str, nargs='?', const="", metavar='CATEGORY', + help='List all test cases, or those only within the specified category') + parser.add_argument('-s', '--show', type=str, nargs=1, metavar='ID', dest='showID', + help='Display the test case with specified id') + parser.add_argument('-e', '--execute', type=str, nargs=1, metavar='ID', + help='Execute the single test case with specified ID') + parser.add_argument('-i', '--id', action='store_true', dest='gen_id', + help='Generate ID numbers for new test cases') + return parser + return parser + + +def check_default_settings(args): + """ + Process any arguments overriding the default settings, and ensure the + settings are correct. + """ + # Allow for overriding specific settings + global NAMES + + if args.path != None: + NAMES['TC'] = args.path + if not os.path.isfile(NAMES['TC']): + print("The specified tc path " + NAMES['TC'] + " does not exist.") + exit(1) + + +def get_id_list(alltests): + """ + Generate a list of all IDs in the test cases. + """ + return [x["id"] for x in alltests] + + +def check_case_id(alltests): + """ + Check for duplicate test case IDs. + """ + idl = get_id_list(alltests) + return [x for x in idl if idl.count(x) > 1] + + +def does_id_exist(alltests, newid): + """ + Check if a given ID already exists in the list of test cases. + """ + idl = get_id_list(alltests) + return (any(newid == x for x in idl)) + + +def generate_case_ids(alltests): + """ + If a test case has a blank ID field, generate a random hex ID for it + and then write the test cases back to disk. + """ + import random + for c in alltests: + if (c["id"] == ""): + while True: + newid = str('%04x' % random.randrange(16**4)) + if (does_id_exist(alltests, newid)): + continue + else: + c['id'] = newid + break + + ufilename = [] + for c in alltests: + if ('filename' in c): + ufilename.append(c['filename']) + ufilename = get_unique_item(ufilename) + for f in ufilename: + testlist = [] + for t in alltests: + if 'filename' in t: + if t['filename'] == f: + del t['filename'] + testlist.append(t) + outfile = open(f, "w") + json.dump(testlist, outfile, indent=4) + outfile.close() + + +def get_test_cases(args): + """ + If a test case file is specified, retrieve tests from that file. + Otherwise, glob for all json files in subdirectories and load from + each one. + """ + import fnmatch + if args.file != None: + if not os.path.isfile(args.file): + print("The specified test case file " + args.file + " does not exist.") + exit(1) + flist = [args.file] + else: + flist = [] + for root, dirnames, filenames in os.walk('tc-tests'): + for filename in fnmatch.filter(filenames, '*.json'): + flist.append(os.path.join(root, filename)) + alltests = list() + for casefile in flist: + alltests = alltests + (load_from_file(casefile)) + return alltests + + +def set_operation_mode(args): + """ + Load the test case data and process remaining arguments to determine + what the script should do for this run, and call the appropriate + function. + """ + alltests = get_test_cases(args) + + if args.gen_id: + idlist = get_id_list(alltests) + if (has_blank_ids(idlist)): + alltests = generate_case_ids(alltests) + else: + print("No empty ID fields found in test files.") + exit(0) + + duplicate_ids = check_case_id(alltests) + if (len(duplicate_ids) > 0): + print("The following test case IDs are not unique:") + print(str(set(duplicate_ids))) + print("Please correct them before continuing.") + exit(1) + + ucat = get_test_categories(alltests) + + if args.showID: + show_test_case_by_id(alltests, args.showID[0]) + exit(0) + + if args.execute: + target_id = args.execute[0] + else: + target_id = "" + + if args.category: + if (args.category == '+c'): + print("Available categories:") + print_sll(ucat) + exit(0) + else: + target_category = args.category + else: + target_category = "" + + + testcases = get_categorized_testlist(alltests, ucat) + + if args.list: + if (len(args.list) == 0): + list_test_cases(alltests) + exit(0) + elif(len(args.list > 0)): + if (args.list not in ucat): + print("Unknown category " + args.list) + print("Available categories:") + print_sll(ucat) + exit(1) + list_test_cases(testcases[args.list]) + exit(0) + + if (os.geteuid() != 0): + print("This script must be run with root privileges.\n") + exit(1) + + ns_create() + + if (len(target_category) == 0): + if (len(target_id) > 0): + alltests = list(filter(lambda x: target_id in x['id'], alltests)) + if (len(alltests) == 0): + print("Cannot find a test case with ID matching " + target_id) + exit(1) + catresults = test_runner(alltests) + print("All test results: " + "\n\n" + catresults) + elif (len(target_category) > 0): + if (target_category not in ucat): + print("Specified category is not present in this file.") + exit(1) + else: + catresults = test_runner(testcases[target_category]) + print("Category " + target_category + "\n\n" + catresults) + + ns_destroy() + + +def main(): + """ + Start of execution; set up argument parser and get the arguments, + and start operations. + """ + parser = args_parse() + parser = set_args(parser) + (args, remaining) = parser.parse_known_args() + check_default_settings(args) + + set_operation_mode(args) + + exit(0) + + +if __name__ == "__main__": + main() diff --git a/tools/testing/selftests/tc-testing/tdc_config.py b/tools/testing/selftests/tc-testing/tdc_config.py new file mode 100644 index 000000000000..01087375a7c3 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc_config.py @@ -0,0 +1,17 @@ +""" +tdc_config.py - tdc user-specified values + +Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> +""" + +# Dictionary containing all values that can be substituted in executable +# commands. +NAMES = { + # Substitute your own tc path here + 'TC': '/sbin/tc', + # Name of veth devices to be created for the namespace + 'DEV0': 'v0p0', + 'DEV1': 'v0p1', + # Name of the namespace to use + 'NS': 'tcut' + } diff --git a/tools/testing/selftests/tc-testing/tdc_helper.py b/tools/testing/selftests/tc-testing/tdc_helper.py new file mode 100644 index 000000000000..c3254f861fb2 --- /dev/null +++ b/tools/testing/selftests/tc-testing/tdc_helper.py @@ -0,0 +1,75 @@ +""" +tdc_helper.py - tdc helper functions + +Copyright (C) 2017 Lucas Bates <lucasb@mojatatu.com> +""" + +def get_categorized_testlist(alltests, ucat): + """ Sort the master test list into categories. """ + testcases = dict() + + for category in ucat: + testcases[category] = list(filter(lambda x: category in x['category'], alltests)) + + return(testcases) + + +def get_unique_item(lst): + """ For a list, return a set of the unique items in the list. """ + return list(set(lst)) + + +def get_test_categories(alltests): + """ Discover all unique test categories present in the test case file. """ + ucat = [] + for t in alltests: + ucat.extend(get_unique_item(t['category'])) + ucat = get_unique_item(ucat) + return ucat + +def list_test_cases(testlist): + """ Print IDs and names of all test cases. """ + for curcase in testlist: + print(curcase['id'] + ': (' + ', '.join(curcase['category']) + ") " + curcase['name']) + + +def list_categories(testlist): + """ Show all categories that are present in a test case file. """ + categories = set(map(lambda x: x['category'], testlist)) + print("Available categories:") + print(", ".join(str(s) for s in categories)) + print("") + + +def print_list(cmdlist): + """ Print a list of strings prepended with a tab. """ + for l in cmdlist: + if (type(l) == list): + print("\t" + str(l[0])) + else: + print("\t" + str(l)) + + +def print_sll(items): + print("\n".join(str(s) for s in items)) + + +def print_test_case(tcase): + """ Pretty-printing of a given test case. """ + for k in tcase.keys(): + if (type(tcase[k]) == list): + print(k + ":") + print_list(tcase[k]) + else: + print(k + ": " + tcase[k]) + + +def show_test_case_by_id(testlist, caseID): + """ Find the specified test case to pretty-print. """ + if not any(d.get('id', None) == caseID for d in testlist): + print("That ID does not exist.") + exit(1) + else: + print_test_case(next((d for d in testlist if d['id'] == caseID))) + + diff --git a/tools/testing/selftests/timers/Makefile b/tools/testing/selftests/timers/Makefile index 5fa1d7e9a915..ae4593115408 100644 --- a/tools/testing/selftests/timers/Makefile +++ b/tools/testing/selftests/timers/Makefile @@ -1,16 +1,17 @@ -BUILD_FLAGS = -DKTEST -CFLAGS += -O3 -Wl,-no-as-needed -Wall $(BUILD_FLAGS) -LDFLAGS += -lrt -lpthread +CFLAGS += -O3 -Wl,-no-as-needed -Wall +LDFLAGS += -lrt -lpthread -lm # these are all "safe" tests that don't modify # system time or require escalated privileges 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 \ - skew_consistency clocksource-switch leap-a-day \ +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 +TEST_GEN_PROGS_EXTENDED = $(DESTRUCTIVE_TESTS) rtctest_setdate + include ../lib.mk @@ -18,15 +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 - ./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 new file mode 100644 index 000000000000..14a2b77fd012 --- /dev/null +++ b/tools/testing/selftests/timers/freq-step.c @@ -0,0 +1,271 @@ +/* + * This test checks the response of the system clock to frequency + * steps made with adjtimex(). The frequency error and stability of + * the CLOCK_MONOTONIC clock relative to the CLOCK_MONOTONIC_RAW clock + * is measured in two intervals following the step. The test fails if + * values from the second interval exceed specified limits. + * + * Copyright (C) Miroslav Lichvar <mlichvar@redhat.com> 2017 + * + * 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 <math.h> +#include <stdio.h> +#include <sys/timex.h> +#include <time.h> +#include <unistd.h> + +#include "../kselftest.h" + +#define SAMPLES 100 +#define SAMPLE_READINGS 10 +#define MEAN_SAMPLE_INTERVAL 0.1 +#define STEP_INTERVAL 1.0 +#define MAX_PRECISION 100e-9 +#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; +}; + +static time_t mono_raw_base; +static time_t mono_base; +static long user_hz; +static double precision; +static double mono_freq_offset; + +static double diff_timespec(struct timespec *ts1, struct timespec *ts2) +{ + return ts1->tv_sec - ts2->tv_sec + (ts1->tv_nsec - ts2->tv_nsec) / 1e9; +} + +static double get_sample(struct sample *sample) +{ + double delay, mindelay = 0.0; + struct timespec ts1, ts2, ts3; + int i; + + for (i = 0; i < SAMPLE_READINGS; i++) { + clock_gettime(CLOCK_MONOTONIC_RAW, &ts1); + clock_gettime(CLOCK_MONOTONIC, &ts2); + clock_gettime(CLOCK_MONOTONIC_RAW, &ts3); + + ts1.tv_sec -= mono_raw_base; + ts2.tv_sec -= mono_base; + ts3.tv_sec -= mono_raw_base; + + delay = diff_timespec(&ts3, &ts1); + if (delay <= 1e-9) { + i--; + continue; + } + + if (!i || delay < mindelay) { + sample->offset = diff_timespec(&ts2, &ts1); + sample->offset -= delay / 2.0; + sample->time = ts1.tv_sec + ts1.tv_nsec / 1e9; + mindelay = delay; + } + } + + return mindelay; +} + +static void reset_ntp_error(void) +{ + struct timex txc; + + txc.modes = ADJ_SETOFFSET; + txc.time.tv_sec = 0; + txc.time.tv_usec = 0; + + if (adjtimex(&txc) < 0) { + perror("[FAIL] adjtimex"); + ksft_exit_fail(); + } +} + +static void set_frequency(double freq) +{ + struct timex txc; + int tick_offset; + + tick_offset = 1e6 * freq / user_hz; + + txc.modes = ADJ_TICK | ADJ_FREQUENCY; + txc.tick = 1000000 / user_hz + tick_offset; + txc.freq = (1e6 * freq - user_hz * tick_offset) * (1 << 16); + + if (adjtimex(&txc) < 0) { + perror("[FAIL] adjtimex"); + ksft_exit_fail(); + } +} + +static void regress(struct sample *samples, int n, double *intercept, + double *slope, double *r_stddev, double *r_max) +{ + double x, y, r, x_sum, y_sum, xy_sum, x2_sum, r2_sum; + int i; + + x_sum = 0.0, y_sum = 0.0, xy_sum = 0.0, x2_sum = 0.0; + + for (i = 0; i < n; i++) { + x = samples[i].time; + y = samples[i].offset; + + x_sum += x; + y_sum += y; + xy_sum += x * y; + x2_sum += x * x; + } + + *slope = (xy_sum - x_sum * y_sum / n) / (x2_sum - x_sum * x_sum / n); + *intercept = (y_sum - *slope * x_sum) / n; + + *r_max = 0.0, r2_sum = 0.0; + + for (i = 0; i < n; i++) { + x = samples[i].time; + y = samples[i].offset; + r = fabs(x * *slope + *intercept - y); + if (*r_max < r) + *r_max = r; + r2_sum += r * r; + } + + *r_stddev = sqrt(r2_sum / n); +} + +static int run_test(int calibration, double freq_base, double freq_step) +{ + struct sample samples[SAMPLES]; + double intercept, slope, stddev1, max1, stddev2, max2; + double freq_error1, freq_error2; + int i; + + set_frequency(freq_base); + + for (i = 0; i < 10; i++) + usleep(1e6 * MEAN_SAMPLE_INTERVAL / 10); + + reset_ntp_error(); + + set_frequency(freq_base + freq_step); + + for (i = 0; i < 10; i++) + usleep(rand() % 2000000 * STEP_INTERVAL / 10); + + set_frequency(freq_base); + + for (i = 0; i < SAMPLES; i++) { + usleep(rand() % 2000000 * MEAN_SAMPLE_INTERVAL); + get_sample(&samples[i]); + } + + if (calibration) { + regress(samples, SAMPLES, &intercept, &slope, &stddev1, &max1); + mono_freq_offset = slope; + printf("CLOCK_MONOTONIC_RAW frequency offset: %11.3f ppm\n", + 1e6 * mono_freq_offset); + return 0; + } + + regress(samples, SAMPLES / 2, &intercept, &slope, &stddev1, &max1); + freq_error1 = slope * (1.0 - mono_freq_offset) - mono_freq_offset - + freq_base; + + regress(samples + SAMPLES / 2, SAMPLES / 2, &intercept, &slope, + &stddev2, &max2); + freq_error2 = slope * (1.0 - mono_freq_offset) - mono_freq_offset - + freq_base; + + printf("%6.0f %+10.3f %6.0f %7.0f %+10.3f %6.0f %7.0f\t", + 1e6 * freq_step, + 1e6 * freq_error1, 1e9 * stddev1, 1e9 * max1, + 1e6 * freq_error2, 1e9 * stddev2, 1e9 * max2); + + if (fabs(freq_error2) > MAX_FREQ_ERROR || stddev2 > MAX_STDDEV) { + printf("[FAIL]\n"); + return 1; + } + + printf("[OK]\n"); + return 0; +} + +static void init_test(void) +{ + struct timespec ts; + struct sample sample; + + if (clock_gettime(CLOCK_MONOTONIC_RAW, &ts)) { + perror("[FAIL] clock_gettime(CLOCK_MONOTONIC_RAW)"); + ksft_exit_fail(); + } + + mono_raw_base = ts.tv_sec; + + if (clock_gettime(CLOCK_MONOTONIC, &ts)) { + perror("[FAIL] clock_gettime(CLOCK_MONOTONIC)"); + ksft_exit_fail(); + } + + mono_base = ts.tv_sec; + + user_hz = sysconf(_SC_CLK_TCK); + + precision = get_sample(&sample) / 2.0; + printf("CLOCK_MONOTONIC_RAW+CLOCK_MONOTONIC precision: %.0f ns\t\t", + 1e9 * precision); + + 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); + + run_test(1, 0.0, 0.0); +} + +int main(int argc, char **argv) +{ + double freq_base, freq_step; + int i, j, fails = 0; + + init_test(); + + printf("Checking response to frequency step:\n"); + printf(" Step 1st interval 2nd interval\n"); + printf(" Freq Dev Max Freq Dev Max\n"); + + for (i = 2; i >= 0; i--) { + for (j = 0; j < 5; j++) { + freq_base = (rand() % (1 << 24) - (1 << 23)) / 65536e6; + freq_step = 10e-6 * (1 << (6 * i)); + fails += run_test(0, freq_base, freq_step); + } + } + + set_frequency(0.0); + + if (fails) + return ksft_exit_fail(); + + return ksft_exit_pass(); +} diff --git a/tools/testing/selftests/timers/inconsistency-check.c b/tools/testing/selftests/timers/inconsistency-check.c index caf1bc9257c4..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 @@ -118,7 +107,7 @@ int consistency_test(int clock_type, unsigned long seconds) start_str = ctime(&t); while (seconds == -1 || now - then < seconds) { - inconsistent = 0; + inconsistent = -1; /* Fill list */ for (i = 0; i < CALLS_PER_LOOP; i++) @@ -130,7 +119,7 @@ int consistency_test(int clock_type, unsigned long seconds) inconsistent = i; /* display inconsistency */ - if (inconsistent) { + if (inconsistent >= 0) { unsigned long long delta; printf("\%s\n", start_str); 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 4230d3052e5d..411eff625e66 100644 --- a/tools/testing/selftests/timers/rtctest.c +++ b/tools/testing/selftests/timers/rtctest.c @@ -21,6 +21,9 @@ #include <stdlib.h> #include <errno.h> +#ifndef ARRAY_SIZE +# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif /* * This expects the new RTC class driver framework, working with @@ -29,23 +32,84 @@ */ static const char default_rtc[] = "/dev/rtc0"; +static struct rtc_time cutoff_dates[] = { + { + .tm_year = 70, /* 1970 -1900 */ + .tm_mday = 1, + }, + /* signed time_t 19/01/2038 3:14:08 */ + { + .tm_year = 138, + .tm_mday = 19, + }, + { + .tm_year = 138, + .tm_mday = 20, + }, + { + .tm_year = 199, /* 2099 -1900 */ + .tm_mday = 1, + }, + { + .tm_year = 200, /* 2100 -1900 */ + .tm_mday = 1, + }, + /* unsigned time_t 07/02/2106 7:28:15*/ + { + .tm_year = 205, + .tm_mon = 1, + .tm_mday = 7, + }, + { + .tm_year = 206, + .tm_mon = 1, + .tm_mday = 8, + }, + /* signed time on 64bit in nanoseconds 12/04/2262 01:47:16*/ + { + .tm_year = 362, + .tm_mon = 3, + .tm_mday = 12, + }, + { + .tm_year = 362, /* 2262 -1900 */ + .tm_mon = 3, + .tm_mday = 13, + }, +}; + +static int compare_dates(struct rtc_time *a, struct rtc_time *b) +{ + if (a->tm_year != b->tm_year || + a->tm_mon != b->tm_mon || + a->tm_mday != b->tm_mday || + a->tm_hour != b->tm_hour || + a->tm_min != b->tm_min || + ((b->tm_sec - a->tm_sec) > 1)) + return 1; + + return 0; +} int main(int argc, char **argv) { - int i, fd, retval, irqcount = 0; + int i, fd, retval, irqcount = 0, dangerous = 0; unsigned long tmp, data; struct rtc_time rtc_tm; const char *rtc = default_rtc; struct timeval start, end, diff; switch (argc) { + case 3: + if (*argv[2] == 'd') + dangerous = 1; case 2: rtc = argv[1]; /* FALLTHROUGH */ case 1: break; default: - fprintf(stderr, "usage: rtctest [rtcdev]\n"); + fprintf(stderr, "usage: rtctest [rtcdev] [d]\n"); return 1; } @@ -157,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); } @@ -167,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; @@ -202,7 +271,7 @@ test_PIE: /* not all RTCs support periodic IRQs */ if (errno == EINVAL) { fprintf(stderr, "\nNo periodic IRQ support\n"); - goto done; + goto test_DATE; } perror("RTC_IRQP_READ ioctl"); exit(errno); @@ -221,7 +290,7 @@ test_PIE: if (errno == EINVAL) { fprintf(stderr, "\n...Periodic IRQ rate is fixed\n"); - goto done; + goto test_DATE; } perror("RTC_IRQP_SET ioctl"); exit(errno); @@ -269,6 +338,62 @@ test_PIE: } } +test_DATE: + if (!dangerous) + goto done; + + fprintf(stderr, "\nTesting problematic dates\n"); + + for (i = 0; i < ARRAY_SIZE(cutoff_dates); i++) { + struct rtc_time current; + + /* Write the new date in RTC */ + retval = ioctl(fd, RTC_SET_TIME, &cutoff_dates[i]); + if (retval == -1) { + perror("RTC_SET_TIME ioctl"); + close(fd); + exit(errno); + } + + /* Read back */ + retval = ioctl(fd, RTC_RD_TIME, ¤t); + if (retval == -1) { + perror("RTC_RD_TIME ioctl"); + exit(errno); + } + + if(compare_dates(&cutoff_dates[i], ¤t)) { + fprintf(stderr,"Setting date %d failed\n", + cutoff_dates[i].tm_year + 1900); + goto done; + } + + cutoff_dates[i].tm_sec += 5; + + /* Write the new alarm in RTC */ + retval = ioctl(fd, RTC_ALM_SET, &cutoff_dates[i]); + if (retval == -1) { + perror("RTC_ALM_SET ioctl"); + close(fd); + exit(errno); + } + + /* Read back */ + retval = ioctl(fd, RTC_ALM_READ, ¤t); + if (retval == -1) { + perror("RTC_ALM_READ ioctl"); + exit(errno); + } + + if(compare_dates(&cutoff_dates[i], ¤t)) { + fprintf(stderr,"Setting alarm %d failed\n", + cutoff_dates[i].tm_year + 1900); + goto done; + } + + fprintf(stderr, "Setting year %d is OK \n", + cutoff_dates[i].tm_year + 1900); + } done: fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n"); diff --git a/tools/testing/selftests/timers/rtctest_setdate.c b/tools/testing/selftests/timers/rtctest_setdate.c new file mode 100644 index 000000000000..2cb78489eca4 --- /dev/null +++ b/tools/testing/selftests/timers/rtctest_setdate.c @@ -0,0 +1,86 @@ +/* Real Time Clock Driver Test + * by: Benjamin Gaignard (benjamin.gaignard@linaro.org) + * + * To build + * gcc rtctest_setdate.c -o rtctest_setdate + * + * 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. + */ + +#include <stdio.h> +#include <linux/rtc.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> + +static const char default_time[] = "00:00:00"; + +int main(int argc, char **argv) +{ + int fd, retval; + struct rtc_time new, current; + const char *rtc, *date; + const char *time = default_time; + + switch (argc) { + case 4: + time = argv[3]; + /* FALLTHROUGH */ + case 3: + date = argv[2]; + rtc = argv[1]; + break; + default: + fprintf(stderr, "usage: rtctest_setdate <rtcdev> <DD-MM-YYYY> [HH:MM:SS]\n"); + return 1; + } + + fd = open(rtc, O_RDONLY); + if (fd == -1) { + perror(rtc); + exit(errno); + } + + sscanf(date, "%d-%d-%d", &new.tm_mday, &new.tm_mon, &new.tm_year); + new.tm_mon -= 1; + new.tm_year -= 1900; + sscanf(time, "%d:%d:%d", &new.tm_hour, &new.tm_min, &new.tm_sec); + + fprintf(stderr, "Test will set RTC date/time to %d-%d-%d, %02d:%02d:%02d.\n", + new.tm_mday, new.tm_mon + 1, new.tm_year + 1900, + new.tm_hour, new.tm_min, new.tm_sec); + + /* Write the new date in RTC */ + retval = ioctl(fd, RTC_SET_TIME, &new); + if (retval == -1) { + perror("RTC_SET_TIME ioctl"); + close(fd); + exit(errno); + } + + /* Read back */ + retval = ioctl(fd, RTC_RD_TIME, ¤t); + if (retval == -1) { + perror("RTC_RD_TIME ioctl"); + exit(errno); + } + + fprintf(stderr, "\n\nCurrent RTC date/time is %d-%d-%d, %02d:%02d:%02d.\n", + current.tm_mday, current.tm_mon + 1, current.tm_year + 1900, + current.tm_hour, current.tm_min, current.tm_sec); + + close(fd); + return 0; +} 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..50da45437daa 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,15 +134,17 @@ 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)) { printf("%-22s %s missing CAP_WAKE_ALARM? : [UNSUPPORTED]\n", clockstring(clock_id), flags ? "ABSTIME":"RELTIME"); - return 0; + /* Indicate timer isn't set, so caller doesn't wait */ + return 1; } printf("%s - timer_create() failed\n", clockstring(clock_id)); return -1; @@ -158,32 +158,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); + /* Unsupported case - return 0 to not fail the test */ + if (err) + return err == 1 ? 0 : 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; + int err; + + err = setup_timer(clock_id, flags, interval, &tm1); + /* Unsupported case - return 0 to not fail the test */ + if (err) + return err == 1 ? 0 : err; + + memset(&timeout, 0, sizeof(timeout)); + timeout.tv_sec = 5; + do { + err = select(0, NULL, 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 +274,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..de2f9ec8a87f 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,7 +380,24 @@ static void *locking_thread(void *arg) return NULL; } -static int copy_page(int ufd, unsigned long offset) +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, bool retry) { struct uffdio_copy uffdio_copy; @@ -351,11 +417,26 @@ 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 && retry) { + test_uffdio_copy_eexist = false; + retry_copy_page(ufd, &uffdio_copy, offset); + } return 1; + } return 0; } +static int copy_page_retry(int ufd, unsigned long offset) +{ + return __copy_page(ufd, offset, true); +} + +static int copy_page(int ufd, unsigned long offset) +{ + return __copy_page(ufd, offset, false); +} + static void *uffd_poll_thread(void *arg) { unsigned long cpu = (unsigned long) arg; @@ -408,6 +489,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; @@ -472,7 +554,7 @@ static void *background_thread(void *arg) for (page_nr = cpu * nr_pages_per_cpu; page_nr < (cpu+1) * nr_pages_per_cpu; page_nr++) - copy_page(uffd, page_nr * page_size); + copy_page_retry(uffd, page_nr * page_size); return NULL; } @@ -572,6 +654,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 +678,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 +740,9 @@ static int faulting_process(void) } } + if (signal_test) + return signalled != split_nr_pages; + if (test_type == TEST_HUGETLB) return 0; @@ -636,7 +772,24 @@ static int faulting_process(void) return 0; } -static int uffdio_zeropage(int ufd, unsigned long offset) +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, bool retry) { struct uffdio_zeropage uffdio_zeropage; int ret; @@ -670,8 +823,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 && retry) { + test_uffdio_zeropage_eexist = false; + retry_uffdio_zeropage(ufd, &uffdio_zeropage, + offset); + } return 1; + } } else { fprintf(stderr, "UFFDIO_ZEROPAGE succeeded %Ld\n", @@ -681,6 +840,11 @@ static int uffdio_zeropage(int ufd, unsigned long offset) return 0; } +static int uffdio_zeropage(int ufd, unsigned long offset) +{ + return __uffdio_zeropage(ufd, offset, false); +} + /* exercise UFFDIO_ZEROPAGE */ static int userfaultfd_zeropage_test(void) { @@ -761,7 +925,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 +942,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 +1109,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 +1151,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 +1183,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 +1197,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 +1233,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 +1258,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/vm/virtual_address_range.c b/tools/testing/selftests/vm/virtual_address_range.c index 3b02aa6eb9da..1830d66a6f0e 100644 --- a/tools/testing/selftests/vm/virtual_address_range.c +++ b/tools/testing/selftests/vm/virtual_address_range.c @@ -10,7 +10,6 @@ #include <string.h> #include <unistd.h> #include <errno.h> -#include <numaif.h> #include <sys/mman.h> #include <sys/time.h> @@ -32,15 +31,33 @@ * different areas one below 128TB and one above 128TB * till it reaches 512TB. One with size 128TB and the * other being 384TB. + * + * On Arm64 the address space is 256TB and no high mappings + * are supported so far. */ + #define NR_CHUNKS_128TB 8192UL /* Number of 16GB chunks for 128TB */ -#define NR_CHUNKS_384TB 24576UL /* Number of 16GB chunks for 384TB */ +#define NR_CHUNKS_256TB (NR_CHUNKS_128TB * 2UL) +#define NR_CHUNKS_384TB (NR_CHUNKS_128TB * 3UL) #define ADDR_MARK_128TB (1UL << 47) /* First address beyond 128TB */ +#define ADDR_MARK_256TB (1UL << 48) /* First address beyond 256TB */ + +#ifdef __aarch64__ +#define HIGH_ADDR_MARK ADDR_MARK_256TB +#define HIGH_ADDR_SHIFT 49 +#define NR_CHUNKS_LOW NR_CHUNKS_256TB +#define NR_CHUNKS_HIGH 0 +#else +#define HIGH_ADDR_MARK ADDR_MARK_128TB +#define HIGH_ADDR_SHIFT 48 +#define NR_CHUNKS_LOW NR_CHUNKS_128TB +#define NR_CHUNKS_HIGH NR_CHUNKS_384TB +#endif static char *hind_addr(void) { - int bits = 48 + rand() % 15; + int bits = HIGH_ADDR_SHIFT + rand() % (63 - HIGH_ADDR_SHIFT); return (char *) (1UL << bits); } @@ -50,14 +67,14 @@ static int validate_addr(char *ptr, int high_addr) unsigned long addr = (unsigned long) ptr; if (high_addr) { - if (addr < ADDR_MARK_128TB) { + if (addr < HIGH_ADDR_MARK) { printf("Bad address %lx\n", addr); return 1; } return 0; } - if (addr > ADDR_MARK_128TB) { + if (addr > HIGH_ADDR_MARK) { printf("Bad address %lx\n", addr); return 1; } @@ -79,12 +96,12 @@ static int validate_lower_address_hint(void) int main(int argc, char *argv[]) { - char *ptr[NR_CHUNKS_128TB]; - char *hptr[NR_CHUNKS_384TB]; + char *ptr[NR_CHUNKS_LOW]; + char *hptr[NR_CHUNKS_HIGH]; char *hint; unsigned long i, lchunks, hchunks; - for (i = 0; i < NR_CHUNKS_128TB; i++) { + for (i = 0; i < NR_CHUNKS_LOW; i++) { ptr[i] = mmap(NULL, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); @@ -99,7 +116,7 @@ int main(int argc, char *argv[]) } lchunks = i; - for (i = 0; i < NR_CHUNKS_384TB; i++) { + for (i = 0; i < NR_CHUNKS_HIGH; i++) { hint = hind_addr(); hptr[i] = mmap(hint, MAP_CHUNK_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); diff --git a/tools/testing/selftests/watchdog/Makefile b/tools/testing/selftests/watchdog/Makefile index f863c664e3d1..ee068511fd0b 100644 --- a/tools/testing/selftests/watchdog/Makefile +++ b/tools/testing/selftests/watchdog/Makefile @@ -1,8 +1,3 @@ -TEST_PROGS := watchdog-test - -all: $(TEST_PROGS) +TEST_GEN_PROGS := watchdog-test include ../lib.mk - -clean: - rm -fr $(TEST_PROGS) 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); - - close(fd); - if (ret < 0) - printf("\nStopping watchdog ticks failed (%d)...\n", errno); - else - printf("\nStopping watchdog ticks...\n"); - exit(0); + 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); +} + +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; - - setbuf(stdout, NULL); - - fd = open("/dev/watchdog", O_WRONLY); - - if (fd == -1) { - printf("Watchdog device not enabled.\n"); - exit(-1); - } - - 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; - } - } - - printf("Watchdog Ticking Away!\n"); - - signal(SIGINT, term); - - while(1) { - keep_alive(); - sleep(ping_rate); - } + int flags; + unsigned int ping_rate = DEFAULT_PING_RATE; + int ret; + int c; + int oneshot = 0; + + setbuf(stdout, NULL); + + fd = open("/dev/watchdog", O_WRONLY); + + 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; + } + } + + if (oneshot) + goto end; + + printf("Watchdog Ticking Away!\n"); + + signal(SIGINT, term); + + 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/Makefile b/tools/testing/selftests/x86/Makefile index 97f187e2663f..0a74a20ca32b 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -20,7 +20,7 @@ BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64) BINARIES_32 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_32)) BINARIES_64 := $(patsubst %,$(OUTPUT)/%,$(BINARIES_64)) -CFLAGS := -O2 -g -std=gnu99 -pthread -Wall +CFLAGS := -O2 -g -std=gnu99 -pthread -Wall -no-pie UNAME_M := $(shell uname -m) CAN_BUILD_I386 := $(shell ./check_cc.sh $(CC) trivial_32bit_program.c -m32) 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]); + } } } diff --git a/tools/testing/selftests/x86/mpx-mini-test.c b/tools/testing/selftests/x86/mpx-mini-test.c index a8df159a8924..ec0f6b45ce8b 100644 --- a/tools/testing/selftests/x86/mpx-mini-test.c +++ b/tools/testing/selftests/x86/mpx-mini-test.c @@ -391,8 +391,7 @@ void handler(int signum, siginfo_t *si, void *vucontext) br_count++; dprintf1("#BR 0x%jx (total seen: %d)\n", status, br_count); -#define __SI_FAULT (3 << 16) -#define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */ +#define SEGV_BNDERR 3 /* failed address bound checks */ dprintf2("Saw a #BR! status 0x%jx at %016lx br_reason: %jx\n", status, ip, br_reason); diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c index 3237bc010e1c..23927845518d 100644 --- a/tools/testing/selftests/x86/protection_keys.c +++ b/tools/testing/selftests/x86/protection_keys.c @@ -212,19 +212,18 @@ void dump_mem(void *dumpme, int len_bytes) } } -#define __SI_FAULT (3 << 16) -#define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */ -#define SEGV_PKUERR (__SI_FAULT|4) +#define SEGV_BNDERR 3 /* failed address bound checks */ +#define SEGV_PKUERR 4 static char *si_code_str(int si_code) { - if (si_code & SEGV_MAPERR) + if (si_code == SEGV_MAPERR) return "SEGV_MAPERR"; - if (si_code & SEGV_ACCERR) + if (si_code == SEGV_ACCERR) return "SEGV_ACCERR"; - if (si_code & SEGV_BNDERR) + if (si_code == SEGV_BNDERR) return "SEGV_BNDERR"; - if (si_code & SEGV_PKUERR) + if (si_code == SEGV_PKUERR) return "SEGV_PKUERR"; return "UNKNOWN"; } |