Commit e9936076 authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'First set of verifier/*.c migrated to inline assembly'

Eduard Zingerman says:

====================

This is a follow up for RFC [1]. It migrates a first batch of 38
verifier/*.c tests to inline assembly and use of ./test_progs for
actual execution. The migration is done by a python script (see [2]).

Each migrated verifier/xxx.c file is mapped to progs/verifier_xxx.c
plus an entry in the prog_tests/verifier.c. One patch per each file.

A few patches at the beginning of the patch-set extend test_loader
with necessary functionality, mainly:
- support for tests execution in unprivileged mode;
- support for test runs for test programs.

Migrated tests could be selected for execution using the following filter:

  ./test_progs -a verifier_*

An example of the migrated test:

  SEC("xdp")
  __description("XDP pkt read, pkt_data' > pkt_end, corner case, good access")
  __success __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
  __naked void end_corner_case_good_access_1(void)
  {
          asm volatile ("                                 \
          r2 = *(u32*)(r1 + %[xdp_md_data]);              \
          r3 = *(u32*)(r1 + %[xdp_md_data_end]);          \
          r1 = r2;                                        \
          r1 += 8;                                        \
          if r1 > r3 goto l0_%=;                          \
          r0 = *(u64*)(r1 - 8);                           \
  l0_%=:  r0 = 0;                                         \
          exit;                                           \
  "       :
          : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
            __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
          : __clobber_all);
  }

Changes compared to RFC:
- test_loader.c is extended to support test program runs;
- capabilities handling now matches behavior of test_verifier;
- BPF_ST_MEM instructions are automatically replaced by BPF_STX_MEM
  instructions to overcome current clang limitations;
- tests styling updates according to RFC feedback;
- 38 migrated files are included instead of 1.

I used the following means for testing:
- migration tool itself has a set of self-tests;
- migrated tests are passing;
- manually compared each old/new file side-by-side.

While doing side-by-side comparison I've noted a few defects in the
original tests:
- and.c:
  - One of the jump targets is off by one;
  - BPF_ST_MEM wrong OFF/IMM ordering;
- array_access.c:
  - BPF_ST_MEM wrong OFF/IMM ordering;
- value_or_null.c:
  - BPF_ST_MEM wrong OFF/IMM ordering.

These defects would be addressed separately.

[1] RFC
    https://lore.kernel.org/bpf/20230123145148.2791939-1-eddyz87@gmail.com/
[2] Migration tool
    https://github.com/eddyz87/verifier-tests-migrator


====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 496f4f1b ffb515c9
Loading
Loading
Loading
Loading
+6 −4
Original line number Diff line number Diff line
@@ -231,8 +231,9 @@ TEST_GEN_PROGS_EXTENDED += $(TRUNNER_BPFTOOL)

$(TEST_GEN_PROGS) $(TEST_GEN_PROGS_EXTENDED): $(BPFOBJ)

CGROUP_HELPERS	:= $(OUTPUT)/cgroup_helpers.o
TESTING_HELPERS	:= $(OUTPUT)/testing_helpers.o
CGROUP_HELPERS	:= $(OUTPUT)/cgroup_helpers.o
UNPRIV_HELPERS  := $(OUTPUT)/unpriv_helpers.o
TRACE_HELPERS	:= $(OUTPUT)/trace_helpers.o
JSON_WRITER		:= $(OUTPUT)/json_writer.o
CAP_HELPERS	:= $(OUTPUT)/cap_helpers.o
@@ -252,7 +253,7 @@ $(OUTPUT)/test_lirc_mode2_user: $(TESTING_HELPERS)
$(OUTPUT)/xdping: $(TESTING_HELPERS)
$(OUTPUT)/flow_dissector_load: $(TESTING_HELPERS)
$(OUTPUT)/test_maps: $(TESTING_HELPERS)
$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS)
$(OUTPUT)/test_verifier: $(TESTING_HELPERS) $(CAP_HELPERS) $(UNPRIV_HELPERS)
$(OUTPUT)/xsk.o: $(BPFOBJ)

BPFTOOL ?= $(DEFAULT_BPFTOOL)
@@ -561,7 +562,8 @@ TRUNNER_EXTRA_SOURCES := test_progs.c cgroup_helpers.c trace_helpers.c \
			 network_helpers.c testing_helpers.c		\
			 btf_helpers.c flow_dissector_load.h		\
			 cap_helpers.c test_loader.c xsk.c disasm.c	\
			 json_writer.c
			 json_writer.c unpriv_helpers.c

TRUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read $(OUTPUT)/bpf_testmod.ko	\
		       $(OUTPUT)/liburandom_read.so			\
		       $(OUTPUT)/xdp_synproxy				\
+9 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#ifdef HAVE_GENHDR
# include "autoconf.h"
#else
# if defined(__i386) || defined(__x86_64) || defined(__s390x__) || defined(__aarch64__)
#  define CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS 1
# endif
#endif
+104 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only

#include <test_progs.h>

#include "cap_helpers.h"
#include "verifier_and.skel.h"
#include "verifier_array_access.skel.h"
#include "verifier_basic_stack.skel.h"
#include "verifier_bounds_deduction.skel.h"
#include "verifier_bounds_mix_sign_unsign.skel.h"
#include "verifier_cfg.skel.h"
#include "verifier_cgroup_inv_retcode.skel.h"
#include "verifier_cgroup_skb.skel.h"
#include "verifier_cgroup_storage.skel.h"
#include "verifier_const_or.skel.h"
#include "verifier_ctx_sk_msg.skel.h"
#include "verifier_direct_stack_access_wraparound.skel.h"
#include "verifier_div0.skel.h"
#include "verifier_div_overflow.skel.h"
#include "verifier_helper_access_var_len.skel.h"
#include "verifier_helper_packet_access.skel.h"
#include "verifier_helper_restricted.skel.h"
#include "verifier_helper_value_access.skel.h"
#include "verifier_int_ptr.skel.h"
#include "verifier_ld_ind.skel.h"
#include "verifier_leak_ptr.skel.h"
#include "verifier_map_ptr.skel.h"
#include "verifier_map_ret_val.skel.h"
#include "verifier_masking.skel.h"
#include "verifier_meta_access.skel.h"
#include "verifier_raw_stack.skel.h"
#include "verifier_raw_tp_writable.skel.h"
#include "verifier_ringbuf.skel.h"
#include "verifier_spill_fill.skel.h"
#include "verifier_stack_ptr.skel.h"
#include "verifier_uninit.skel.h"
#include "verifier_value_adj_spill.skel.h"
#include "verifier_value.skel.h"
#include "verifier_value_or_null.skel.h"
#include "verifier_var_off.skel.h"
#include "verifier_xadd.skel.h"
#include "verifier_xdp.skel.h"

__maybe_unused
static void run_tests_aux(const char *skel_name, skel_elf_bytes_fn elf_bytes_factory)
{
	struct test_loader tester = {};
	__u64 old_caps;
	int err;

	/* test_verifier tests are executed w/o CAP_SYS_ADMIN, do the same here */
	err = cap_disable_effective(1ULL << CAP_SYS_ADMIN, &old_caps);
	if (err) {
		PRINT_FAIL("failed to drop CAP_SYS_ADMIN: %i, %s\n", err, strerror(err));
		return;
	}

	test_loader__run_subtests(&tester, skel_name, elf_bytes_factory);
	test_loader_fini(&tester);

	err = cap_enable_effective(old_caps, NULL);
	if (err)
		PRINT_FAIL("failed to restore CAP_SYS_ADMIN: %i, %s\n", err, strerror(err));
}

