Loading fs/bpf_fs_kfuncs.c +34 −0 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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) Loading fs/kernfs/inode.c +40 −34 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); } Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading kernel/bpf/helpers.c +3 −0 Original line number Diff line number Diff line Loading @@ -3779,6 +3779,9 @@ BTF_ID_FLAGS(func, bpf_strspn); BTF_ID_FLAGS(func, bpf_strcspn); BTF_ID_FLAGS(func, bpf_strstr); BTF_ID_FLAGS(func, bpf_strnstr); #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 = { Loading kernel/bpf/verifier.c +5 −0 Original line number Diff line number Diff line Loading @@ -7063,6 +7063,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; Loading Loading @@ -7112,6 +7116,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"); } Loading tools/testing/selftests/bpf/bpf_experimental.h +3 −0 Original line number Diff line number Diff line Loading @@ -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
fs/bpf_fs_kfuncs.c +34 −0 Original line number Diff line number Diff line Loading @@ -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> Loading Loading @@ -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) Loading
fs/kernfs/inode.c +40 −34 Original line number Diff line number Diff line Loading @@ -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) Loading Loading @@ -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); } Loading @@ -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 Loading Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading
kernel/bpf/helpers.c +3 −0 Original line number Diff line number Diff line Loading @@ -3779,6 +3779,9 @@ BTF_ID_FLAGS(func, bpf_strspn); BTF_ID_FLAGS(func, bpf_strcspn); BTF_ID_FLAGS(func, bpf_strstr); BTF_ID_FLAGS(func, bpf_strnstr); #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 = { Loading
kernel/bpf/verifier.c +5 −0 Original line number Diff line number Diff line Loading @@ -7063,6 +7063,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; Loading Loading @@ -7112,6 +7116,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"); } Loading
tools/testing/selftests/bpf/bpf_experimental.h +3 −0 Original line number Diff line number Diff line Loading @@ -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