bpf.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Andrii Nakryiko <andrii@kernel.org>
To: <bpf@vger.kernel.org>, <ast@kernel.org>, <daniel@iogearbox.net>,
	<kpsingh@kernel.org>, <keescook@chromium.org>,
	<paul@paul-moore.com>
Cc: <linux-security-module@vger.kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>
Subject: [PATCH bpf-next 8/8] selftests/bpf: enhance lsm_map_create test with BTF LSM control
Date: Tue, 11 Apr 2023 21:33:00 -0700	[thread overview]
Message-ID: <20230412043300.360803-9-andrii@kernel.org> (raw)
In-Reply-To: <20230412043300.360803-1-andrii@kernel.org>

Adjust and augment lsm_map_create selftest with bpf_btf_load_security
LSM hook and validate that BPF maps that require custom BTF are
succeeding even without privileged, as long as LSM policy allows both
BPF map and BTF object creation.

Further, add another subtest that uses libbpf's BPF skeleton to create
a bunch of maps declaratively. We also add read-only global variable to
validate that BPF_MAP_FREEZE command follows LSM policy as well.

Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 .../selftests/bpf/prog_tests/lsm_map_create.c | 89 ++++++++++++++++---
 tools/testing/selftests/bpf/progs/just_maps.c | 56 ++++++++++++
 .../selftests/bpf/progs/lsm_map_create.c      | 15 ++++
 3 files changed, 148 insertions(+), 12 deletions(-)
 create mode 100644 tools/testing/selftests/bpf/progs/just_maps.c

diff --git a/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c
index fee78b0448c3..497268d6febd 100644
--- a/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c
+++ b/tools/testing/selftests/bpf/prog_tests/lsm_map_create.c
@@ -5,6 +5,7 @@
 #include <bpf/btf.h>
 #include "cap_helpers.h"
 #include "lsm_map_create.skel.h"
+#include "just_maps.skel.h"
 
 static int drop_priv_caps(__u64 *old_caps)
 {
@@ -19,7 +20,7 @@ static int restore_priv_caps(__u64 old_caps)
 	return cap_enable_effective(old_caps, NULL);
 }
 
-void test_lsm_map_create(void)
+static void subtest_map_create_probes(void)
 {
 	struct btf *btf = NULL;
 	struct lsm_map_create *skel = NULL;
@@ -59,6 +60,7 @@ void test_lsm_map_create(void)
 
 		if (map_type == BPF_MAP_TYPE_UNSPEC)
 			continue;
+		map_type = BPF_MAP_TYPE_SK_STORAGE;
 
 		/* this will show which map type we are working with in verbose log */
 		map_type_name = btf__str_by_offset(btf, e->name_off);
@@ -100,13 +102,6 @@ void test_lsm_map_create(void)
 		ret = libbpf_probe_bpf_map_type(map_type, NULL);
 		ASSERT_EQ(ret, 1, "default_priv_mode");
 
-		/* local storage needs custom BTF to be loaded, which we
-		 * currently can't do once we drop privileges, so skip few
-		 * checks for such maps
-		 */
-		if (needs_btf)
-			goto skip_if_needs_btf;
-
 		/* now let's drop privileges, and chech that unpriv maps are
 		 * still possible to create
 		 */
@@ -114,7 +109,11 @@ void test_lsm_map_create(void)
 			goto cleanup;
 
 		ret = libbpf_probe_bpf_map_type(map_type, NULL);
-		ASSERT_EQ(ret, is_map_priv ? 0 : 1,  "default_unpriv_mode");
+		/* maps that require custom BTF will fail with -EPERM */
+		if (needs_btf)
+			ASSERT_EQ(ret, -EPERM, "default_unpriv_mode");
+		else
+			ASSERT_EQ(ret, is_map_priv ? 0 : 1,  "default_unpriv_mode");
 
 		/* allow any map creation for our thread */
 		skel->bss->decision = 1;
@@ -124,20 +123,86 @@ void test_lsm_map_create(void)
 		/* reject any map creation for our thread */
 		skel->bss->decision = -1;
 		ret = libbpf_probe_bpf_map_type(map_type, NULL);
-		ASSERT_EQ(ret, 0, "lsm_reject_unpriv_mode");
+		/* maps that require custom BTF will fail with -EPERM */
+		if (needs_btf)
+			ASSERT_EQ(ret, -EPERM, "lsm_reject_unpriv_mode");
+		else
+			ASSERT_EQ(ret, 0, "lsm_reject_unpriv_mode");
 
 		/* restore privileges, but keep reject LSM policy */
 		if (!ASSERT_OK(restore_priv_caps(orig_caps), "restore_caps"))
 			goto cleanup;
 
-skip_if_needs_btf:
 		/* even with all caps map create will fail */
 		skel->bss->decision = -1;
 		ret = libbpf_probe_bpf_map_type(map_type, NULL);
-		ASSERT_EQ(ret, 0, "lsm_reject_priv_mode");
+		if (needs_btf)
+			ASSERT_EQ(ret, -EPERM, "lsm_reject_priv_mode");
+		else
+			ASSERT_EQ(ret, 0, "lsm_reject_priv_mode");
 	}
 
 cleanup:
 	btf__free(btf);
 	lsm_map_create__destroy(skel);
 }