#define RUN(skel) run_tests_aux(#skel, skel##__elf_bytes)

void test_verifier_and(void)                  { RUN(verifier_and); }
void test_verifier_array_access(void)         { RUN(verifier_array_access); }
void test_verifier_basic_stack(void)          { RUN(verifier_basic_stack); }
void test_verifier_bounds_deduction(void)     { RUN(verifier_bounds_deduction); }
void test_verifier_bounds_mix_sign_unsign(void) { RUN(verifier_bounds_mix_sign_unsign); }
void test_verifier_cfg(void)                  { RUN(verifier_cfg); }
void test_verifier_cgroup_inv_retcode(void)   { RUN(verifier_cgroup_inv_retcode); }
void test_verifier_cgroup_skb(void)           { RUN(verifier_cgroup_skb); }
void test_verifier_cgroup_storage(void)       { RUN(verifier_cgroup_storage); }
void test_verifier_const_or(void)             { RUN(verifier_const_or); }
void test_verifier_ctx_sk_msg(void)           { RUN(verifier_ctx_sk_msg); }
void test_verifier_direct_stack_access_wraparound(void) { RUN(verifier_direct_stack_access_wraparound); }
void test_verifier_div0(void)                 { RUN(verifier_div0); }
void test_verifier_div_overflow(void)         { RUN(verifier_div_overflow); }
void test_verifier_helper_access_var_len(void) { RUN(verifier_helper_access_var_len); }
void test_verifier_helper_packet_access(void) { RUN(verifier_helper_packet_access); }
void test_verifier_helper_restricted(void)    { RUN(verifier_helper_restricted); }
void test_verifier_helper_value_access(void)  { RUN(verifier_helper_value_access); }
void test_verifier_int_ptr(void)              { RUN(verifier_int_ptr); }
void test_verifier_ld_ind(void)               { RUN(verifier_ld_ind); }
void test_verifier_leak_ptr(void)             { RUN(verifier_leak_ptr); }
void test_verifier_map_ptr(void)              { RUN(verifier_map_ptr); }
void test_verifier_map_ret_val(void)          { RUN(verifier_map_ret_val); }
void test_verifier_masking(void)              { RUN(verifier_masking); }
void test_verifier_meta_access(void)          { RUN(verifier_meta_access); }
void test_verifier_raw_stack(void)            { RUN(verifier_raw_stack); }
void test_verifier_raw_tp_writable(void)      { RUN(verifier_raw_tp_writable); }
void test_verifier_ringbuf(void)              { RUN(verifier_ringbuf); }
void test_verifier_spill_fill(void)           { RUN(verifier_spill_fill); }
void test_verifier_stack_ptr(void)            { RUN(verifier_stack_ptr); }
void test_verifier_uninit(void)               { RUN(verifier_uninit); }
void test_verifier_value_adj_spill(void)      { RUN(verifier_value_adj_spill); }
void test_verifier_value(void)                { RUN(verifier_value); }
void test_verifier_value_or_null(void)        { RUN(verifier_value_or_null); }
void test_verifier_var_off(void)              { RUN(verifier_var_off); }
void test_verifier_xadd(void)                 { RUN(verifier_xadd); }
void test_verifier_xdp(void)                  { RUN(verifier_xdp); }
+42 −0
Original line number Diff line number Diff line
@@ -5,12 +5,42 @@
/* This set of attributes controls behavior of the
 * test_loader.c:test_loader__run_subtests().
 *
 * The test_loader sequentially loads each program in a skeleton.
 * Programs could be loaded in privileged and unprivileged modes.
 * - __success, __failure, __msg imply privileged mode;
 * - __success_unpriv, __failure_unpriv, __msg_unpriv imply
 *   unprivileged mode.
 * If combination of privileged and unprivileged attributes is present
 * both modes are used. If none are present privileged mode is implied.
 *
 * See test_loader.c:drop_capabilities() for exact set of capabilities
 * that differ between privileged and unprivileged modes.
 *
 * For test filtering purposes the name of the program loaded in
 * unprivileged mode is derived from the usual program name by adding
 * `@unpriv' suffix.
 *
 * __msg             Message expected to be found in the verifier log.
 *                   Multiple __msg attributes could be specified.
 * __msg_unpriv      Same as __msg but for unprivileged mode.
 *
 * __success         Expect program load success in privileged mode.
 * __success_unpriv  Expect program load success in unprivileged mode.
 *
 * __failure         Expect program load failure in privileged mode.
 * __failure_unpriv  Expect program load failure in unprivileged mode.
 *
 * __retval          Execute the program using BPF_PROG_TEST_RUN command,
 *                   expect return value to match passed parameter:
 *                   - a decimal number
 *                   - a hexadecimal number, when starts from 0x
 *                   - literal INT_MIN
 *                   - literal POINTER_VALUE (see definition below)
 *                   - literal TEST_DATA_LEN (see definition below)
 * __retval_unpriv   Same, but load program in unprivileged mode.
 *
 * __description     Text to be used instead of a program name for display
 *                   and filtering purposes.
 *
 * __log_level       Log level to use for the program, numeric value expected.
 *
@@ -27,16 +57,28 @@
#define __msg(msg)		__attribute__((btf_decl_tag("comment:test_expect_msg=" msg)))
#define __failure		__attribute__((btf_decl_tag("comment:test_expect_failure")))
#define __success		__attribute__((btf_decl_tag("comment:test_expect_success")))
#define __description(desc)	__attribute__((btf_decl_tag("comment:test_description=" desc)))
#define __msg_unpriv(msg)	__attribute__((btf_decl_tag("comment:test_expect_msg_unpriv=" msg)))
#define __failure_unpriv	__attribute__((btf_decl_tag("comment:test_expect_failure_unpriv")))
#define __success_unpriv	__attribute__((btf_decl_tag("comment:test_expect_success_unpriv")))
#define __log_level(lvl)	__attribute__((btf_decl_tag("comment:test_log_level="#lvl)))
#define __flag(flag)		__attribute__((btf_decl_tag("comment:test_prog_flags="#flag)))
#define __retval(val)		__attribute__((btf_decl_tag("comment:test_retval="#val)))
#define __retval_unpriv(val)	__attribute__((btf_decl_tag("comment:test_retval_unpriv="#val)))

/* Convenience macro for use with 'asm volatile' blocks */
#define __naked __attribute__((naked))
#define __clobber_all "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "memory"
#define __clobber_common "r0", "r1", "r2", "r3", "r4", "r5", "memory"
#define __imm(name) [name]"i"(name)
#define __imm_const(name, expr) [name]"i"(expr)
#define __imm_addr(name) [name]"i"(&name)
#define __imm_ptr(name) [name]"p"(&name)
#define __imm_insn(name, expr) [name]"i"(*(long *)&(expr))

