Unverified Commit 13b0cce9 authored by Christian Brauner's avatar Christian Brauner
Browse files

Merge patch series "Introduce bpf_cgroup_read_xattr"

Song Liu <song@kernel.org> says:

Introduce a new kfunc bpf_cgroup_read_xattr, which can read xattr from
cgroupfs nodes. The primary users are LSMs, cgroup programs, and sched_ext.

* patches from https://lore.kernel.org/20250623063854.1896364-1-song@kernel.org:
  selftests/bpf: Add tests for bpf_cgroup_read_xattr
  bpf: Mark cgroup_subsys_state->cgroup RCU safe
  bpf: Introduce bpf_cgroup_read_xattr to read xattr of cgroup's node
  kernfs: remove iattr_mutex

Link: https://lore.kernel.org/20250623063854.1896364-1-song@kernel.org


Signed-off-by: default avatarChristian Brauner <brauner@kernel.org>
parents 19272b37 f4fba2d6
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@
#include <linux/fs.h>
#include <linux/fsnotify.h>
#include <linux/file.h>
#include <linux/kernfs.h>
#include <linux/mm.h>
#include <linux/xattr.h>

@@ -322,6 +323,39 @@ __bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name_
	return ret;
}

#ifdef CONFIG_CGROUPS
/**
 * bpf_cgroup_read_xattr - read xattr of a cgroup's node in cgroupfs
 * @cgroup: cgroup to get xattr from
 * @name__str: name of the xattr
 * @value_p: output buffer of the xattr value
 *
 * Get xattr *name__str* of *cgroup* and store the output in *value_ptr*.
 *
 * For security reasons, only *name__str* with prefix "user." is allowed.
 *
 * Return: length of the xattr value on success, a negative value on error.
 */
__bpf_kfunc int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str,
					struct bpf_dynptr *value_p)
{
	struct bpf_dynptr_kern *value_ptr = (struct bpf_dynptr_kern *)value_p;
	u32 value_len;
	void *value;

	/* Only allow reading "user.*" xattrs */
	if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
		return -EPERM;

	value_len = __bpf_dynptr_size(value_ptr);
	value = __bpf_dynptr_data_rw(value_ptr, value_len);
	if (!value)
		return -EINVAL;

	return kernfs_xattr_get(cgroup->kn, name__str, value, value_len);
}
#endif /* CONFIG_CGROUPS */

__bpf_kfunc_end_defs();

BTF_KFUNCS_START(bpf_fs_kfunc_set_ids)
+40 −34
Original line number Diff line number Diff line
@@ -24,45 +24,46 @@ static const struct inode_operations kernfs_iops = {
	.listxattr	= kernfs_iop_listxattr,
};

static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, int alloc)
static struct kernfs_iattrs *__kernfs_iattrs(struct kernfs_node *kn, bool alloc)
{
	static DEFINE_MUTEX(iattr_mutex);
	struct kernfs_iattrs *ret;
	struct kernfs_iattrs *ret __free(kfree) = NULL;
	struct kernfs_iattrs *attr;

	mutex_lock(&iattr_mutex);
	attr = READ_ONCE(kn->iattr);
	if (attr || !alloc)
		return attr;

	if (kn->iattr || !alloc)
		goto out_unlock;

	kn->iattr = kmem_cache_zalloc(kernfs_iattrs_cache, GFP_KERNEL);
	if (!kn->iattr)
		goto out_unlock;
	ret = kmem_cache_zalloc(kernfs_iattrs_cache, GFP_KERNEL);
	if (!ret)
		return NULL;

	/* assign default attributes */
	kn->iattr->ia_uid = GLOBAL_ROOT_UID;
	kn->iattr->ia_gid = GLOBAL_ROOT_GID;

	ktime_get_real_ts64(&kn->iattr->ia_atime);
	kn->iattr->ia_mtime = kn->iattr->ia_atime;
	kn->iattr->ia_ctime = kn->iattr->ia_atime;

	simple_xattrs_init(&kn->iattr->xattrs);
	atomic_set(&kn->iattr->nr_user_xattrs, 0);
	atomic_set(&kn->iattr->user_xattr_size, 0);
out_unlock:
	ret = kn->iattr;
	mutex_unlock(&iattr_mutex);
	return ret;
	ret->ia_uid = GLOBAL_ROOT_UID;
	ret->ia_gid = GLOBAL_ROOT_GID;

	ktime_get_real_ts64(&ret->ia_atime);
	ret->ia_mtime = ret->ia_atime;
	ret->ia_ctime = ret->ia_atime;

	simple_xattrs_init(&ret->xattrs);
	atomic_set(&ret->nr_user_xattrs, 0);
	atomic_set(&ret->user_xattr_size, 0);

	/* If someone raced us, recognize it. */
	if (!try_cmpxchg(&kn->iattr, &attr, ret))
		return READ_ONCE(kn->iattr);

	return no_free_ptr(ret);
}

