[PATCH v6 bpf-next 2/4] security: add security_lsmxattr_add()
David Windsor
dwindsor at gmail.com
Thu Jul 30 23:45:31 UTC 2026
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 <paul at paul-moore.com>
Signed-off-by: David Windsor <dwindsor at gmail.com>
---
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
More information about the Linux-security-module-archive
mailing list