/* Magic constants used with __retval() */
#define POINTER_VALUE	0xcafe4all
#define TEST_DATA_LEN	64

#if defined(__TARGET_ARCH_x86)
#define SYSCALL_WRAPPER 1
+107 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Converted from tools/testing/selftests/bpf/verifier/and.c */

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"

#define MAX_ENTRIES 11

struct test_val {
	unsigned int index;
	int foo[MAX_ENTRIES];
};

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(max_entries, 1);
	__type(key, long long);
	__type(value, struct test_val);
} map_hash_48b SEC(".maps");

SEC("socket")
__description("invalid and of negative number")
__failure __msg("R0 max value is outside of the allowed memory range")
__failure_unpriv
__flag(BPF_F_ANY_ALIGNMENT)
__naked void invalid_and_of_negative_number(void)
{
	asm volatile ("					\
	r1 = 0;						\
	*(u64*)(r10 - 8) = r1;				\
	r2 = r10;					\
	r2 += -8;					\
	r1 = %[map_hash_48b] ll;			\
	call %[bpf_map_lookup_elem];			\
	if r0 == 0 goto l0_%=;				\
	r1 = *(u8*)(r0 + 0);				\
	r1 &= -4;					\
	r1 <<= 2;					\
	r0 += r1;					\
l0_%=:	r1 = %[test_val_foo];				\
	*(u64*)(r0 + 0) = r1;				\
	exit;						\
"	:
	: __imm(bpf_map_lookup_elem),
	  __imm_addr(map_hash_48b),
	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
	: __clobber_all);
}