static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn)
{
	return __kernfs_iattrs(kn, 1);
	return __kernfs_iattrs(kn, true);
}

static struct kernfs_iattrs *kernfs_iattrs_noalloc(struct kernfs_node *kn)
{
	return __kernfs_iattrs(kn, 0);
	return __kernfs_iattrs(kn, false);
}

int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr)
@@ -141,9 +142,9 @@ ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size)
	struct kernfs_node *kn = kernfs_dentry_node(dentry);
	struct kernfs_iattrs *attrs;

	attrs = kernfs_iattrs(kn);
	attrs = kernfs_iattrs_noalloc(kn);
	if (!attrs)
		return -ENOMEM;
		return -ENODATA;

	return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size);
}
@@ -166,9 +167,10 @@ static inline void set_inode_attr(struct inode *inode,

static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode)
{
	struct kernfs_iattrs *attrs = kn->iattr;
	struct kernfs_iattrs *attrs;

	inode->i_mode = kn->mode;
	attrs = kernfs_iattrs_noalloc(kn);
	if (attrs)
		/*
		 * kernfs_node has non-default attributes get them from
@@ -306,7 +308,9 @@ int kernfs_xattr_set(struct kernfs_node *kn, const char *name,
		     const void *value, size_t size, int flags)
{
	struct simple_xattr *old_xattr;
	struct kernfs_iattrs *attrs = kernfs_iattrs(kn);
	struct kernfs_iattrs *attrs;

	attrs = kernfs_iattrs(kn);
	if (!attrs)
		return -ENOMEM;

@@ -345,8 +349,9 @@ static int kernfs_vfs_user_xattr_add(struct kernfs_node *kn,
				     struct simple_xattrs *xattrs,
				     const void *value, size_t size, int flags)
{
	atomic_t *sz = &kn->iattr->user_xattr_size;
	atomic_t *nr = &kn->iattr->nr_user_xattrs;
	struct kernfs_iattrs *attr = kernfs_iattrs_noalloc(kn);
	atomic_t *sz = &attr->user_xattr_size;
	atomic_t *nr = &attr->nr_user_xattrs;
	struct simple_xattr *old_xattr;
	int ret;

@@ -384,8 +389,9 @@ static int kernfs_vfs_user_xattr_rm(struct kernfs_node *kn,
				    struct simple_xattrs *xattrs,
				    const void *value, size_t size, int flags)
{
	atomic_t *sz = &kn->iattr->user_xattr_size;
	atomic_t *nr = &kn->iattr->nr_user_xattrs;
	struct kernfs_iattrs *attr = kernfs_iattrs_noalloc(kn);
	atomic_t *sz = &attr->user_xattr_size;
	atomic_t *nr = &attr->nr_user_xattrs;
	struct simple_xattr *old_xattr;

	old_xattr = simple_xattr_set(xattrs, full_name, value, size, flags);
+3 −0
Original line number Diff line number Diff line
@@ -3397,6 +3397,9 @@ BTF_ID_FLAGS(func, bpf_iter_dmabuf_next, KF_ITER_NEXT | KF_RET_NULL | KF_SLEEPAB
BTF_ID_FLAGS(func, bpf_iter_dmabuf_destroy, KF_ITER_DESTROY | KF_SLEEPABLE)
#endif
BTF_ID_FLAGS(func, __bpf_trap)
#ifdef CONFIG_CGROUPS
BTF_ID_FLAGS(func, bpf_cgroup_read_xattr, KF_RCU)
#endif
BTF_KFUNCS_END(common_btf_ids)

static const struct btf_kfunc_id_set common_kfunc_set = {
+5 −0
Original line number Diff line number Diff line
@@ -6993,6 +6993,10 @@ BTF_TYPE_SAFE_RCU(struct css_set) {
	struct cgroup *dfl_cgrp;
};
BTF_TYPE_SAFE_RCU(struct cgroup_subsys_state) {
	struct cgroup *cgroup;
};
/* RCU trusted: these fields are trusted in RCU CS and can be NULL */
BTF_TYPE_SAFE_RCU_OR_NULL(struct mm_struct) {
	struct file __rcu *exe_file;
@@ -7043,6 +7047,7 @@ static bool type_is_rcu(struct bpf_verifier_env *env,
	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct task_struct));
	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct cgroup));
	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct css_set));
	BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU(struct cgroup_subsys_state));
	return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_rcu");
}
+3 −0
Original line number Diff line number Diff line
@@ -596,4 +596,7 @@ extern int bpf_iter_dmabuf_new(struct bpf_iter_dmabuf *it) __weak __ksym;
extern struct dma_buf *bpf_iter_dmabuf_next(struct bpf_iter_dmabuf *it) __weak __ksym;
extern void bpf_iter_dmabuf_destroy(struct bpf_iter_dmabuf *it) __weak __ksym;

extern int bpf_cgroup_read_xattr(struct cgroup *cgroup, const char *name__str,
				 struct bpf_dynptr *value_p) __weak __ksym;

#endif
Loading