Add selftests validating BPF_LOG_FIXED behavior, which used to be the only behavior, and now default rotating BPF verifier log, which returns just up to last N bytes of full verifier log, instead of returning -ENOSPC. To stress test correctness of in-kernel verifier log logic, we force it to truncate program's verifier log to all lengths from 1 all the way to its full size (about 450 bytes today). This was a useful stress test while developing the feature. Signed-off-by: Andrii Nakryiko <andrii@xxxxxxxxxx> --- .../selftests/bpf/prog_tests/verifier_log.c | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 tools/testing/selftests/bpf/prog_tests/verifier_log.c diff --git a/tools/testing/selftests/bpf/prog_tests/verifier_log.c b/tools/testing/selftests/bpf/prog_tests/verifier_log.c new file mode 100644 index 000000000000..4d9630d74ae0 --- /dev/null +++ b/tools/testing/selftests/bpf/prog_tests/verifier_log.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ + +#include <test_progs.h> +#include <bpf/btf.h> + +#include "test_log_buf.skel.h" + +void test_verifier_log(void) +{ + LIBBPF_OPTS(bpf_prog_load_opts, opts); + char full_log[1024], log_buf[1024], *exp_log; + char prog_name[16], op_name[32]; + struct test_log_buf *skel; + const struct bpf_insn *insns; + size_t insn_cnt, fixed_log_sz; + int i, err, prog_fd; + + skel = test_log_buf__open(); + if (!ASSERT_OK_PTR(skel, "skel_open")) + return; + + bpf_program__set_autoload(skel->progs.bad_prog, false); + + err = test_log_buf__load(skel); + if (!ASSERT_OK(err, "skel_load")) + goto cleanup; + + insns = bpf_program__insns(skel->progs.good_prog); + insn_cnt = bpf_program__insn_cnt(skel->progs.good_prog); + + opts.log_buf = full_log; + opts.log_size = sizeof(full_log); + opts.log_level = 2 | 8 /* BPF_LOG_FIXED */; + prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, "log_fixed", + "GPL", insns, insn_cnt, &opts); + if (!ASSERT_GT(prog_fd, 0, "fixed_log_prog_load")) + goto cleanup; + close(prog_fd); + + fixed_log_sz = strlen(full_log) + 1; + if (!ASSERT_GT(fixed_log_sz, 100, "fixed_log_sz")) + goto cleanup; + + /* validate BPF_LOG_FIXED works as verifier log used to work, that is: + * we get -ENOSPC and beginning of the full verifier log + */ + opts.log_buf = log_buf; + opts.log_level = 2 | 8; /* verbose level 2, fixed-length log */ + opts.log_size = 50; + + prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, "log_fixed50", + "GPL", insns, insn_cnt, &opts); + if (!ASSERT_EQ(prog_fd, -ENOSPC, "unexpected_log_fixed_prog_load_result")) { + if (prog_fd >= 0) + close(prog_fd); + goto cleanup; + } + if (!ASSERT_EQ(strlen(log_buf), 49, "log_fixed_50")) + goto cleanup; + if (!ASSERT_STRNEQ(log_buf, full_log, 49, op_name)) + goto cleanup; + + /* validate rolling verifier log logic: try all variations of log buf + * length to force various truncation scenarios + */ + opts.log_buf = log_buf; + opts.log_level = 2; /* verbose level 2, rolling log */ + for (i = 1; i <= fixed_log_sz; i++) { + opts.log_size = i; + + snprintf(prog_name, sizeof(prog_name), "log_roll_%d", i); + prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, prog_name, + "GPL", insns, insn_cnt, &opts); + + snprintf(op_name, sizeof(op_name), "log_roll_prog_load_%d", i); + if (!ASSERT_GT(prog_fd, 0, op_name)) + goto cleanup; + close(prog_fd); + + exp_log = full_log + fixed_log_sz - i; + snprintf(op_name, sizeof(op_name), "log_roll_contents_%d", i); + if (!ASSERT_STREQ(log_buf, exp_log, op_name)) + goto cleanup; + } + +cleanup: + test_log_buf__destroy(skel); +} -- 2.34.1