SEC("socket")
__description("invalid range check")
__failure __msg("R0 max value is outside of the allowed memory range")
__failure_unpriv
__flag(BPF_F_ANY_ALIGNMENT)
__naked void invalid_range_check(void)
{
	asm volatile ("					\
	r1 = 0;						\
	*(u64*)(r10 - 8) = r1;				\
	r2 = r10;					\
	r2 += -8;					\
	r1 = %[map_hash_48b] ll;			\
	call %[bpf_map_lookup_elem];			\
	if r0 == 0 goto l0_%=;				\
	r1 = *(u32*)(r0 + 0);				\
	r9 = 1;						\
	w1 %%= 2;					\
	w1 += 1;					\
	w9 &= w1;					\
	w9 += 1;					\
	w9 >>= 1;					\
	w3 = 1;						\
	w3 -= w9;					\
	w3 *= 0x10000000;				\
	r0 += r3;					\
	*(u32*)(r0 + 0) = r3;				\
l0_%=:	r0 = r0;					\
	exit;						\
"	:
	: __imm(bpf_map_lookup_elem),
	  __imm_addr(map_hash_48b)
	: __clobber_all);
}

SEC("socket")
__description("check known subreg with unknown reg")
__success __failure_unpriv __msg_unpriv("R1 !read_ok")
__retval(0)
__naked void known_subreg_with_unknown_reg(void)
{
	asm volatile ("					\
	call %[bpf_get_prandom_u32];			\
	r0 <<= 32;					\
	r0 += 1;					\
	r0 &= 0xFFFF1234;				\
	/* Upper bits are unknown but AND above masks out 1 zero'ing lower bits */\
	if w0 < 1 goto l0_%=;				\
	r1 = *(u32*)(r1 + 512);				\
l0_%=:	r0 = 0;						\
	exit;						\
"	:
	: __imm(bpf_get_prandom_u32)
	: __clobber_all);
}

char _license[] SEC("license") = "GPL";
Loading