mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02: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
					
				
					 2 changed files with 240 additions and 55 deletions
				
			
		|  | @ -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 a new issue
	
	 Alexei Starovoitov
						Alexei Starovoitov