From: Andrii Nakryiko <andrii@kernel.org>
To: <bpf@vger.kernel.org>, <ast@kernel.org>, <daniel@iogearbox.net>,
<martin.lau@kernel.org>, <lmb@isovalent.com>, <timo@incline.eu>,
<robin.goegge@isovalent.com>
Cc: <andrii@kernel.org>, <kernel-team@meta.com>
Subject: [PATCH v4 bpf-next 06/19] selftests/bpf: add fixed vs rotating verifier log tests
Date: Thu, 6 Apr 2023 16:41:52 -0700 [thread overview]
Message-ID: <20230406234205.323208-7-andrii@kernel.org> (raw)
In-Reply-To: <20230406234205.323208-1-andrii@kernel.org>
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.
For both fixed and rotating log modes we expect -ENOSPC if log contents
doesn't fit in user-supplied log buffer.
Acked-by: Lorenz Bauer <lmb@isovalent.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
.../selftests/bpf/prog_tests/verifier_log.c | 179 ++++++++++++++++++
1 file changed, 179 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..3284108a6ce8
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/verifier_log.c
@@ -0,0 +1,179 @@
+// 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"
+
+
+static bool check_prog_load(int prog_fd, bool expect_err, const char *tag)
+{
+ if (expect_err) {
+ if (!ASSERT_LT(prog_fd, 0, tag)) {
+ close(prog_fd);
+ return false;
+ }
+ } else /* !expect_err */ {
+ if (!ASSERT_GT(prog_fd, 0, tag))
+ return false;
+ }
+ return true;
+}
+
+static void verif_log_subtest(const char *name, bool expect_load_error, int log_level)
+{
+ LIBBPF_OPTS(bpf_prog_load_opts, opts);
+ struct {
+ /* strategically placed before others to avoid accidental modification by kernel */
+ char filler[1024];
+ char buf[1024];
+ /* strategically placed after buf[] to catch more accidental corruptions */
+ char reference[1024];
+ } logs;
+ char *exp_log, prog_name[16], op_name[32];
+ struct test_log_buf *skel;
+ struct bpf_program *prog;
+ const struct bpf_insn *insns;
+ size_t insn_cnt, fixed_log_sz;
+ int i, mode, err, prog_fd;
+
+ skel = test_log_buf__open();
+ if (!ASSERT_OK_PTR(skel, "skel_open"))
+ return;
+
+ bpf_object__for_each_program(prog, skel->obj) {
+ if (strcmp(bpf_program__name(prog), name) == 0)
+ bpf_program__set_autoload(prog, true);
+ else
+ bpf_program__set_autoload(prog, false);
+ }
+
+ err = test_log_buf__load(skel);
+ if (!expect_load_error && !ASSERT_OK(err, "unexpected_load_failure"))
+ goto cleanup;
+ if (expect_load_error && !ASSERT_ERR(err, "unexpected_load_success"))
+ goto cleanup;
+
+ insns = bpf_program__insns(skel->progs.good_prog);
+ insn_cnt = bpf_program__insn_cnt(skel->progs.good_prog);
+
+ opts.log_buf = logs.reference;
+ opts.log_size = sizeof(logs.reference);
+ opts.log_level = log_level | 8 /* BPF_LOG_FIXED */;
+ prog_fd = bpf_prog_load(BPF_PROG_TYPE_RAW_TRACEPOINT, "log_fixed",
+ "GPL", insns, insn_cnt, &opts);
+ if (!check_prog_load(prog_fd, expect_load_error, "fixed_buf_prog_load"))
+ goto cleanup;
+ close(prog_fd);
+
+ fixed_log_sz = strlen(logs.reference) + 1;
+ if (!ASSERT_GT(fixed_log_sz, 50, "fixed_log_sz"))
+ goto cleanup;
+ memset(logs.reference + fixed_log_sz, 0, sizeof(logs.reference) - fixed_log_sz);
+
+ /* validate BPF_LOG_FIXED works as verifier log used to work, that is:
+ * we get -ENOSPC and beginning of the full verifier log. This only
+ * works for log_level 2 and log_level 1 + failed program. For log
+ * level 2 we don't reset log at all. For log_level 1 + failed program
+ * we don't get to verification stats output. With log level 1
+ * for successful program final result will be just verifier stats.
+ * But if provided too short log buf, kernel will NULL-out log->ubuf
+ * and will stop emitting further log. This means we'll never see
+ * predictable verifier stats.
+ * Long story short, we do the following -ENOSPC test only for
+ * predictable combinations.
+ */
+ if (log_level >= 2 || expect_load_error) {
+ opts.log_buf = logs.buf;
+ opts.log_level = log_level | 8; /* fixed-length log */
+ opts.log_size = 25;
+
+ 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(logs.buf), 24, "log_fixed_25"))
+ goto cleanup;
+ if (!ASSERT_STRNEQ(logs.buf, logs.reference, 24, op_name))
+ goto cleanup;
+ }
+
+ /* validate rolling verifier log logic: try all variations of log buf
+ * length to force various truncation scenarios
+ */
+ opts.log_buf = logs.buf;
+
+ /* rotating mode, then fixed mode */
+ for (mode = 1; mode >= 0; mode--) {
+ /* prefill logs.buf with 'A's to detect any write beyond allowed length */
+ memset(logs.filler, 'A', sizeof(logs.filler));
+ logs.filler[sizeof(logs.filler) - 1] = '\0';
+ memset(logs.buf, 'A', sizeof(logs.buf));
+ logs.buf[sizeof(logs.buf) - 1] = '\0';
+
+ for (i = 1; i < fixed_log_sz; i++) {
+ opts.log_size = i;
+ opts.log_level = log_level | (mode ? 0 : 8 /* BPF_LOG_FIXED */);
+
+ snprintf(prog_name, sizeof(prog_name),
+ "log_%s_%d", mode ? "roll" : "fixed", 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_%s_prog_load_%d", mode ? "roll" : "fixed", i);
+ if (!ASSERT_EQ(prog_fd, -ENOSPC, op_name)) {
+ if (prog_fd >= 0)
+ close(prog_fd);
+ goto cleanup;
+ }
+
+ snprintf(op_name, sizeof(op_name),
+ "log_%s_strlen_%d", mode ? "roll" : "fixed", i);
+ ASSERT_EQ(strlen(logs.buf), i - 1, op_name);
+
+ if (mode)
+ exp_log = logs.reference + fixed_log_sz - i;
+ else
+ exp_log = logs.reference;
+
+ snprintf(op_name, sizeof(op_name),
+ "log_%s_contents_%d", mode ? "roll" : "fixed", i);
+ if (!ASSERT_STRNEQ(logs.buf, exp_log, i - 1, op_name)) {
+ printf("CMP:%d\nS1:'%s'\nS2:'%s'\n",
+ strncmp(logs.buf, exp_log, i - 1),
+ logs.buf, exp_log);
+ goto cleanup;
+ }
+
+ /* check that unused portions of logs.buf is not overwritten */
+ snprintf(op_name, sizeof(op_name),
+ "log_%s_unused_%d", mode ? "roll" : "fixed", i);
+ if (!ASSERT_STREQ(logs.buf + i, logs.filler + i, op_name)) {
+ printf("CMP:%d\nS1:'%s'\nS2:'%s'\n",
+ strcmp(logs.buf + i, logs.filler + i),
+ logs.buf + i, logs.filler + i);
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ test_log_buf__destroy(skel);
+}
+
+void test_verifier_log(void)
+{
+ if (test__start_subtest("good_prog-level1"))
+ verif_log_subtest("good_prog", false, 1);
+ if (test__start_subtest("good_prog-level2"))
+ verif_log_subtest("good_prog", false, 2);
+ if (test__start_subtest("bad_prog-level1"))
+ verif_log_subtest("bad_prog", true, 1);
+ if (test__start_subtest("bad_prog-level2"))
+ verif_log_subtest("bad_prog", true, 2);
+}
--
2.34.1
next prev parent reply other threads:[~2023-04-06 23:42 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-06 23:41 [PATCH v4 bpf-next 00/19] BPF verifier rotating log Andrii Nakryiko
2023-04-06 23:41 ` [PATCH v4 bpf-next 01/19] bpf: split off basic BPF verifier log into separate file Andrii Nakryiko
2023-04-11 10:43 ` Lorenz Bauer
2023-04-06 23:41 ` [PATCH v4 bpf-next 02/19] bpf: remove minimum size restrictions on verifier log buffer Andrii Nakryiko
2023-04-06 23:41 ` [PATCH v4 bpf-next 03/19] bpf: switch BPF verifier log to be a rotating log by default Andrii Nakryiko
2023-04-11 10:28 ` Lorenz Bauer
2023-04-06 23:41 ` [PATCH v4 bpf-next 04/19] libbpf: don't enforce unnecessary verifier log restrictions on libbpf side Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-06 23:41 ` [PATCH v4 bpf-next 05/19] veristat: add more veristat control over verifier log options Andrii Nakryiko
2023-04-06 23:41 ` Andrii Nakryiko [this message]
2023-04-06 23:41 ` [PATCH v4 bpf-next 07/19] bpf: ignore verifier log reset in BPF_LOG_KERNEL mode Andrii Nakryiko
2023-04-06 23:41 ` [PATCH v4 bpf-next 08/19] bpf: fix missing -EFAULT return on user log buf error in btf_parse() Andrii Nakryiko
2023-04-06 23:41 ` [PATCH v4 bpf-next 09/19] bpf: avoid incorrect -EFAULT error in BPF_LOG_KERNEL mode Andrii Nakryiko
2023-04-06 23:41 ` [PATCH v4 bpf-next 10/19] bpf: simplify logging-related error conditions handling Andrii Nakryiko
2023-04-06 23:41 ` [PATCH v4 bpf-next 11/19] bpf: keep track of total log content size in both fixed and rolling modes Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-06 23:41 ` [PATCH v4 bpf-next 12/19] bpf: add log_true_size output field to return necessary log buffer size Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-06 23:41 ` [PATCH v4 bpf-next 13/19] bpf: simplify internal verifier log interface Andrii Nakryiko
2023-04-06 23:42 ` [PATCH v4 bpf-next 14/19] bpf: relax log_buf NULL conditions when log_level>0 is requested Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-06 23:42 ` [PATCH v4 bpf-next 15/19] libbpf: wire through log_true_size returned from kernel for BPF_PROG_LOAD Andrii Nakryiko
2023-04-06 23:42 ` [PATCH v4 bpf-next 16/19] libbpf: wire through log_true_size for bpf_btf_load() API Andrii Nakryiko
2023-04-06 23:42 ` [PATCH v4 bpf-next 17/19] selftests/bpf: add tests to validate log_true_size feature Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-06 23:42 ` [PATCH v4 bpf-next 18/19] selftests/bpf: add testing of log_buf==NULL condition for BPF_PROG_LOAD Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-06 23:42 ` [PATCH v4 bpf-next 19/19] selftests/bpf: add verifier log tests for BPF_BTF_LOAD command Andrii Nakryiko
2023-04-11 10:44 ` Lorenz Bauer
2023-04-11 14:03 ` [PATCH v4 bpf-next 00/19] BPF verifier rotating log Lorenz Bauer
2023-04-11 16:10 ` patchwork-bot+netdevbpf
2023-04-12 15:31 ` Lorenz Bauer
2023-04-12 17:03 ` Andrii Nakryiko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230406234205.323208-7-andrii@kernel.org \
--to=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@meta.com \
--cc=lmb@isovalent.com \
--cc=martin.lau@kernel.org \
--cc=robin.goegge@isovalent.com \
--cc=timo@incline.eu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.