The vfs has long had a fallback to obtain the security.* xattrs from the LSM when the filesystem does not implement its own listxattr, but shmem/tmpfs and kernfs later gained their own xattr handlers to support other xattrs. Unfortunately, as a side effect, tmpfs and kernfs-based filesystems like sysfs no longer return the synthetic security.* xattr names via listxattr unless they are explicitly set by userspace or initially set upon inode creation after policy load. coreutils has recently switched from unconditionally invoking getxattr for security.* for ls -Z via libselinux to only doing so if listxattr returns the xattr name, breaking ls -Z of such inodes. Before: $ getfattr -m.* /run/initramfs <no output> $ getfattr -m.* /sys/kernel/fscaps <no output> After: $ getfattr -m.* /run/initramfs security.selinux $ getfattr -m.* /sys/kernel/fscaps security.selinux Link: https://lore.kernel.org/selinux/CAFqZXNtF8wDyQajPCdGn=iOawX4y77ph0EcfcqcUUj+T87FKyA@xxxxxxxxxxxxxx/ Link: https://lore.kernel.org/selinux/20250423175728.3185-2-stephen.smalley.work@xxxxxxxxx/ Signed-off-by: Stephen Smalley <stephen.smalley.work@xxxxxxxxx> --- fs/kernfs/inode.c | 8 +++++++- fs/xattr.c | 13 +++++++++++++ mm/shmem.c | 8 +++++++- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index b83054da68b3..8fd69e48d32d 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -140,12 +140,18 @@ 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; + ssize_t sz; attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; - return simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); + sz = simple_xattr_list(d_inode(dentry), &attrs->xattrs, buf, size); + if (sz >= 0 && sz <= size) + sz += security_inode_listsecurity(d_inode(dentry), + buf ? buf + sz : NULL, + size - sz); + return sz; } static inline void set_default_inode_attr(struct inode *inode, umode_t mode) diff --git a/fs/xattr.c b/fs/xattr.c index 02bee149ad96..68ac91d0dbc3 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -1428,6 +1428,15 @@ static bool xattr_is_trusted(const char *name) return !strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN); } +static bool xattr_is_maclabel(const char *name) +{ + const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; + + return !strncmp(name, XATTR_SECURITY_PREFIX, + XATTR_SECURITY_PREFIX_LEN) && + security_ismaclabel(suffix); +} + /** * simple_xattr_list - list all xattr objects * @inode: inode from which to get the xattrs @@ -1468,6 +1477,10 @@ ssize_t simple_xattr_list(struct inode *inode, struct simple_xattrs *xattrs, if (!trusted && xattr_is_trusted(xattr->name)) continue; + /* skip MAC labels; these are provided by LSM separately */ + if (xattr_is_maclabel(xattr->name)) + continue; + err = xattr_list_one(&buffer, &remaining_size, xattr->name); if (err) break; diff --git a/mm/shmem.c b/mm/shmem.c index 99327c30507c..69f664668a3a 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -4372,7 +4372,13 @@ static const struct xattr_handler * const shmem_xattr_handlers[] = { static ssize_t shmem_listxattr(struct dentry *dentry, char *buffer, size_t size) { struct shmem_inode_info *info = SHMEM_I(d_inode(dentry)); - return simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, size); + ssize_t sz = simple_xattr_list(d_inode(dentry), &info->xattrs, buffer, + size); + if (sz >= 0 && sz <= size) + sz += security_inode_listsecurity(d_inode(dentry), + buffer ? buffer + sz : NULL, + size - sz); + return sz; } #endif /* CONFIG_TMPFS_XATTR */ -- 2.49.0