+
+static void subtest_map_create_obj(void)
+{
+	struct lsm_map_create *skel = NULL;
+	struct just_maps *maps_skel = NULL;
+	struct bpf_map_info map_info;
+	__u32 map_info_sz = sizeof(map_info);
+	__u64 orig_caps;
+	int err, map_fd;
+
+	skel = lsm_map_create__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+		return;
+
+	skel->bss->my_tid = syscall(SYS_gettid);
+	skel->bss->decision = 0;
+
+	err = lsm_map_create__attach(skel);
+	if (!ASSERT_OK(err, "skel_attach"))
+		goto cleanup;
+
+	/* now let's drop privileges, and chech that unpriv maps are
+	 * still possible to create and they do have BTF associated with it
+	 */
+	if (!ASSERT_OK(drop_priv_caps(&orig_caps), "drop_caps"))
+		goto cleanup;
+
+	/* allow unprivileged BPF map and BTF obj creation */
+	skel->bss->decision = 1;
+
+	maps_skel = just_maps__open_and_load();
+	if (!ASSERT_OK_PTR(maps_skel, "maps_skel_open_and_load"))
+		goto restore_caps;
+
+	ASSERT_GT(bpf_object__btf_fd(maps_skel->obj), 0, "maps_btf_fd");
+
+	/* check that SK_LOCAL_STORAGE map has BTF info */
+	map_fd = bpf_map__fd(maps_skel->maps.sk_msg_netns_cookies);
+	memset(&map_info, 0, map_info_sz);
+	err = bpf_map_get_info_by_fd(map_fd, &map_info, &map_info_sz);
+	ASSERT_OK(err, "get_map_info_by_fd");
+
+	ASSERT_GT(map_info.btf_id, 0, "map_btf_id");
+	ASSERT_GT(map_info.btf_key_type_id, 0, "map_btf_key_type_id");
+	ASSERT_GT(map_info.btf_value_type_id, 0, "map_btf_value_type_id");
+
+restore_caps:
+	ASSERT_OK(restore_priv_caps(orig_caps), "restore_caps");
+cleanup:
+	just_maps__destroy(maps_skel);
+	lsm_map_create__destroy(skel);
+}
+
+void test_lsm_map_create(void)
+{
+	if (test__start_subtest("map_create_probes"))
+		subtest_map_create_probes();
+	if (test__start_subtest("map_create_obj"))
+		subtest_map_create_obj();
+}
diff --git a/tools/testing/selftests/bpf/progs/just_maps.c b/tools/testing/selftests/bpf/progs/just_maps.c
new file mode 100644
index 000000000000..9073a51da705
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/just_maps.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
+#include <linux/bpf.h>
+#include <bpf/bpf_helpers.h>
+
+struct array_map {
+	__uint(type, BPF_MAP_TYPE_ARRAY);
+	__uint(max_entries, 1);
+	__type(key, int);
+	__type(value, int);
+} array SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_ARRAY_OF_MAPS);
+	__uint(max_entries, 1);
+	__type(key, int);
+	__type(value, int);
+	__array(values, struct array_map);
+} outer_arr SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH_OF_MAPS);
+	__uint(max_entries, 5);
+	__type(key, int);
+	__array(values, struct array_map);
+} outer_hash SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_REUSEPORT_SOCKARRAY);
+	__uint(max_entries, 1);
+	__type(key, int);
+	__type(value, int);
+} sockarr SEC(".maps");
+
+struct hmap_elem {
+	volatile int cnt;
+	struct bpf_spin_lock lock;
+	int test_padding;
+};
+
+struct {
+	__uint(type, BPF_MAP_TYPE_HASH);
+	__uint(max_entries, 1);
+	__type(key, int);
+	__type(value, struct hmap_elem);
+} hmap SEC(".maps");
+
+struct {
+	__uint(type, BPF_MAP_TYPE_SK_STORAGE);
+	__uint(map_flags, BPF_F_NO_PREALLOC);
+	__type(key, int);
+	__type(value, int);
+} sk_msg_netns_cookies SEC(".maps");
+
+/* .rodata to test BPF_MAP_FREEZE as well */
+const volatile int some_read_only_variable = 123;
diff --git a/tools/testing/selftests/bpf/progs/lsm_map_create.c b/tools/testing/selftests/bpf/progs/lsm_map_create.c
index 093825c68459..f3c8465c1ed0 100644
--- a/tools/testing/selftests/bpf/progs/lsm_map_create.c
+++ b/tools/testing/selftests/bpf/progs/lsm_map_create.c
@@ -30,3 +30,18 @@ int BPF_PROG(allow_unpriv_maps, union bpf_attr *attr)
 
 	return -EPERM;
 }
