1586 lines · 41.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* Copyright (c) 2022 Meta Platforms, Inc. and affiliates. */#include <linux/capability.h>#include <linux/err.h>#include <stdlib.h>#include <test_progs.h>#include <bpf/btf.h> #include "autoconf_helper.h"#include "disasm_helpers.h"#include "unpriv_helpers.h"#include "cap_helpers.h"#include "jit_disasm_helpers.h" static inline const char *str_has_pfx(const char *str, const char *pfx){	size_t len = strlen(pfx); 	return strncmp(str, pfx, len) == 0 ? str + len : NULL;} #define TEST_LOADER_LOG_BUF_SZ 2097152  /* Warning: duplicated in bpf_misc.h */#define POINTER_VALUE	0xbadcafe#define TEST_DATA_LEN	64 #ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS#define EFFICIENT_UNALIGNED_ACCESS 1#else#define EFFICIENT_UNALIGNED_ACCESS 0#endif static int sysctl_unpriv_disabled = -1; enum mode {	PRIV = 1,	UNPRIV = 2}; enum load_mode {	JITED		= 1 << 0,	NO_JITED	= 1 << 1,}; struct test_subspec {	char *name;	char *description;	bool expect_failure;	struct expected_msgs expect_msgs;	struct expected_msgs expect_xlated;	struct expected_msgs jited;	struct expected_msgs stderr;	struct expected_msgs stdout;	int retval;	bool execute;	__u64 caps;}; struct test_spec {	const char *prog_name;	struct test_subspec priv;	struct test_subspec unpriv;	const char *btf_custom_path;	const char *btf_custom_func_path;	int log_level;	int prog_flags;	int mode_mask;	int arch_mask;	int load_mask;	int linear_sz;	bool auxiliary;	bool valid;}; static int tester_init(struct test_loader *tester){	if (!tester->log_buf) {		tester->log_buf_sz = TEST_LOADER_LOG_BUF_SZ;		tester->log_buf = calloc(tester->log_buf_sz, 1);		if (!ASSERT_OK_PTR(tester->log_buf, "tester_log_buf"))			return -ENOMEM;	} 	return 0;} void test_loader_fini(struct test_loader *tester){	if (!tester)		return; 	free(tester->log_buf);} void free_msgs(struct expected_msgs *msgs){	int i; 	for (i = 0; i < msgs->cnt; i++)		if (msgs->patterns[i].is_regex)			regfree(&msgs->patterns[i].regex);	free(msgs->patterns);	msgs->patterns = NULL;	msgs->cnt = 0;} static void free_test_spec(struct test_spec *spec){	/* Deallocate expect_msgs arrays. */	free_msgs(&spec->priv.expect_msgs);	free_msgs(&spec->unpriv.expect_msgs);	free_msgs(&spec->priv.expect_xlated);	free_msgs(&spec->unpriv.expect_xlated);	free_msgs(&spec->priv.jited);	free_msgs(&spec->unpriv.jited);	free_msgs(&spec->unpriv.stderr);	free_msgs(&spec->priv.stderr);	free_msgs(&spec->unpriv.stdout);	free_msgs(&spec->priv.stdout); 	free(spec->priv.name);	free(spec->priv.description);	free(spec->unpriv.name);	free(spec->unpriv.description);	spec->priv.name = NULL;	spec->priv.description = NULL;	spec->unpriv.name = NULL;	spec->unpriv.description = NULL;} /* Compiles regular expression matching pattern. * Pattern has a special syntax: * *   pattern := (<verbatim text> | regex)* *   regex := "{{" <posix extended regular expression> "}}" * * In other words, pattern is a verbatim text with inclusion * of regular expressions enclosed in "{{" "}}" pairs. * For example, pattern "foo{{[0-9]+}}" matches strings like * "foo0", "foo007", etc. */static int compile_regex(const char *pattern, regex_t *regex){	char err_buf[256], buf[256] = {}, *ptr, *buf_end;	const char *original_pattern = pattern, *next;	bool in_regex = false;	int err; 	buf_end = buf + sizeof(buf);	ptr = buf;	while (*pattern && ptr < buf_end - 2) {		if (!in_regex && (next = str_has_pfx(pattern, "{{"))) {			in_regex = true;			pattern = next;			continue;		}		if (in_regex && (next = str_has_pfx(pattern, "}}"))) {			in_regex = false;			pattern = next;			continue;		}		if (in_regex) {			*ptr++ = *pattern++;			continue;		}		/* list of characters that need escaping for extended posix regex */		if (strchr(".[]\\()*+?{}|^$", *pattern)) {			*ptr++ = '\\';			*ptr++ = *pattern++;			continue;		}		*ptr++ = *pattern++;	}	if (*pattern) {		PRINT_FAIL("Regexp too long: '%s'\n", original_pattern);		return -EINVAL;	}	if (in_regex) {		PRINT_FAIL("Regexp has open '{{' but no closing '}}': '%s'\n", original_pattern);		return -EINVAL;	}	err = regcomp(regex, buf, REG_EXTENDED | REG_NEWLINE);	if (err != 0) {		regerror(err, regex, err_buf, sizeof(err_buf));		PRINT_FAIL("Regexp compilation error in '%s': '%s'\n", buf, err_buf);		return -EINVAL;	}	return 0;} static int __push_msg(const char *pattern, bool on_next_line, bool negative,		      struct expected_msgs *msgs){	struct expect_msg *msg;	void *tmp;	int err; 	tmp = realloc(msgs->patterns,		      (1 + msgs->cnt) * sizeof(struct expect_msg));	if (!tmp) {		ASSERT_FAIL("failed to realloc memory for messages\n");		return -ENOMEM;	}	msgs->patterns = tmp;	msg = &msgs->patterns[msgs->cnt];	msg->on_next_line = on_next_line;	msg->substr = pattern;	msg->negative = negative;	msg->is_regex = false;	if (strstr(pattern, "{{")) {		err = compile_regex(pattern, &msg->regex);		if (err)			return err;		msg->is_regex = true;	}	msgs->cnt += 1;	return 0;} static int clone_msgs(struct expected_msgs *from, struct expected_msgs *to){	struct expect_msg *msg;	int i, err; 	for (i = 0; i < from->cnt; i++) {		msg = &from->patterns[i];		err = __push_msg(msg->substr, msg->on_next_line, msg->negative, to);		if (err)			return err;	}	return 0;} static int push_msg(const char *substr, bool negative, struct expected_msgs *msgs){	return __push_msg(substr, false, negative, msgs);} static int push_disasm_msg(const char *regex_str, bool *on_next_line, struct expected_msgs *msgs){	int err; 	if (strcmp(regex_str, "...") == 0) {		*on_next_line = false;		return 0;	}	err = __push_msg(regex_str, *on_next_line, false, msgs);	if (err)		return err;	*on_next_line = true;	return 0;} static int parse_int(const char *str, int *val, const char *name){	char *end;	long tmp; 	errno = 0;	if (str_has_pfx(str, "0x"))		tmp = strtol(str + 2, &end, 16);	else		tmp = strtol(str, &end, 10);	if (errno || end[0] != '\0') {		PRINT_FAIL("failed to parse %s from '%s'\n", name, str);		return -EINVAL;	}	*val = tmp;	return 0;} static int parse_caps(const char *str, __u64 *val, const char *name){	int cap_flag = 0;	char *token = NULL, *saveptr = NULL; 	char *str_cpy = strdup(str);	if (str_cpy == NULL) {		PRINT_FAIL("Memory allocation failed\n");		return -EINVAL;	} 	token = strtok_r(str_cpy, "|", &saveptr);	while (token != NULL) {		errno = 0;		if (!strncmp("CAP_", token, sizeof("CAP_") - 1)) {			PRINT_FAIL("define %s constant in bpf_misc.h, failed to parse caps\n", token);			return -EINVAL;		}		cap_flag = strtol(token, NULL, 10);		if (!cap_flag || errno) {			PRINT_FAIL("failed to parse caps %s\n", name);			return -EINVAL;		}		*val |= (1ULL << cap_flag);		token = strtok_r(NULL, "|", &saveptr);	} 	free(str_cpy);	return 0;} static int parse_retval(const char *str, int *val, const char *name){	/*	 * INT_MIN is defined as (-INT_MAX -1), i.e. it doesn't expand to a	 * single int and cannot be parsed with strtol, so we handle it	 * separately here. In addition, it expands to different expressions in	 * different compilers so we use a prefixed _INT_MIN instead.	 */	if (strcmp(str, "_INT_MIN") == 0) {		*val = INT_MIN;		return 0;	} 	return parse_int(str, val, name);} static void update_flags(int *flags, int flag, bool clear){	if (clear)		*flags &= ~flag;	else		*flags |= flag;} static const char *skip_decl_tag_pfx(const char *s){	int n = 0; 	if (sscanf(s, "comment:%*d:%n", &n) < 0 || !n)		return NULL;	return s + n;} static int compare_decl_tags(const void *a, const void *b){	return strverscmp(*(const char **)a, *(const char **)b);} /* * Compilers don't guarantee order in which BTF attributes would be generated, * while order is important for test tags like __msg. * Each test tag has the following prefix: "comment:" __COUNTER__, * when sorted using strverscmp this gives same order as in the original C code. */static const char **collect_decl_tags(struct btf *btf, int id, int *cnt){	const char **tmp, **tags = NULL;	const struct btf_type *t;	int i; 	*cnt = 0;	for (i = 1; i < btf__type_cnt(btf); i++) {		t = btf__type_by_id(btf, i);		if (!btf_is_decl_tag(t) || t->type != id || btf_decl_tag(t)->component_idx != -1)			continue;		tmp = realloc(tags, (*cnt + 1) * sizeof(*tags));		if (!tmp) {			free(tags);			return ERR_PTR(-ENOMEM);		}		tags = tmp;		tags[(*cnt)++] = btf__str_by_offset(btf, t->name_off);	} 	if (*cnt)		qsort(tags, *cnt, sizeof(*tags), compare_decl_tags);	return tags;} enum arch {	ARCH_UNKNOWN	= 0x1,	ARCH_X86_64	= 0x2,	ARCH_ARM64	= 0x4,	ARCH_RISCV64	= 0x8,	ARCH_S390X	= 0x10,	ARCH_LOONGARCH	= 0x20,}; static int get_current_arch(void){#if defined(__x86_64__)	return ARCH_X86_64;#elif defined(__aarch64__)	return ARCH_ARM64;#elif defined(__riscv) && __riscv_xlen == 64	return ARCH_RISCV64;#elif defined(__s390x__)	return ARCH_S390X;#elif defined(__loongarch__)	return ARCH_LOONGARCH;#endif	return ARCH_UNKNOWN;} /* Uses btf_decl_tag attributes to describe the expected test * behavior, see bpf_misc.h for detailed description of each attribute * and attribute combinations. */static int parse_test_spec(struct test_loader *tester,			   struct bpf_object *obj,			   struct bpf_program *prog,			   struct test_spec *spec){	const char *description = NULL;	bool has_unpriv_result = false;	bool has_unpriv_retval = false;	bool unpriv_xlated_on_next_line = true;	bool xlated_on_next_line = true;	bool unpriv_jit_on_next_line;	bool jit_on_next_line;	bool stderr_on_next_line = true;	bool unpriv_stderr_on_next_line = true;	bool stdout_on_next_line = true;	bool unpriv_stdout_on_next_line = true;	bool collect_jit = false;	const char **tags = NULL;	int func_id, i, nr_tags;	int err = 0;	u32 arch_mask = 0;	u32 load_mask = 0;	struct btf *btf;	enum arch arch; 	memset(spec, 0, sizeof(*spec)); 	spec->prog_name = bpf_program__name(prog);	spec->prog_flags = testing_prog_flags(); 	btf = bpf_object__btf(obj);	if (!btf) {		ASSERT_FAIL("BPF object has no BTF");		return -EINVAL;	} 	func_id = btf__find_by_name_kind(btf, spec->prog_name, BTF_KIND_FUNC);	if (func_id < 0) {		ASSERT_FAIL("failed to find FUNC BTF type for '%s'", spec->prog_name);		return -EINVAL;	} 	tags = collect_decl_tags(btf, func_id, &nr_tags);	if (IS_ERR(tags))		return PTR_ERR(tags); 	for (i = 0; i < nr_tags; i++) {		const char *s, *val, *msg;		bool clear;		int flags; 		s = skip_decl_tag_pfx(tags[i]);		if (!s)			continue;		if ((val = str_has_pfx(s, "test_description="))) {			description = val;		} else if (strcmp(s, "test_expect_failure") == 0) {			spec->priv.expect_failure = true;			spec->mode_mask |= PRIV;		} else if (strcmp(s, "test_expect_success") == 0) {			spec->priv.expect_failure = false;			spec->mode_mask |= PRIV;		} else if (strcmp(s, "test_expect_failure_unpriv") == 0) {			spec->unpriv.expect_failure = true;			spec->mode_mask |= UNPRIV;			has_unpriv_result = true;		} else if (strcmp(s, "test_expect_success_unpriv") == 0) {			spec->unpriv.expect_failure = false;			spec->mode_mask |= UNPRIV;			has_unpriv_result = true;		} else if (strcmp(s, "test_auxiliary") == 0) {			spec->auxiliary = true;			spec->mode_mask |= PRIV;		} else if (strcmp(s, "test_auxiliary_unpriv") == 0) {			spec->auxiliary = true;			spec->mode_mask |= UNPRIV;		} else if ((msg = str_has_pfx(s, "test_expect_msg="))) {			err = push_msg(msg, false, &spec->priv.expect_msgs);			if (err)				goto cleanup;			spec->mode_mask |= PRIV;		} else if ((msg = str_has_pfx(s, "test_expect_not_msg="))) {			err = push_msg(msg, true, &spec->priv.expect_msgs);			if (err)				goto cleanup;			spec->mode_mask |= PRIV;		} else if ((msg = str_has_pfx(s, "test_expect_msg_unpriv="))) {			err = push_msg(msg, false, &spec->unpriv.expect_msgs);			if (err)				goto cleanup;			spec->mode_mask |= UNPRIV;		} else if ((msg = str_has_pfx(s, "test_expect_not_msg_unpriv="))) {			err = push_msg(msg, true, &spec->unpriv.expect_msgs);			if (err)				goto cleanup;			spec->mode_mask |= UNPRIV;		} else if ((msg = str_has_pfx(s, "test_jited="))) {			if (arch_mask == 0) {

Lines 1–500 of 1586.