// 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, "{{"))
|