All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrii Nakryiko <andrii@kernel.org>
To: <bpf@vger.kernel.org>, <netdev@vger.kernel.org>,
	<paul@paul-moore.com>, <brauner@kernel.org>,
	<torvalds@linuxfoundation.org>
Cc: <linux-fsdevel@vger.kernel.org>,
	<linux-security-module@vger.kernel.org>, <kernel-team@meta.com>
Subject: [PATCH bpf-next 19/29] bpf: support symbolic BPF FS delegation mount options
Date: Wed, 3 Jan 2024 14:20:24 -0800	[thread overview]
Message-ID: <20240103222034.2582628-20-andrii@kernel.org> (raw)
In-Reply-To: <20240103222034.2582628-1-andrii@kernel.org>

Besides already supported special "any" value and hex bit mask, support
string-based parsing of delegation masks based on exact enumerator
names. Utilize BTF information of `enum bpf_cmd`, `enum bpf_map_type`,
`enum bpf_prog_type`, and `enum bpf_attach_type` types to find supported
symbolic names (ignoring __MAX_xxx guard values and stripping repetitive
prefixes like BPF_ for cmd and attach types, BPF_MAP_TYPE_ for maps, and
BPF_PROG_TYPE_ for prog types). The case doesn't matter, but it is
normalized to lower case in mount option output. So "PROG_LOAD",
"prog_load", and "MAP_create" are all valid values to specify for
delegate_cmds options, "array" is among supported for map types, etc.

Besides supporting string values, we also support multiple values
specified at the same time, using colon (':') separator.

There are corresponding changes on bpf_show_options side to use known
values to print them in human-readable format, falling back to hex mask
printing, if there are any unrecognized bits. This shouldn't be
necessary when enum BTF information is present, but in general we should
always be able to fall back to this even if kernel was built without BTF.
As mentioned, emitted symbolic names are normalized to be all lower case.

Example below shows various ways to specify delegate_cmds options
through mount command and how mount options are printed back:

12/14 14:39:07.604
vmuser@archvm:~/local/linux/tools/testing/selftests/bpf
$ mount | rg token

  $ sudo mkdir -p /sys/fs/bpf/token
  $ sudo mount -t bpf bpffs /sys/fs/bpf/token \
               -o delegate_cmds=prog_load:MAP_CREATE \
               -o delegate_progs=kprobe \
               -o delegate_attachs=xdp
  $ mount | grep token
  bpffs on /sys/fs/bpf/token type bpf (rw,relatime,delegate_cmds=map_create:prog_load,delegate_progs=kprobe,delegate_attachs=xdp)

Acked-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
---
 kernel/bpf/inode.c | 249 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 211 insertions(+), 38 deletions(-)

diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c
index 5fb10da5717f..af5d2ffadd70 100644
--- a/kernel/bpf/inode.c
+++ b/kernel/bpf/inode.c
@@ -595,6 +595,136 @@ struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type typ
 }
 EXPORT_SYMBOL(bpf_prog_get_type_path);
 
