Commit 0f5d5454 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'bpf-fs-mount-options-parsing-follow-ups'

Andrii Nakryiko says:

====================
BPF FS mount options parsing follow ups

Original BPF token patch set ([0]) added delegate_xxx mount options which
supported only special "any" value and hexadecimal bitmask. This patch set
attempts to make specifying and inspecting these mount options more
human-friendly by supporting string constants matching corresponding bpf_cmd,
bpf_map_type, bpf_prog_type, and bpf_attach_type enumerators.

This implementation relies on BTF information to find all supported symbolic
names. If kernel wasn't built with BTF, BPF FS will still support "any" and
hex-based mask.

  [0] https://patchwork.kernel.org/project/netdevbpf/list/?series=805707&state=*

v1->v2:
  - strip BPF_, BPF_MAP_TYPE_, and BPF_PROG_TYPE_ prefixes,
    do case-insensitive comparison, normalize to lower case (Alexei).
====================

Link: https://lore.kernel.org/r/20231214225016.1209867-1-andrii@kernel.orgSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 403f3e8f f2d0ffee
...@@ -595,6 +595,136 @@ struct bpf_prog *bpf_prog_get_type_path(const char *name, enum bpf_prog_type typ ...@@ -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); 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. * Display the mount options in /proc/mounts.
*/ */
...@@ -614,29 +744,34 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root) ...@@ -614,29 +744,34 @@ static int bpf_show_options(struct seq_file *m, struct dentry *root)
if (mode != S_IRWXUGO) if (mode != S_IRWXUGO)
seq_printf(m, ",mode=%o", mode); seq_printf(m, ",mode=%o", mode);
mask = (1ULL << __MAX_BPF_CMD) - 1; if (opts->delegate_cmds || opts->delegate_maps ||
if ((opts->delegate_cmds & mask) == mask) opts->delegate_progs || opts->delegate_attachs) {
seq_printf(m, ",delegate_cmds=any"); struct bpffs_btf_enums info;
else if (opts->delegate_cmds)
seq_printf(m, ",delegate_cmds=0x%llx", opts->delegate_cmds); /* ignore errors, fallback to hex */
(void)find_bpffs_btf_enums(&info);
mask = (1ULL << __MAX_BPF_MAP_TYPE) - 1;
if ((opts->delegate_maps & mask) == mask) mask = (1ULL << __MAX_BPF_CMD) - 1;
seq_printf(m, ",delegate_maps=any"); seq_print_delegate_opts(m, "delegate_cmds",
else if (opts->delegate_maps) info.btf, info.cmd_t, "BPF_",
seq_printf(m, ",delegate_maps=0x%llx", opts->delegate_maps); opts->delegate_cmds, mask);
mask = (1ULL << __MAX_BPF_PROG_TYPE) - 1; mask = (1ULL << __MAX_BPF_MAP_TYPE) - 1;
if ((opts->delegate_progs & mask) == mask) seq_print_delegate_opts(m, "delegate_maps",
seq_printf(m, ",delegate_progs=any"); info.btf, info.map_t, "BPF_MAP_TYPE_",
else if (opts->delegate_progs) opts->delegate_maps, mask);
seq_printf(m, ",delegate_progs=0x%llx", opts->delegate_progs);
mask = (1ULL << __MAX_BPF_PROG_TYPE) - 1;
mask = (1ULL << __MAX_BPF_ATTACH_TYPE) - 1; seq_print_delegate_opts(m, "delegate_progs",
if ((opts->delegate_attachs & mask) == mask) info.btf, info.prog_t, "BPF_PROG_TYPE_",
seq_printf(m, ",delegate_attachs=any"); opts->delegate_progs, mask);
else if (opts->delegate_attachs)
seq_printf(m, ",delegate_attachs=0x%llx", opts->delegate_attachs); 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; return 0;
} }
...@@ -686,7 +821,6 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -686,7 +821,6 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
int opt, err; int opt, err;
u64 msk;
opt = fs_parse(fc, bpf_fs_parameters, param, &result); opt = fs_parse(fc, bpf_fs_parameters, param, &result);
if (opt < 0) { if (opt < 0) {
...@@ -741,24 +875,63 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param) ...@@ -741,24 +875,63 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
case OPT_DELEGATE_CMDS: case OPT_DELEGATE_CMDS:
case OPT_DELEGATE_MAPS: case OPT_DELEGATE_MAPS:
case OPT_DELEGATE_PROGS: case OPT_DELEGATE_PROGS:
case OPT_DELEGATE_ATTACHS: case OPT_DELEGATE_ATTACHS: {
if (strcmp(param->string, "any") == 0) { struct bpffs_btf_enums info;
msk = ~0ULL; const struct btf_type *enum_t;
} else { const char *enum_pfx;
err = kstrtou64(param->string, 0, &msk); u64 *delegate_msk, msk = 0;
if (err) char *p;
return err; 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 */ /* Setting delegation mount options requires privileges */
if (msk && !capable(CAP_SYS_ADMIN)) if (msk && !capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
switch (opt) {
case OPT_DELEGATE_CMDS: opts->delegate_cmds |= msk; break; *delegate_msk |= msk;
case OPT_DELEGATE_MAPS: opts->delegate_maps |= msk; break; break;
case OPT_DELEGATE_PROGS: opts->delegate_progs |= msk; break; }
case OPT_DELEGATE_ATTACHS: opts->delegate_attachs |= msk; break; default:
default: return -EINVAL; /* ignore unknown mount options */
}
break; break;
} }
......
...@@ -66,14 +66,22 @@ static int restore_priv_caps(__u64 old_caps) ...@@ -66,14 +66,22 @@ static int restore_priv_caps(__u64 old_caps)
return cap_enable_effective(old_caps, NULL); return cap_enable_effective(old_caps, NULL);
} }
static int set_delegate_mask(int fs_fd, const char *key, __u64 mask) static int set_delegate_mask(int fs_fd, const char *key, __u64 mask, const char *mask_str)
{ {
char buf[32]; char buf[32];
int err; int err;
snprintf(buf, sizeof(buf), "0x%llx", (unsigned long long)mask); if (!mask_str) {
if (mask == ~0ULL) {
mask_str = "any";
} else {
snprintf(buf, sizeof(buf), "0x%llx", (unsigned long long)mask);
mask_str = buf;
}
}
err = sys_fsconfig(fs_fd, FSCONFIG_SET_STRING, key, err = sys_fsconfig(fs_fd, FSCONFIG_SET_STRING, key,
mask == ~0ULL ? "any" : buf, 0); mask_str, 0);
if (err < 0) if (err < 0)
err = -errno; err = -errno;
return err; return err;
...@@ -86,6 +94,10 @@ struct bpffs_opts { ...@@ -86,6 +94,10 @@ struct bpffs_opts {
__u64 maps; __u64 maps;
__u64 progs; __u64 progs;
__u64 attachs; __u64 attachs;
const char *cmds_str;
const char *maps_str;
const char *progs_str;
const char *attachs_str;
}; };
static int create_bpffs_fd(void) static int create_bpffs_fd(void)
...@@ -104,16 +116,16 @@ static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts) ...@@ -104,16 +116,16 @@ static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts)
int mnt_fd, err; int mnt_fd, err;
/* set up token delegation mount options */ /* set up token delegation mount options */
err = set_delegate_mask(fs_fd, "delegate_cmds", opts->cmds); err = set_delegate_mask(fs_fd, "delegate_cmds", opts->cmds, opts->cmds_str);
if (!ASSERT_OK(err, "fs_cfg_cmds")) if (!ASSERT_OK(err, "fs_cfg_cmds"))
return err; return err;
err = set_delegate_mask(fs_fd, "delegate_maps", opts->maps); err = set_delegate_mask(fs_fd, "delegate_maps", opts->maps, opts->maps_str);
if (!ASSERT_OK(err, "fs_cfg_maps")) if (!ASSERT_OK(err, "fs_cfg_maps"))
return err; return err;
err = set_delegate_mask(fs_fd, "delegate_progs", opts->progs); err = set_delegate_mask(fs_fd, "delegate_progs", opts->progs, opts->progs_str);
if (!ASSERT_OK(err, "fs_cfg_progs")) if (!ASSERT_OK(err, "fs_cfg_progs"))
return err; return err;
err = set_delegate_mask(fs_fd, "delegate_attachs", opts->attachs); err = set_delegate_mask(fs_fd, "delegate_attachs", opts->attachs, opts->attachs_str);
if (!ASSERT_OK(err, "fs_cfg_attachs")) if (!ASSERT_OK(err, "fs_cfg_attachs"))
return err; return err;
...@@ -295,13 +307,13 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba ...@@ -295,13 +307,13 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba
} }
/* ensure unprivileged child cannot set delegation options */ /* ensure unprivileged child cannot set delegation options */
err = set_delegate_mask(fs_fd, "delegate_cmds", 0x1); err = set_delegate_mask(fs_fd, "delegate_cmds", 0x1, NULL);
ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm"); ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm");
err = set_delegate_mask(fs_fd, "delegate_maps", 0x1); err = set_delegate_mask(fs_fd, "delegate_maps", 0x1, NULL);
ASSERT_EQ(err, -EPERM, "delegate_maps_eperm"); ASSERT_EQ(err, -EPERM, "delegate_maps_eperm");
err = set_delegate_mask(fs_fd, "delegate_progs", 0x1); err = set_delegate_mask(fs_fd, "delegate_progs", 0x1, NULL);
ASSERT_EQ(err, -EPERM, "delegate_progs_eperm"); ASSERT_EQ(err, -EPERM, "delegate_progs_eperm");
err = set_delegate_mask(fs_fd, "delegate_attachs", 0x1); err = set_delegate_mask(fs_fd, "delegate_attachs", 0x1, NULL);
ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm"); ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm");
/* pass BPF FS context object to parent */ /* pass BPF FS context object to parent */
...@@ -325,22 +337,22 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba ...@@ -325,22 +337,22 @@ static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callba
} }
/* ensure unprivileged child cannot reconfigure to set delegation options */ /* ensure unprivileged child cannot reconfigure to set delegation options */
err = set_delegate_mask(fs_fd, "delegate_cmds", ~0ULL); err = set_delegate_mask(fs_fd, "delegate_cmds", 0, "any");
if (!ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm_reconfig")) { if (!ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm_reconfig")) {
err = -EINVAL; err = -EINVAL;
goto cleanup; goto cleanup;
} }
err = set_delegate_mask(fs_fd, "delegate_maps", ~0ULL); err = set_delegate_mask(fs_fd, "delegate_maps", 0, "any");
if (!ASSERT_EQ(err, -EPERM, "delegate_maps_eperm_reconfig")) { if (!ASSERT_EQ(err, -EPERM, "delegate_maps_eperm_reconfig")) {
err = -EINVAL; err = -EINVAL;
goto cleanup; goto cleanup;
} }
err = set_delegate_mask(fs_fd, "delegate_progs", ~0ULL); err = set_delegate_mask(fs_fd, "delegate_progs", 0, "any");
if (!ASSERT_EQ(err, -EPERM, "delegate_progs_eperm_reconfig")) { if (!ASSERT_EQ(err, -EPERM, "delegate_progs_eperm_reconfig")) {
err = -EINVAL; err = -EINVAL;
goto cleanup; goto cleanup;
} }
err = set_delegate_mask(fs_fd, "delegate_attachs", ~0ULL); err = set_delegate_mask(fs_fd, "delegate_attachs", 0, "any");
if (!ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm_reconfig")) { if (!ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm_reconfig")) {
err = -EINVAL; err = -EINVAL;
goto cleanup; goto cleanup;
...@@ -933,8 +945,8 @@ void test_token(void) ...@@ -933,8 +945,8 @@ void test_token(void)
{ {
if (test__start_subtest("map_token")) { if (test__start_subtest("map_token")) {
struct bpffs_opts opts = { struct bpffs_opts opts = {
.cmds = 1ULL << BPF_MAP_CREATE, .cmds_str = "map_create",
.maps = 1ULL << BPF_MAP_TYPE_STACK, .maps_str = "stack",
}; };
subtest_userns(&opts, userns_map_create); subtest_userns(&opts, userns_map_create);
...@@ -948,9 +960,9 @@ void test_token(void) ...@@ -948,9 +960,9 @@ void test_token(void)
} }
if (test__start_subtest("prog_token")) { if (test__start_subtest("prog_token")) {
struct bpffs_opts opts = { struct bpffs_opts opts = {
.cmds = 1ULL << BPF_PROG_LOAD, .cmds_str = "PROG_LOAD",
.progs = 1ULL << BPF_PROG_TYPE_XDP, .progs_str = "XDP",
.attachs = 1ULL << BPF_XDP, .attachs_str = "xdp",
}; };
subtest_userns(&opts, userns_prog_load); subtest_userns(&opts, userns_prog_load);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment