[PATCH v6 bpf-next 2/4] security: add security_lsmxattr_add()
David Windsor <[email protected]> Thu, 30 Jul 2026 19:45:31 -0400
| Newsgroups | org.kernel.vger.linux-integrity,org.kernel.vger.bpf,org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-kselftest,org.kernel.vger.linux-security-module,org.kernel.vger.selinux |
|---|---|
| Message-ID | <[email protected]> |
Add security_lsmxattr_add(), which claims a slot in the inode_init_security xattr array on behalf of the calling LSM and fills it with a copy of the given name and value. Callers pass only the name components beyond their LSM's standard xattr suffix; security_lsmxattr_add() builds the full xattr name from the suffix associated with the given lsm_id. Suggested-by: Paul Moore <[email protected]> Signed-off-by: David Windsor <[email protected]> --- include/linux/bpf_lsm.h | 3 ++ include/linux/security.h | 10 +++++ security/bpf/hooks.c | 1 + security/security.c | 96 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) diff --git a/include/linux/bpf_lsm.h b/include/linux/bpf_lsm.h index dda272d78f01..4bf350ef02f4 100644 --- a/include/linux/bpf_lsm.h +++ b/include/linux/bpf_lsm.h @@ -12,6 +12,9 @@ #include <linux/bpf_verifier.h> #include <linux/lsm_hooks.h> +/* max bpf xattrs per inode */ +#define BPF_LSM_INODE_INIT_XATTRS 4 + #ifdef CONFIG_BPF_LSM extern bool bpf_lsm_initialized __ro_after_init; diff --git a/include/linux/security.h b/include/linux/security.h index 0be590c40689..d35fde7aa11f 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -406,6 +406,9 @@ void security_inode_free(struct inode *inode); int security_inode_init_security(struct inode *inode, struct inode *dir, const struct qstr *qstr, initxattrs initxattrs, void *fs_data); +int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name_extra, const void *value, + size_t value_len); int security_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode); @@ -900,6 +903,13 @@ static inline int security_inode_init_security(struct inode *inode, return 0; } +static inline int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name_extra, + const void *value, size_t value_len) +{ + return -EOPNOTSUPP; +} + static inline int security_inode_init_security_anon(struct inode *inode, const struct qstr *name, const struct inode *context_inode) diff --git a/security/bpf/hooks.c b/security/bpf/hooks.c index 7b98f5d1e2be..8f8c3de3035f 100644 --- a/security/bpf/hooks.c +++ b/security/bpf/hooks.c @@ -33,6 +33,7 @@ static int __init bpf_lsm_init(void) struct lsm_blob_sizes bpf_lsm_blob_sizes __ro_after_init = { .lbs_inode = sizeof(struct bpf_storage_blob), + .lbs_xattr_count = BPF_LSM_INODE_INIT_XATTRS, }; DEFINE_LSM(bpf) = { diff --git a/security/security.c b/security/security.c index 2ad7f09c1a61..ae72102cd29b 100644 --- a/security/security.c +++ b/security/security.c @@ -12,6 +12,7 @@ #define pr_fmt(fmt) "LSM: " fmt #include <linux/bpf.h> +#include <linux/bpf_lsm.h> #include <linux/capability.h> #include <linux/dcache.h> #include <linux/export.h> @@ -1376,6 +1377,101 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, } EXPORT_SYMBOL(security_inode_init_security); +static unsigned int lsm_xattrs_used(const struct lsm_xattrs *xattrs, + const char *prefix) +{ + size_t prefix_len = strlen(prefix); + unsigned int i, n = 0; + + for (i = 0; i < xattrs->xattr_count; i++) { + const char *name = xattrs->xattrs[i].name; + + if (name && !strncmp(name, prefix, prefix_len)) + n++; + } + return n; +} + +/** + * security_lsmxattr_add() - Add an xattr during inode_init_security + * @xattrs: xattr state shared by inode_init_security hooks + * @lsm_id: LSM_ID_* value identifying the calling LSM + * @name_extra: xattr name components beyond the calling LSM's standard + * xattr suffix, NULL if the standard suffix is the full name + * @value: xattr value + * @value_len: length of @value + * + * Claim an xattr slot in @xattrs on behalf of the LSM identified by + * @lsm_id and fill it with a copy of @value. The xattr name is built from + * the standard xattr suffix of the calling LSM, followed by @name_extra. + * Callers can invoke this function from non-sleepable context. + * + * Return: Returns 0 on success or if the filesystem does not accept xattrs + * at inode creation, -ENOSPC if the calling LSM's slot budget is + * exhausted, negative values on other errors. + */ +int security_lsmxattr_add(struct lsm_xattrs *xattrs, u64 lsm_id, + const char *name_extra, const void *value, + size_t value_len) +{ + struct xattr *xattr; + void *xattr_value; + const char *suffix; + size_t suffix_len, extra_len, name_len; + + if (!xattrs || !value) + return -EINVAL; + + /* The filesystem did not provide an initxattrs callback. */ + if (!xattrs->xattrs) + return 0; + + switch (lsm_id) { + case LSM_ID_BPF: + if (!name_extra || !name_extra[0]) + return -EINVAL; + suffix = XATTR_BPF_LSM_SUFFIX; + if (lsm_xattrs_used(xattrs, XATTR_BPF_LSM_SUFFIX) >= + BPF_LSM_INODE_INIT_XATTRS) + return -ENOSPC; + break; + default: + return -EINVAL; + } + + suffix_len = strlen(suffix); + extra_len = name_extra ? strlen(name_extra) : 0; + name_len = suffix_len + extra_len; + if (name_len > XATTR_NAME_MAX) + return -EINVAL; + if (value_len == 0 || value_len > XATTR_SIZE_MAX) + return -EINVAL; + + /* Combine xattr value + name into one allocation. */ + xattr_value = kmalloc(value_len + name_len + 1, GFP_NOWAIT); + if (!xattr_value) + return -ENOMEM; + + memcpy(xattr_value, value, value_len); + memcpy(xattr_value + value_len, suffix, suffix_len); + if (extra_len) + memcpy(xattr_value + value_len + suffix_len, name_extra, + extra_len); + ((char *)xattr_value)[value_len + name_len] = '\0'; + + xattr = lsm_get_xattr_slot(xattrs); + if (!xattr) { + kfree(xattr_value); + return -ENOSPC; + } + + xattr->value = xattr_value; + xattr->name = (const char *)xattr_value + value_len; + xattr->value_len = value_len; + + return 0; +} + /** * security_inode_init_security_anon() - Initialize an anonymous inode * @inode: the inode -- 2.53.0