+struct bpffs_btf_enums {
+	const struct btf *btf;
+	const struct btf_type *cmd_t;
+	const struct btf_type *map_t;
+	const struct btf_type *prog_t;
+	const struct btf_type *attach_t;
+};
+
+static int find_bpffs_btf_enums(struct bpffs_btf_enums *info)
+{
+	const struct btf *btf;
+	const struct btf_type *t;
+	const char *name;
+	int i, n;
+
+	memset(info, 0, sizeof(*info));
+
+	btf = bpf_get_btf_vmlinux();
+	if (IS_ERR(btf))
+		return PTR_ERR(btf);
+	if (!btf)
+		return -ENOENT;
+
+	info->btf = btf;
+
+	for (i = 1, n = btf_nr_types(btf); i < n; i++) {
+		t = btf_type_by_id(btf, i);
+		if (!btf_type_is_enum(t))
+			continue;
+
+		name = btf_name_by_offset(btf, t->name_off);
+		if (!name)
+			continue;
+
+		if (strcmp(name, "bpf_cmd") == 0)
+			info->cmd_t = t;
+		else if (strcmp(name, "bpf_map_type") == 0)
+			info->map_t = t;
+		else if (strcmp(name, "bpf_prog_type") == 0)
+			info->prog_t = t;
+		else if (strcmp(name, "bpf_attach_type") == 0)
+			info->attach_t = t;
+		else
+			continue;
+
+		if (info->cmd_t && info->map_t && info->prog_t && info->attach_t)
+			return 0;
+	}
+
+	return -ESRCH;
+}
+
+static bool find_btf_enum_const(const struct btf *btf, const struct btf_type *enum_t,
+				const char *prefix, const char *str, int *value)
+{
+	const struct btf_enum *e;
+	const char *name;
+	int i, n, pfx_len = strlen(prefix);
+
+	*value = 0;
+
+	if (!btf || !enum_t)
+		return false;
+
+	for (i = 0, n = btf_vlen(enum_t); i < n; i++) {
+		e = &btf_enum(enum_t)[i];
+
+		name = btf_name_by_offset(btf, e->name_off);
+		if (!name || strncasecmp(name, prefix, pfx_len) != 0)
+			continue;
+
+		/* match symbolic name case insensitive and ignoring prefix */
+		if (strcasecmp(name + pfx_len, str) == 0) {
+			*value = e->val;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static void seq_print_delegate_opts(struct seq_file *m,
+				    const char *opt_name,
+				    const struct btf *btf,
+				    const struct btf_type *enum_t,
+				    const char *prefix,
+				    u64 delegate_msk, u64 any_msk)
+{
+	const struct btf_enum *e;
+	bool first = true;
+	const char *name;
+	u64 msk;
+	int i, n, pfx_len = strlen(prefix);
+
+	delegate_msk &= any_msk; /* clear unknown bits */
+
+	if (delegate_msk == 0)
+		return;
+
+	seq_printf(m, ",%s", opt_name);
+	if (delegate_msk == any_msk) {
+		seq_printf(m, "=any");
+		return;
+	}
+
+	if (btf && enum_t) {
+		for (i = 0, n = btf_vlen(enum_t); i < n; i++) {
+			e = &btf_enum(enum_t)[i];
+			name = btf_name_by_offset(btf, e->name_off);
+			if (!name || strncasecmp(name, prefix, pfx_len) != 0)
+				continue;
+			msk = 1ULL << e->val;
+			if (delegate_msk & msk) {
+				/* emit lower-case name without prefix */
+				seq_printf(m, "%c", first ? '=' : ':');
+				name += pfx_len;
+				while (*name) {
+					seq_printf(m, "%c", tolower(*name));
+					name++;
+				}
+
+				delegate_msk &= ~msk;
+				first = false;
+			}
+		}
+	}
+	if (delegate_msk)
+		seq_printf(m, "%c0x%llx", first ? '=' : ':', delegate_msk);
+}
+
 /*
  * Display the mount options in /proc/mounts.
  */
@@ -614,29 +744,34 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
 	if (mode != S_IRWXUGO)
 		seq_printf(m, ",mode=%o", mode);
 
-	mask = (1ULL << __MAX_BPF_CMD) - 1;
-	if ((opts->delegate_cmds & mask) == mask)
-		seq_printf(m, ",delegate_cmds=any");
-	else if (opts->delegate_cmds)
-		seq_printf(m, ",delegate_cmds=0x%llx", opts->delegate_cmds);
-
-	mask = (1ULL << __MAX_BPF_MAP_TYPE) - 1;
-	if ((opts->delegate_maps & mask) == mask)
-		seq_printf(m, ",delegate_maps=any");
-	else if (opts->delegate_maps)
-		seq_printf(m, ",delegate_maps=0x%llx", opts->delegate_maps);
-
-	mask = (1ULL << __MAX_BPF_PROG_TYPE) - 1;
-	if ((opts->delegate_progs & mask) == mask)
-		seq_printf(m, ",delegate_progs=any");
-	else if (opts->delegate_progs)
-		seq_printf(m, ",delegate_progs=0x%llx", opts->delegate_progs);
-
-	mask = (1ULL << __MAX_BPF_ATTACH_TYPE) - 1;
-	if ((opts->delegate_attachs & mask) == mask)
-		seq_printf(m, ",delegate_attachs=any");
-	else if (opts->delegate_attachs)
-		seq_printf(m, ",delegate_attachs=0x%llx", opts->delegate_attachs);
+	if (opts->delegate_cmds || opts->delegate_maps ||
+	    opts->delegate_progs || opts->delegate_attachs) {
+		struct bpffs_btf_enums info;
+
+		/* ignore errors, fallback to hex */
+		(void)find_bpffs_btf_enums(&info);
+
+		mask = (1ULL << __MAX_BPF_CMD) - 1;
+		seq_print_delegate_opts(m, "delegate_cmds",
+					info.btf, info.cmd_t, "BPF_",
+					opts->delegate_cmds, mask);
+
+		mask = (1ULL << __MAX_BPF_MAP_TYPE) - 1;
+		seq_print_delegate_opts(m, "delegate_maps",
+					info.btf, info.map_t, "BPF_MAP_TYPE_",
+					opts->delegate_maps, mask);
+
+		mask = (1ULL << __MAX_BPF_PROG_TYPE) - 1;
+		seq_print_delegate_opts(m, "delegate_progs",
+					info.btf, info.prog_t, "BPF_PROG_TYPE_",
+					opts->delegate_progs, mask);
+
+		mask = (1ULL << __MAX_BPF_ATTACH_TYPE) - 1;
+		seq_print_delegate_opts(m, "delegate_attachs",
+					info.btf, info.attach_t, "BPF_",
+					opts->delegate_attachs, mask);
+	}
+
 	return 0;
 }
 
@@ -686,7 +821,6 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
 	kuid_t uid;
 	kgid_t gid;
 	int opt, err;
-	u64 msk;
 
 	opt = fs_parse(fc, bpf_fs_parameters, param, &result);
 	if (opt < 0) {
@@ -741,24 +875,63 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
 	case OPT_DELEGATE_CMDS:
 	case OPT_DELEGATE_MAPS:
 	case OPT_DELEGATE_PROGS:
-	case OPT_DELEGATE_ATTACHS:
-		if (strcmp(param->string, "any") == 0) {
-			msk = ~0ULL;
-		} else {
-			err = kstrtou64(param->string, 0, &msk);
-			if (err)
-				return err;
+	case OPT_DELEGATE_ATTACHS: {
+		struct bpffs_btf_enums info;
+		const struct btf_type *enum_t;
+		const char *enum_pfx;
+		u64 *delegate_msk, msk = 0;
+		char *p;
+		int val;
+
+		/* ignore errors, fallback to hex */
+		(void)find_bpffs_btf_enums(&info);
+
+		switch (opt) {
+		case OPT_DELEGATE_CMDS:
+			delegate_msk = &opts->delegate_cmds;
+			enum_t = info.cmd_t;
+			enum_pfx = "BPF_";
+			break;
+		case OPT_DELEGATE_MAPS:
+			delegate_msk = &opts->delegate_maps;
+			enum_t = info.map_t;
+			enum_pfx = "BPF_MAP_TYPE_";
+			break;
+		case OPT_DELEGATE_PROGS:
+			delegate_msk = &opts->delegate_progs;
+			enum_t = info.prog_t;
+			enum_pfx = "BPF_PROG_TYPE_";
+			break;
+		case OPT_DELEGATE_ATTACHS:
+			delegate_msk = &opts->delegate_attachs;
+			enum_t = info.attach_t;
+			enum_pfx = "BPF_";
+			break;
+		default:
+			return -EINVAL;
 		}
+
+		while ((p = strsep(&param->string, ":"))) {
+			if (strcmp(p, "any") == 0) {
+				msk |= ~0ULL;
+			} else if (find_btf_enum_const(info.btf, enum_t, enum_pfx, p, &val)) {
+				msk |= 1ULL << val;
+			} else {
+				err = kstrtou64(p, 0, &msk);
+				if (err)
+					return err;
+			}
+		}
+
 		/* Setting delegation mount options requires privileges */
 		if (msk && !capable(CAP_SYS_ADMIN))
 			return -EPERM;
-		switch (opt) {
-		case OPT_DELEGATE_CMDS: opts->delegate_cmds |= msk; break;
-		case OPT_DELEGATE_MAPS: opts->delegate_maps |= msk; break;
-		case OPT_DELEGATE_PROGS: opts->delegate_progs |= msk; break;
-		case OPT_DELEGATE_ATTACHS: opts->delegate_attachs |= msk; break;
-		default: return -EINVAL;
-		}
+
+		*delegate_msk |= msk;
+		break;
+	}
+	default:
+		/* ignore unknown mount options */
 		break;
 	}
 
-- 
2.34.1


  parent reply	other threads:[~2024-01-03 22:23 UTC|newest]

Thread overview: 59+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-01-03 22:20 [PATCH bpf-next 00/29] BPF token Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 01/29] bpf: align CAP_NET_ADMIN checks with bpf_capable() approach Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 02/29] bpf: add BPF token delegation mount options to BPF FS Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 03/29] bpf: introduce BPF token object Andrii Nakryiko
2024-01-05 20:25   ` Linus Torvalds
2024-01-05 20:32     ` Matthew Wilcox
2024-01-05 20:45       ` Linus Torvalds
2024-01-05 22:06         ` Andrii Nakryiko
2024-01-05 22:05     ` Andrii Nakryiko
2024-01-05 22:27       ` Alexei Starovoitov
2024-01-05 21:45   ` Linus Torvalds
2024-01-05 22:18     ` Andrii Nakryiko
2024-01-08 12:02       ` Christian Brauner
2024-01-08 23:58         ` Andrii Nakryiko
2024-01-09 14:52           ` Christian Brauner
2024-01-09 19:00             ` Andrii Nakryiko
2024-01-10 14:59               ` Christian Brauner
2024-01-11  0:42                 ` Andrii Nakryiko
2024-01-11 10:38                   ` Christian Brauner
2024-01-11 17:41                     ` Andrii Nakryiko
2024-01-12  7:58                       ` Christian Brauner
2024-01-12 18:32                         ` Andrii Nakryiko
2024-01-12 19:16                           ` Christian Brauner
2024-01-14  2:29                             ` Andrii Nakryiko
2024-01-16 16:37                               ` Christian Brauner
2024-01-08 12:01     ` Christian Brauner
2024-01-08 16:45     ` Paul Moore
2024-01-09  0:07       ` Andrii Nakryiko
2024-01-10 19:29         ` Paul Moore
2024-01-08 11:44   ` Christian Brauner
2024-01-03 22:20 ` [PATCH bpf-next 04/29] bpf: add BPF token support to BPF_MAP_CREATE command Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 05/29] bpf: add BPF token support to BPF_BTF_LOAD command Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 06/29] bpf: add BPF token support to BPF_PROG_LOAD command Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 07/29] bpf: take into account BPF token when fetching helper protos Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 08/29] bpf: consistently use BPF token throughout BPF verifier logic Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 09/29] bpf,lsm: refactor bpf_prog_alloc/bpf_prog_free LSM hooks Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 10/29] bpf,lsm: refactor bpf_map_alloc/bpf_map_free " Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 11/29] bpf,lsm: add BPF token " Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 12/29] libbpf: add bpf_token_create() API Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 13/29] libbpf: add BPF token support to bpf_map_create() API Andrii Nakryiko
2024-01-04 19:04   ` Linus Torvalds
2024-01-04 19:23     ` Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 14/29] libbpf: add BPF token support to bpf_btf_load() API Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 15/29] libbpf: add BPF token support to bpf_prog_load() API Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 16/29] selftests/bpf: add BPF token-enabled tests Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 17/29] bpf,selinux: allocate bpf_security_struct per BPF token Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 18/29] bpf: fail BPF_TOKEN_CREATE if no delegation option was set on BPF FS Andrii Nakryiko
2024-01-03 22:20 ` Andrii Nakryiko [this message]
2024-01-03 22:20 ` [PATCH bpf-next 20/29] selftests/bpf: utilize string values for delegate_xxx mount options Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 21/29] libbpf: split feature detectors definitions from cached results Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 22/29] libbpf: further decouple feature checking logic from bpf_object Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 23/29] libbpf: move feature detection code into its own file Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 24/29] libbpf: wire up token_fd into feature probing logic Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 25/29] libbpf: wire up BPF token support at BPF object level Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 26/29] selftests/bpf: add BPF object loading tests with explicit token passing Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 27/29] selftests/bpf: add tests for BPF object load with implicit token Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 28/29] libbpf: support BPF token path setting through LIBBPF_BPF_TOKEN_PATH envvar Andrii Nakryiko
2024-01-03 22:20 ` [PATCH bpf-next 29/29] selftests/bpf: add tests for " Andrii Nakryiko
2024-01-03 23:49 ` [PATCH bpf-next 00/29] BPF token Jakub Kicinski

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=20240103222034.2582628-20-andrii@kernel.org \
    --to=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=brauner@kernel.org \
    --cc=kernel-team@meta.com \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=paul@paul-moore.com \
    --cc=torvalds@linuxfoundation.org \
    /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.