[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20241113005351.2197340-5-song@kernel.org>
Date: Tue, 12 Nov 2024 16:53:51 -0800
From: Song Liu <song@...nel.org>
To: bpf@...r.kernel.org,
linux-fsdevel@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-security-module@...r.kernel.org
Cc: kernel-team@...a.com,
andrii@...nel.org,
eddyz87@...il.com,
ast@...nel.org,
daniel@...earbox.net,
martin.lau@...ux.dev,
viro@...iv.linux.org.uk,
brauner@...nel.org,
jack@...e.cz,
kpsingh@...nel.org,
mattbobrowski@...gle.com,
amir73il@...il.com,
repnop@...gle.com,
jlayton@...nel.org,
josef@...icpanda.com,
mic@...ikod.net,
gnoack@...gle.com,
Song Liu <song@...nel.org>
Subject: [PATCH v3 bpf-next 4/4] selftest/bpf: Test inode local storage recursion prevention
Add selftest for recursion prevention logic of bpf local storage.
When inode local storage function is traced, helpers that access inode
local storage should return -EBUSY.
The recurring program is attached to inode_storage_lookup(). This is not
an ideal target for recursion tests. However, given that the target
function have to take "struct inode *" argument, there isn't a better
target function for the tests.
Test results showed that inode_storage_lookup() is inlined in s390x.
Work around this by adding this test to DENYLIST.s390x.
Signed-off-by: Song Liu <song@...nel.org>
---
tools/testing/selftests/bpf/DENYLIST.s390x | 1 +
.../bpf/prog_tests/inode_local_storage.c | 72 +++++++++++++++
.../bpf/progs/inode_storage_recursion.c | 90 +++++++++++++++++++
3 files changed, 163 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/inode_local_storage.c
create mode 100644 tools/testing/selftests/bpf/progs/inode_storage_recursion.c
diff --git a/tools/testing/selftests/bpf/DENYLIST.s390x b/tools/testing/selftests/bpf/DENYLIST.s390x
index 3ebd77206f98..6b8c9c9ec754 100644
--- a/tools/testing/selftests/bpf/DENYLIST.s390x
+++ b/tools/testing/selftests/bpf/DENYLIST.s390x
@@ -1,5 +1,6 @@
# TEMPORARY
# Alphabetical order
get_stack_raw_tp # user_stack corrupted user stack (no backchain userspace)
+inode_localstorage/recursion # target function (inode_storage_lookup) is inlined on s390)
stacktrace_build_id # compare_map_keys stackid_hmap vs. stackmap err -2 errno 2 (?)
verifier_iterating_callbacks
diff --git a/tools/testing/selftests/bpf/prog_tests/inode_local_storage.c b/tools/testing/selftests/bpf/prog_tests/inode_local_storage.c
new file mode 100644
index 000000000000..a9d9f77216f4
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/inode_local_storage.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <test_progs.h>
+#include "inode_storage_recursion.skel.h"
+
+#define TDIR "/tmp/inode_local_storage"
+#define TDIR_PARENT "/tmp"
+
+static void test_recursion(void)
+{
+ struct inode_storage_recursion *skel;
+ struct bpf_prog_info info;
+ __u32 info_len = sizeof(info);
+ int err, prog_fd, map_fd, inode_fd = -1;
+ long value;
+
+ skel = inode_storage_recursion__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ return;
+
+ skel->bss->test_pid = getpid();
+
+ err = inode_storage_recursion__attach(skel);
+ if (!ASSERT_OK(err, "skel_attach"))
+ goto out;
+
+ err = mkdir(TDIR, 0755);
+ if (!ASSERT_OK(err, "mkdir " TDIR))
+ goto out;
+
+ inode_fd = open(TDIR_PARENT, O_RDONLY | O_CLOEXEC);
+ if (!ASSERT_OK_FD(inode_fd, "open inode_fd"))
+ goto out;
+
+ /* Detach so that the following lookup won't trigger
+ * trace_inode_storage_lookup and further change the values.
+ */
+ inode_storage_recursion__detach(skel);
+ map_fd = bpf_map__fd(skel->maps.inode_map);
+ err = bpf_map_lookup_elem(map_fd, &inode_fd, &value);
+ ASSERT_OK(err, "lookup inode_map");
+
+ /* Check trace_inode_mkdir for the reason that value == 201 */
+ ASSERT_EQ(value, 201, "inode_map value");
+ ASSERT_EQ(skel->bss->nr_del_errs, 1, "bpf_task_storage_delete busy");
+
+ prog_fd = bpf_program__fd(skel->progs.trace_inode_mkdir);
+ memset(&info, 0, sizeof(info));
+ err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
+ ASSERT_OK(err, "get prog info");
+ ASSERT_EQ(info.recursion_misses, 0, "trace_inode_mkdir prog recursion");
+
+ prog_fd = bpf_program__fd(skel->progs.trace_inode_storage_lookup);
+ memset(&info, 0, sizeof(info));
+ err = bpf_prog_get_info_by_fd(prog_fd, &info, &info_len);
+ ASSERT_OK(err, "get prog info");
+ ASSERT_EQ(info.recursion_misses, 3, "trace_inode_storage_lookup prog recursion");
+
+out:
+ rmdir(TDIR);
+ close(inode_fd);
+ inode_storage_recursion__destroy(skel);
+}
+
+void test_inode_localstorage(void)
+{
+ if (test__start_subtest("recursion"))
+ test_recursion();
+}
diff --git a/tools/testing/selftests/bpf/progs/inode_storage_recursion.c b/tools/testing/selftests/bpf/progs/inode_storage_recursion.c
new file mode 100644
index 000000000000..0ad36f8c6e04
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/inode_storage_recursion.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
+
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+#ifndef EBUSY
+#define EBUSY 16
+#endif
+
+char _license[] SEC("license") = "GPL";
+int nr_del_errs;
+int test_pid;
+
+struct {
+ __uint(type, BPF_MAP_TYPE_INODE_STORAGE);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+ __type(key, int);
+ __type(value, long);
+} inode_map SEC(".maps");
+
+/* inode_storage_lookup is not an ideal hook for recursion tests, as it
+ * is static and more likely to get inlined. However, there isn't a
+ * better function for the test. This is because we need to call
+ * bpf_inode_storage_* helpers with an inode intput. Unlike task local
+ * storage, for which we can use bpf_get_current_task_btf() to get task
+ * pointer with BTF, for inode local storage, we need the get the inode
+ * pointer from function arguments. Other functions, such as,
+ * bpf_local_storage_get() does not take inode as input.
+ */
+SEC("fentry/inode_storage_lookup")
+int BPF_PROG(trace_inode_storage_lookup, struct inode *inode)
+{
+ struct task_struct *task = bpf_get_current_task_btf();
+ long *ptr;
+ int err;
+
+ if (!test_pid || task->pid != test_pid)
+ return 0;
+
+ /* This doesn't have BPF_LOCAL_STORAGE_GET_F_CREATE, so it will
+ * not trigger on the first call of bpf_inode_storage_get() below.
+ *
+ * This is called twice, recursion_misses += 2.
+ */
+ ptr = bpf_inode_storage_get(&inode_map, inode, 0, 0);
+ if (ptr) {
+ *ptr += 1;
+
+ /* This is called once, recursion_misses += 1. */
+ err = bpf_inode_storage_delete(&inode_map, inode);
+ if (err == -EBUSY)
+ nr_del_errs++;
+ }
+
+ return 0;
+}
+
+SEC("fentry/security_inode_mkdir")
+int BPF_PROG(trace_inode_mkdir, struct inode *dir,
+ struct dentry *dentry,
+ int mode)
+{
+ struct task_struct *task = bpf_get_current_task_btf();
+ long *ptr;
+
+ if (!test_pid || task->pid != test_pid)
+ return 0;
+
+ /* Trigger trace_inode_storage_lookup, the first time */
+ ptr = bpf_inode_storage_get(&inode_map, dir, 0,
+ BPF_LOCAL_STORAGE_GET_F_CREATE);
+
+ /* trace_inode_storage_lookup cannot get ptr, so *ptr is 0.
+ * Set ptr to 200.
+ */
+ if (ptr && !*ptr)
+ *ptr = 200;
+
+ /* Trigger trace_inode_storage_lookup, the second time.
+ * trace_inode_storage_lookup can now get ptr and increase the
+ * value. Now the value is 201.
+ */
+ bpf_inode_storage_get(&inode_map, dir, 0,
+ BPF_LOCAL_STORAGE_GET_F_CREATE);
+
+ return 0;
+
+}
--
2.43.5
Powered by blists - more mailing lists