mirror of
https://github.com/torvalds/linux.git
synced 2024-09-21 15:33:19 +00:00
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.org Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
0f5d5454c7
|
@ -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);
|
||||
if (opts->delegate_cmds || opts->delegate_maps ||
|
||||
opts->delegate_progs || opts->delegate_attachs) {
|
||||
struct bpffs_btf_enums info;
|
||||
|
||||
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);
|
||||
/* ignore errors, fallback to hex */
|
||||
(void)find_bpffs_btf_enums(&info);
|
||||
|
||||
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_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);
|
||||
}
|
||||
|
||||
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);
|
||||
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(¶m->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;
|
||||
}
|
||||
|
||||
|
|
|
@ -66,14 +66,22 @@ static int restore_priv_caps(__u64 old_caps)
|
|||
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];
|
||||
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,
|
||||
mask == ~0ULL ? "any" : buf, 0);
|
||||
mask_str, 0);
|
||||
if (err < 0)
|
||||
err = -errno;
|
||||
return err;
|
||||
|
@ -86,6 +94,10 @@ struct bpffs_opts {
|
|||
__u64 maps;
|
||||
__u64 progs;
|
||||
__u64 attachs;
|
||||
const char *cmds_str;
|
||||
const char *maps_str;
|
||||
const char *progs_str;
|
||||
const char *attachs_str;
|
||||
};
|
||||
|
||||
static int create_bpffs_fd(void)
|
||||
|
@ -104,16 +116,16 @@ static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts)
|
|||
int mnt_fd, err;
|
||||
|
||||
/* 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"))
|
||||
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"))
|
||||
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"))
|
||||
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"))
|
||||
return err;
|
||||
|
||||
|
@ -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 */
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
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");
|
||||
|
||||
/* 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
|
|||
}
|
||||
|
||||
/* 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")) {
|
||||
err = -EINVAL;
|
||||
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")) {
|
||||
err = -EINVAL;
|
||||
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")) {
|
||||
err = -EINVAL;
|
||||
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")) {
|
||||
err = -EINVAL;
|
||||
goto cleanup;
|
||||
|
@ -933,8 +945,8 @@ void test_token(void)
|
|||
{
|
||||
if (test__start_subtest("map_token")) {
|
||||
struct bpffs_opts opts = {
|
||||
.cmds = 1ULL << BPF_MAP_CREATE,
|
||||
.maps = 1ULL << BPF_MAP_TYPE_STACK,
|
||||
.cmds_str = "map_create",
|
||||
.maps_str = "stack",
|
||||
};
|
||||
|
||||
subtest_userns(&opts, userns_map_create);
|
||||
|
@ -948,9 +960,9 @@ void test_token(void)
|
|||
}
|
||||
if (test__start_subtest("prog_token")) {
|
||||
struct bpffs_opts opts = {
|
||||
.cmds = 1ULL << BPF_PROG_LOAD,
|
||||
.progs = 1ULL << BPF_PROG_TYPE_XDP,
|
||||
.attachs = 1ULL << BPF_XDP,
|
||||
.cmds_str = "PROG_LOAD",
|
||||
.progs_str = "XDP",
|
||||
.attachs_str = "xdp",
|
||||
};
|
||||
|
||||
subtest_userns(&opts, userns_prog_load);
|
||||
|
|
Loading…
Reference in New Issue
Block a user