[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