diff options
| author | Daniel Borkmann <daniel@iogearbox.net> | 2026-06-02 09:40:12 +0200 |
|---|---|---|
| committer | Christian Brauner <brauner@kernel.org> | 2026-06-06 15:22:44 +0200 |
| commit | 9722955b54307e9070994f2382ec06af3d7405e0 (patch) | |
| tree | a599499f411dfa7cc47dcda733e04821a2a7fe86 /fs | |
| parent | 6cccc49b027c7551ffc1d2532f2ef1922661f3da (diff) | |
bpf: Add simple xattr support to bpffs
Add support for extended attributes on bpffs inodes so that user space
and BPF LSM programs can attach metadata, for example, a content hash
or a security label - to a pinned object or directory. BPF LSM or user
space tooling can then uniformly look at this (e.g. security.bpf.*) in
similar way to other fs'es. The store is in-memory and non-persistent:
it lives only for the lifetime of the mount, like everything else in
bpffs. The modelling is similar to tmpfs.
bpffs serves the trusted.* and security.* namespaces; user.* is left
unsupported. As bpffs is FS_USERNS_MOUNT, security.* is reachable by
the unprivileged mounter in a user namespace, and thus we are using
the simple_xattr_set_limited infra there (trusted.* needs global
CAP_SYS_ADMIN).
bpf_fill_super() is open-coded instead of using simple_fill_super(),
because the root inode must now be allocated through bpf_fs_alloc_inode()
i.e. carry the bpf_fs_inode wrapper and come from the right cache -
which requires s_op (and s_xattr) to be installed before the first
inode is created. While at it, also harden s_iflags with SB_I_NOEXEC
and SB_I_NODEV.
bpf_fs_listxattr() is only reachable through the filesystem via
i_op->listxattr, so the BPF token inode is left untouched. Name-based
fsetxattr()/fgetxattr() on a token fd still work since the get/set
handlers are installed at the superblock.
For security.* namespace, we use simple_xattr_set_limited() but
there was no simple_xattr_add_limited() API yet which was needed
in bpf_fs_initxattrs() to avoid underflows in the accounting. The
symlink target is freed in bpf_free_inode() rather than in
bpf_destroy_inode() so that it is released only after an RCU grace
period, as an RCU path walk following the symlink may still
dereference inode->i_link in security_inode_follow_link(). Lastly,
the bpf_symlink() allocated the symlink target is switched to
GFP_KERNEL_ACCOUNT, so the string is charged to the caller's memcg.
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://patch.msgid.link/20260602074012.416289-1-daniel@iogearbox.net
Cc: Christian Brauner <brauner@kernel.org>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/xattr.c | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/fs/xattr.c b/fs/xattr.c index 89374cd9029a..ec2a4f3759d8 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -1679,6 +1679,39 @@ int simple_xattr_add(struct simple_xattr_cache *cache, struct list_head *xattrs, } /** + * simple_xattr_add_limited - add an xattr object, charging per-inode limits + * @cache: anchor for the hash table + * @xattrs: the header of the xattr object + * @limits: per-inode limit counters + * @new_xattr: the xattr object to add + * + * Like simple_xattr_add(), but also accounts @new_xattr against @limits so + * that a later removal or replacement of it through simple_xattr_set_limited() + * decrements counters that were actually incremented, rather than underflowing + * them. Use this instead of simple_xattr_add() when seeding initial xattrs + * that share a namespace with the limited set/remove path. + * + * Return: On success zero is returned. On failure a negative error code is + * returned. + */ +int simple_xattr_add_limited(struct simple_xattr_cache *cache, + struct list_head *xattrs, + struct simple_xattr_limits *limits, + struct simple_xattr *new_xattr) +{ + int err; + + err = simple_xattr_limits_inc(limits, new_xattr->size); + if (err) + return err; + + err = simple_xattr_add(cache, xattrs, new_xattr); + if (err) + simple_xattr_limits_dec(limits, new_xattr->size); + return err; +} + +/** * simple_xattrs_free - free xattrs * @cache: anchor for the hash table * @xattrs: xattr header whose xattrs to destroy |