+
+SEC("lsm/bpf_btf_load_security")
+int BPF_PROG(allow_unpriv_btf, union bpf_attr *attr)
+{
+	if (!my_tid || (u32)bpf_get_current_pid_tgid() != my_tid)
+		return 0; /* keep processing LSM hooks */
+
+	if (decision == 0)
+		return 0;
+
+	if (decision > 0)
+		return 1; /* allow */
+
+	return -EPERM;
+}
-- 
2.34.1


  parent reply	other threads:[~2023-04-12  4:34 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-04-12  4:32 [PATCH bpf-next 0/8] New BPF map and BTF security LSM hooks Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 1/8] bpf: move unprivileged checks into map_create() and bpf_prog_load() Andrii Nakryiko
2023-04-12 17:49   ` Kees Cook
2023-04-13  0:22     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 2/8] bpf: inline map creation logic in map_create() function Andrii Nakryiko
2023-04-12 17:53   ` Kees Cook
2023-04-13  0:22     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 3/8] bpf: centralize permissions checks for all BPF map types Andrii Nakryiko
2023-04-12 18:01   ` Kees Cook
2023-04-13  0:23     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 4/8] bpf, lsm: implement bpf_map_create_security LSM hook Andrii Nakryiko
2023-04-12 18:20   ` Kees Cook
2023-04-13  0:23     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 5/8] selftests/bpf: validate new " Andrii Nakryiko
2023-04-12 18:23   ` Kees Cook
2023-04-13  0:23     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 6/8] bpf: drop unnecessary bpf_capable() check in BPF_MAP_FREEZE command Andrii Nakryiko
2023-04-12 18:24   ` Kees Cook
2023-04-13  0:17     ` Andrii Nakryiko
2023-04-12  4:32 ` [PATCH bpf-next 7/8] bpf, lsm: implement bpf_btf_load_security LSM hook Andrii Nakryiko
2023-04-12 16:52   ` Paul Moore
2023-04-13  1:43     ` Andrii Nakryiko
2023-04-13  2:47       ` Paul Moore
2023-04-12  4:33 ` Andrii Nakryiko [this message]
2023-04-12 16:49 ` [PATCH bpf-next 0/8] New BPF map and BTF security LSM hooks Paul Moore
2023-04-12 17:47   ` Kees Cook
2023-04-12 18:06     ` Paul Moore
2023-04-12 18:28       ` Kees Cook
2023-04-12 19:06         ` Paul Moore
2023-04-13  1:43           ` Andrii Nakryiko
2023-04-13  2:56             ` Paul Moore
2023-04-13  5:16               ` Andrii Nakryiko
2023-04-13 15:11                 ` Paul Moore
2023-04-17 23:29                   ` Andrii Nakryiko
2023-04-18  0:47                     ` Casey Schaufler
2023-04-21  0:00                       ` Andrii Nakryiko
2023-04-18 14:21                     ` Paul Moore
2023-04-21  0:00                       ` Andrii Nakryiko
2023-04-21 18:57                         ` Kees Cook
2023-04-13 16:54                 ` Casey Schaufler
2023-04-17 23:31                   ` Andrii Nakryiko
2023-04-13 19:03                 ` Jonathan Corbet
2023-04-17 23:28                   ` Andrii Nakryiko
2023-04-13 16:27             ` Casey Schaufler
2023-04-17 23:31               ` Andrii Nakryiko
2023-04-17 23:53                 ` Casey Schaufler
2023-04-18  0:28                   ` Andrii Nakryiko
2023-04-18  0:52                     ` Casey Schaufler
2023-04-12 18:38       ` Casey Schaufler
2023-04-14 20:23     ` Dr. Greg
2023-04-17 23:31       ` Andrii Nakryiko
2023-04-19 10:53         ` Dr. Greg

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=20230412043300.360803-9-andrii@kernel.org \
    --to=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=keescook@chromium.org \
    --cc=kpsingh@kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=paul@paul-moore.com \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).