Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH v6 08/12] ima: Introduce ima_dump_measurement()
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Introduce ima_dump_measurement() to simplify the code of
ima_dump_measurement_list() and to avoid repeating the
ima_dump_measurement() code block if iteration occurs on multiple lists.

No functional change: only code moved to a separate function.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 security/integrity/ima/ima_kexec.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 8dc9459622b3..26d41974429e 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -80,6 +80,17 @@ static int ima_alloc_kexec_file_buf(size_t segment_size)
 	return 0;
 }
 
+static int ima_dump_measurement(struct ima_kexec_hdr *khdr,
+				struct ima_queue_entry *qe)
+{
+	if (ima_kexec_file.count >= ima_kexec_file.size)
+		return -EINVAL;
+
+	khdr->count++;
+	ima_measurements_show(&ima_kexec_file, qe);
+	return 0;
+}
+
 static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
 				     unsigned long segment_size)
 {
@@ -97,13 +108,9 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
 	khdr.version = 1;
 	/* This is an append-only list, no need to hold the RCU read lock */
 	list_for_each_entry_rcu(qe, &ima_measurements, later, true) {
-		if (ima_kexec_file.count < ima_kexec_file.size) {
-			khdr.count++;
-			ima_measurements_show(&ima_kexec_file, qe);
-		} else {
-			ret = -EINVAL;
+		ret = ima_dump_measurement(&khdr, qe);
+		if (ret < 0)
 			break;
-		}
 	}
 
 	/*
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 07/12] ima: Use snprintf() in create_securityfs_measurement_lists
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Use the more secure snprintf() function (accepting the buffer size) in
create_securityfs_measurement_lists().

No functional change: sprintf() and snprintf() have the same behavior.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 security/integrity/ima/ima_fs.c | 20 ++++++++++++--------
 1 file changed, 12 insertions(+), 8 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 91bd831d070f..f6ecee2d7699 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -503,11 +503,13 @@ static int __init create_securityfs_measurement_lists(void)
 		struct dentry *dentry;
 
 		if (algo == HASH_ALGO__LAST)
-			sprintf(file_name, "ascii_runtime_measurements_tpm_alg_%x",
-				ima_tpm_chip->allocated_banks[i].alg_id);
+			snprintf(file_name, sizeof(file_name),
+				 "ascii_runtime_measurements_tpm_alg_%x",
+				 ima_tpm_chip->allocated_banks[i].alg_id);
 		else
-			sprintf(file_name, "ascii_runtime_measurements_%s",
-				hash_algo_name[algo]);
+			snprintf(file_name, sizeof(file_name),
+				 "ascii_runtime_measurements_%s",
+				 hash_algo_name[algo]);
 		dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
 						ima_dir, (void *)(uintptr_t)i,
 						&ima_ascii_measurements_ops);
@@ -515,11 +517,13 @@ static int __init create_securityfs_measurement_lists(void)
 			return PTR_ERR(dentry);
 
 		if (algo == HASH_ALGO__LAST)
-			sprintf(file_name, "binary_runtime_measurements_tpm_alg_%x",
-				ima_tpm_chip->allocated_banks[i].alg_id);
+			snprintf(file_name, sizeof(file_name),
+				 "binary_runtime_measurements_tpm_alg_%x",
+				 ima_tpm_chip->allocated_banks[i].alg_id);
 		else
-			sprintf(file_name, "binary_runtime_measurements_%s",
-				hash_algo_name[algo]);
+			snprintf(file_name, sizeof(file_name),
+				 "binary_runtime_measurements_%s",
+				 hash_algo_name[algo]);
 		dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
 						ima_dir, (void *)(uintptr_t)i,
 						&ima_measurements_ops);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 06/12] ima: Mediate open/release method of the measurements list
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Introduce the ima_measure_users counter, to implement a semaphore-like
locking scheme where the binary and ASCII measurements list interfaces can
be concurrently opened by multiple readers, or alternatively by a single
writer. In addition, allow the same writer to open the other interfaces for
write or read/write, so that it can see the same measurement state across
all the interfaces.

A semaphore cannot be used because the kernel cannot return to user space
with a lock held.

Introduce the ima_measure_lock() and ima_measure_unlock() primitives, to
respectively lock/unlock the interfaces (safely with the ima_measure_users
counter, without holding a lock).

Finally, introduce _ima_measurements_open() to lock the interface before
seq_open(), and call it from ima_measurements_open() and
ima_ascii_measurements_open(). And, introduce ima_measurements_release(),
to unlock the interface.

Require CAP_SYS_ADMIN if the interface is opened for write (not possible
for the current measurements interfaces, since they only have read
permission).

No functional changes: multiple readers are allowed as before.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 security/integrity/ima/ima_fs.c | 102 ++++++++++++++++++++++++++++++--
 1 file changed, 98 insertions(+), 4 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index dcdc4cb8fa0f..91bd831d070f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,6 +25,10 @@
 #include "ima.h"
 
 static DEFINE_MUTEX(ima_write_mutex);
+static DEFINE_MUTEX(ima_measure_mutex);
+static long ima_measure_users;
+static struct task_struct *measure_writer;
+static long measure_writer_extra_writes;
 
 bool ima_canonical_fmt;
 static int __init default_canonical_fmt_setup(char *str)
@@ -209,16 +213,105 @@ static const struct seq_operations ima_measurments_seqops = {
 	.show = ima_measurements_show
 };
 
+static int ima_measure_lock(bool write)
+{
+	mutex_lock(&ima_measure_mutex);
+	/* Overflow check. */
+	if (!write && ima_measure_users == LONG_MAX) {
+		mutex_unlock(&ima_measure_mutex);
+		return -ENFILE;
+	}
+
+	/* Same writer can do additional writes or read/writes. */
+	if (write && current == measure_writer) {
+		measure_writer_extra_writes++;
+		mutex_unlock(&ima_measure_mutex);
+		return 0;
+	}
+
+	/*
+	 * ima_measure_users: > 0 open readers
+	 * ima_measure_users: == -1 open writer
+	 */
+	if ((write && ima_measure_users != 0) ||
+	    (!write && ima_measure_users < 0)) {
+		mutex_unlock(&ima_measure_mutex);
+		return -EBUSY;
+	}
+
+	if (write) {
+		ima_measure_users--;
+		/* Pointer valid, no reuse while the file descriptor is open. */
+		measure_writer = current;
+	} else {
+		ima_measure_users++;
+	}
+	mutex_unlock(&ima_measure_mutex);
+	return 0;
+}
+
+static void ima_measure_unlock(bool write)
+{
+	mutex_lock(&ima_measure_mutex);
+	/* Decrement additional writes or read/writes. */
+	if (write && current == measure_writer &&
+	    measure_writer_extra_writes != 0) {
+		measure_writer_extra_writes--;
+		mutex_unlock(&ima_measure_mutex);
+		return;
+	}
+	if (write) {
+		ima_measure_users++;
+		measure_writer = NULL;
+	} else {
+		ima_measure_users--;
+	}
+	mutex_unlock(&ima_measure_mutex);
+}
+
+static int _ima_measurements_open(struct inode *inode, struct file *file,
+				  const struct seq_operations *seq_ops)
+{
+	bool write = (file->f_mode & FMODE_WRITE);
+	int ret;
+
+	if (write && !capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	ret = ima_measure_lock(write);
+	if (ret < 0)
+		return ret;
+
+	ret = seq_open(file, seq_ops);
+	if (ret < 0)
+		ima_measure_unlock(write);
+
+	return ret;
+}
+
 static int ima_measurements_open(struct inode *inode, struct file *file)
 {
-	return seq_open(file, &ima_measurments_seqops);
+	return _ima_measurements_open(inode, file, &ima_measurments_seqops);
+}
+
+static int ima_measurements_release(struct inode *inode, struct file *file)
+{
+	bool write = (file->f_mode & FMODE_WRITE);
+	int ret;
+
+	/* seq_release() always returns zero. */
+	ret = seq_release(inode, file);
+
+	ima_measure_unlock(write);
+
+	return ret;
 }
 
 static const struct file_operations ima_measurements_ops = {
 	.open = ima_measurements_open,
 	.read = seq_read,
 	.llseek = seq_lseek,
-	.release = seq_release,
+	.release = ima_measurements_release,
 };
 
 void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
@@ -283,14 +376,15 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
 
 static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
 {
-	return seq_open(file, &ima_ascii_measurements_seqops);
+	return _ima_measurements_open(inode, file,
+				      &ima_ascii_measurements_seqops);
 }
 
 static const struct file_operations ima_ascii_measurements_ops = {
 	.open = ima_ascii_measurements_open,
 	.read = seq_read,
 	.llseek = seq_lseek,
-	.release = seq_release,
+	.release = ima_measurements_release,
 };
 
 static ssize_t ima_read_policy(char *path)
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 05/12] ima: Introduce _ima_measurements_start() and _ima_measurements_next()
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Introduce _ima_measurements_start() and _ima_measurements_next(), renamed
from ima_measurements_start() and ima_measurements_next(), to include the
list head as an additional parameter, so that iteration on different lists
can be implemented by calling those functions.

No functional change: ima_measurements_start() and ima_measurements_next()
pass the ima_measurements list head, used before. They become wrappers for
the new functions.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 security/integrity/ima/ima_fs.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index fcfcf7b6eae2..dcdc4cb8fa0f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -72,14 +72,15 @@ static const struct file_operations ima_measurements_count_ops = {
 };
 
 /* returns pointer to hlist_node */
-static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+static void *_ima_measurements_start(struct seq_file *m, loff_t *pos,
+				     struct list_head *head)
 {
 	loff_t l = *pos;
 	struct ima_queue_entry *qe;
 
 	/* we need a lock since pos could point beyond last element */
 	rcu_read_lock();
-	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+	list_for_each_entry_rcu(qe, head, later) {
 		if (!l--) {
 			rcu_read_unlock();
 			return qe;
@@ -89,7 +90,13 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 	return NULL;
 }
 
-static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+	return _ima_measurements_start(m, pos, &ima_measurements);
+}
+
+static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t *pos,
+				    struct list_head *head)
 {
 	struct ima_queue_entry *qe = v;
 
@@ -101,7 +108,12 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 	rcu_read_unlock();
 	(*pos)++;
 
-	return (&qe->later == &ima_measurements) ? NULL : qe;
+	return (&qe->later == head) ? NULL : qe;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	return _ima_measurements_next(m, v, pos, &ima_measurements);
 }
 
 static void ima_measurements_stop(struct seq_file *m, void *v)
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 04/12] ima: Introduce per binary measurements list type binary_runtime_size value
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Make binary_runtime_size as an array, to have separate counters per binary
measurements list type. Currently, define the BINARY type for the existing
binary measurements list.

Introduce ima_update_binary_runtime_size() to facilitate updating a
binary_runtime_size value with a given binary measurement list type.

Also add the binary measurements list type parameter to
ima_get_binary_runtime_size(), to retrieve the desired value. Retrieving
the value is now done under the ima_extend_list_mutex, since there can be
concurrent updates.

No functional change (except for the mutex usage, that fixes the
concurrency issue): the BINARY array element is equivalent to the old
binary_runtime_size.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 security/integrity/ima/ima.h       |  2 +-
 security/integrity/ima/ima_kexec.c |  5 ++--
 security/integrity/ima/ima_queue.c | 40 +++++++++++++++++++++---------
 3 files changed, 32 insertions(+), 15 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 8f457f2c7b79..c00c133a140f 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -319,7 +319,7 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry);
 int ima_restore_measurement_list(loff_t bufsize, void *buf);
 int ima_measurements_show(struct seq_file *m, void *v);
 int __init ima_init_htable(void);
-unsigned long ima_get_binary_runtime_size(void);
+unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list);
 int ima_init_template(void);
 void ima_init_template_list(void);
 int __init ima_init_digests(void);
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 1a0211a12ea4..8dc9459622b3 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -42,7 +42,7 @@ void ima_measure_kexec_event(const char *event_name)
 	long len;
 	int n;
 
-	buf_size = ima_get_binary_runtime_size();
+	buf_size = ima_get_binary_runtime_size(BINARY);
 	len = atomic_long_read(&ima_num_records[BINARY]);
 
 	n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
@@ -159,7 +159,8 @@ void ima_add_kexec_buffer(struct kimage *image)
 	else
 		extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024;
 
-	binary_runtime_size = ima_get_binary_runtime_size() + extra_memory;
+	binary_runtime_size = ima_get_binary_runtime_size(BINARY) +
+			      extra_memory;
 
 	if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
 		kexec_segment_size = ULONG_MAX;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 012e725ed4fc..618694d5c082 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -27,9 +27,11 @@ static struct tpm_digest *digests;
 
 LIST_HEAD(ima_measurements);	/* list of all measurements */
 #ifdef CONFIG_IMA_KEXEC
-static unsigned long binary_runtime_size;
+static unsigned long binary_runtime_size[BINARY__LAST];
 #else
-static unsigned long binary_runtime_size = ULONG_MAX;
+static unsigned long binary_runtime_size[BINARY__LAST] = {
+	[0 ... BINARY__LAST - 1] = ULONG_MAX
+};
 #endif
 
 atomic_long_t ima_num_records[BINARY__LAST] = {
@@ -128,6 +130,20 @@ static int get_binary_runtime_size(struct ima_template_entry *entry)
 	return size;
 }
 
+static void ima_update_binary_runtime_size(struct ima_template_entry *entry,
+					   enum binary_lists binary_list)
+{
+	int size;
+
+	if (binary_runtime_size[binary_list] == ULONG_MAX)
+		return;
+
+	size = get_binary_runtime_size(entry);
+	binary_runtime_size[binary_list] =
+		(binary_runtime_size[binary_list] < ULONG_MAX - size) ?
+		binary_runtime_size[binary_list] + size : ULONG_MAX;
+}
+
 /* ima_add_template_entry helper function:
  * - Add template entry to the measurement list and hash table, for
  *   all entries except those carried across kexec.
@@ -160,13 +176,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
 		hlist_add_head_rcu(&qe->hnext, &htable[key]);
 	}
 
-	if (binary_runtime_size != ULONG_MAX) {
-		int size;
-
-		size = get_binary_runtime_size(entry);
-		binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ?
-		     binary_runtime_size + size : ULONG_MAX;
-	}
+	ima_update_binary_runtime_size(entry, BINARY);
 	return 0;
 }
 
@@ -175,12 +185,18 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
  * entire binary_runtime_measurement list, including the ima_kexec_hdr
  * structure.
  */
-unsigned long ima_get_binary_runtime_size(void)
+unsigned long ima_get_binary_runtime_size(enum binary_lists binary_list)
 {
-	if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
+	unsigned long val;
+
+	mutex_lock(&ima_extend_list_mutex);
+	val = binary_runtime_size[binary_list];
+	mutex_unlock(&ima_extend_list_mutex);
+
+	if (val >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
 		return ULONG_MAX;
 	else
-		return binary_runtime_size + sizeof(struct ima_kexec_hdr);
+		return val + sizeof(struct ima_kexec_hdr);
 }
 
 static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 03/12] ima: Introduce per binary measurements list type ima_num_records counter
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Make ima_num_records as an array, to have separate counters per binary
measurements list type. Currently, define the BINARY type for the existing
binary measurements list.

No functional change: the BINARY type is equivalent to the value without
the array.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 security/integrity/ima/ima.h       | 9 ++++++++-
 security/integrity/ima/ima_fs.c    | 2 +-
 security/integrity/ima/ima_kexec.c | 2 +-
 security/integrity/ima/ima_queue.c | 6 ++++--
 4 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0e41c2113efd..8f457f2c7b79 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -28,6 +28,13 @@ enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_BINARY_NO_FIELD_LEN,
 		     IMA_SHOW_BINARY_OLD_STRING_FMT, IMA_SHOW_ASCII };
 enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
 
+/*
+ * BINARY: current binary measurements list
+ */
+enum binary_lists {
+	BINARY, BINARY__LAST
+};
+
 /* digest size for IMA, fits SHA1 or MD5 */
 #define IMA_DIGEST_SIZE		SHA1_DIGEST_SIZE
 #define IMA_EVENT_NAME_LEN_MAX	255
@@ -326,7 +333,7 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
 extern spinlock_t ima_queue_lock;
 
 /* Total number of measurement list records since hard boot. */
-extern atomic_long_t ima_num_records;
+extern atomic_long_t ima_num_records[BINARY__LAST];
 /* Total number of violations since hard boot. */
 extern atomic_long_t ima_num_violations;
 extern struct hlist_head __rcu *ima_htable;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 523d3e81f631..fcfcf7b6eae2 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -63,7 +63,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
 {
-	return ima_show_counter(buf, count, ppos, &ima_num_records);
+	return ima_show_counter(buf, count, ppos, &ima_num_records[BINARY]);
 }
 
 static const struct file_operations ima_measurements_count_ops = {
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 77ad370dbc37..1a0211a12ea4 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -43,7 +43,7 @@ void ima_measure_kexec_event(const char *event_name)
 	int n;
 
 	buf_size = ima_get_binary_runtime_size();
-	len = atomic_long_read(&ima_num_records);
+	len = atomic_long_read(&ima_num_records[BINARY]);
 
 	n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
 		      "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index a31b75d9302b..012e725ed4fc 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -32,7 +32,9 @@ static unsigned long binary_runtime_size;
 static unsigned long binary_runtime_size = ULONG_MAX;
 #endif
 
-atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0);
+atomic_long_t ima_num_records[BINARY__LAST] = {
+	[0 ... BINARY__LAST - 1] = ATOMIC_LONG_INIT(0)
+};
 atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
 
 /* key: inode (before secure-hashing a file) */
@@ -152,7 +154,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
 	htable = rcu_dereference_protected(ima_htable,
 				lockdep_is_held(&ima_extend_list_mutex));
 
-	atomic_long_inc(&ima_num_records);
+	atomic_long_inc(&ima_num_records[BINARY]);
 	if (update_htable) {
 		key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
 		hlist_add_head_rcu(&qe->hnext, &htable[key]);
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 02/12] ima: Replace static htable queue with dynamically allocated array
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

The IMA hash table is a fixed-size array of hlist_head buckets:

    struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE];

IMA_MEASURE_HTABLE_SIZE is (1 << IMA_HASH_BITS) = 1024 buckets, each a
struct hlist_head (one pointer, 8 bytes on 64-bit). That is 8 KiB allocated
in BSS for every kernel, regardless of whether IMA is ever used, and
regardless of how many measurements are actually made.

Replace the fixed-size array with a RCU-protected pointer to a dynamically
allocated array that is initialized in ima_init_htable(), which is called
from ima_init() during early boot. ima_init_htable() calls the static
function ima_alloc_replace_htable() which, other than initializing the hash
table the first time, can also hot-swap the existing hash table with a
blank one.

The allocation in ima_alloc_replace_htable() uses kcalloc() so the buckets
are zero-initialised (equivalent to HLIST_HEAD_INIT { .first = NULL }).
Callers of ima_alloc_replace_htable() must call synchronize_rcu() and free
the returned hash table.

Finally, access the hash table with rcu_dereference() in
ima_lookup_digest_entry() (reader side) and with
rcu_dereference_protected() in ima_add_digest_entry() (writer side).

No functional change: bucket count, hash function, and all locking remain
identical.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 security/integrity/ima/ima.h       |  3 +-
 security/integrity/ima/ima_init.c  |  5 ++++
 security/integrity/ima/ima_queue.c | 48 ++++++++++++++++++++++++++----
 3 files changed, 50 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index b3ad7eac6a1e..0e41c2113efd 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -311,6 +311,7 @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
 int ima_restore_measurement_entry(struct ima_template_entry *entry);
 int ima_restore_measurement_list(loff_t bufsize, void *buf);
 int ima_measurements_show(struct seq_file *m, void *v);
+int __init ima_init_htable(void);
 unsigned long ima_get_binary_runtime_size(void);
 int ima_init_template(void);
 void ima_init_template_list(void);
@@ -328,7 +329,7 @@ extern spinlock_t ima_queue_lock;
 extern atomic_long_t ima_num_records;
 /* Total number of violations since hard boot. */
 extern atomic_long_t ima_num_violations;
-extern struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE];
+extern struct hlist_head __rcu *ima_htable;
 
 static inline unsigned int ima_hash_key(u8 *digest)
 {
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index a2f34f2d8ad7..7e0aa09a12e6 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -140,6 +140,11 @@ int __init ima_init(void)
 	rc = ima_init_digests();
 	if (rc != 0)
 		return rc;
+
+	rc = ima_init_htable();
+	if (rc != 0)
+		return rc;
+
 	rc = ima_add_boot_aggregate();	/* boot aggregate must be first entry */
 	if (rc != 0)
 		return rc;
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 6bdaefc790c3..a31b75d9302b 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -36,9 +36,7 @@ atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0);
 atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
 
 /* key: inode (before secure-hashing a file) */
-struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE] = {
-	[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
-};
+struct hlist_head __rcu *ima_htable;
 
 /* mutex protects atomicity of extending measurement list
  * and extending the TPM PCR aggregate. Since tpm_extend can take
@@ -52,17 +50,53 @@ static DEFINE_MUTEX(ima_extend_list_mutex);
  */
 static bool ima_measurements_suspended;
 
+/* Callers must call synchronize_rcu() and free the hash table. */
+static struct hlist_head *ima_alloc_replace_htable(void)
+{
+	struct hlist_head *old_htable, *new_htable;
+
+	/* Initializing to zeros is equivalent to call HLIST_HEAD_INIT. */
+	new_htable = kcalloc(IMA_MEASURE_HTABLE_SIZE, sizeof(struct hlist_head),
+			     GFP_KERNEL);
+	if (!new_htable)
+		return ERR_PTR(-ENOMEM);
+
+	old_htable = rcu_replace_pointer(ima_htable, new_htable,
+				lockdep_is_held(&ima_extend_list_mutex));
+
+	return old_htable;
+}
+
+int __init ima_init_htable(void)
+{
+	struct hlist_head *old_htable;
+
+	mutex_lock(&ima_extend_list_mutex);
+	old_htable = ima_alloc_replace_htable();
+	mutex_unlock(&ima_extend_list_mutex);
+
+	if (IS_ERR(old_htable))
+		return PTR_ERR(old_htable);
+
+	/* Synchronize_rcu() and kfree() not necessary, only for robustness. */
+	synchronize_rcu();
+	kfree(old_htable);
+	return 0;
+}
+
 /* lookup up the digest value in the hash table, and return the entry */
 static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
 						       int pcr)
 {
 	struct ima_queue_entry *qe, *ret = NULL;
+	struct hlist_head *htable;
 	unsigned int key;
 	int rc;
 
 	key = ima_hash_key(digest_value);
 	rcu_read_lock();
-	hlist_for_each_entry_rcu(qe, &ima_htable[key], hnext) {
+	htable = rcu_dereference(ima_htable);
+	hlist_for_each_entry_rcu(qe, &htable[key], hnext) {
 		rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
 			    digest_value, hash_digest_size[ima_hash_algo]);
 		if ((rc == 0) && (qe->entry->pcr == pcr)) {
@@ -102,6 +136,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
 				bool update_htable)
 {
 	struct ima_queue_entry *qe;
+	struct hlist_head *htable;
 	unsigned int key;
 
 	qe = kmalloc_obj(*qe);
@@ -114,10 +149,13 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
 	INIT_LIST_HEAD(&qe->later);
 	list_add_tail_rcu(&qe->later, &ima_measurements);
 
+	htable = rcu_dereference_protected(ima_htable,
+				lockdep_is_held(&ima_extend_list_mutex));
+
 	atomic_long_inc(&ima_num_records);
 	if (update_htable) {
 		key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
-		hlist_add_head_rcu(&qe->hnext, &ima_htable[key]);
+		hlist_add_head_rcu(&qe->hnext, &htable[key]);
 	}
 
 	if (binary_runtime_size != ULONG_MAX) {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 01/12] ima: Remove ima_h_table structure
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu
In-Reply-To: <20260602111401.1706052-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

The ima_h_table structure is a collection of IMA measurement list
metadata - number of records in the IMA measurement list, number of
integrity violations, and a hash table containing the IMA template data
hash, needed to prevent measurement list record duplication.

Removing records from the measurement list needs to be reflected in the
hash table. As a pre-req to removing records from the measurement list,
separate those counters from the hash table, remove the ima_h_table
structure, and just replace the hash table pointer.

Finally, rename ima_show_htable_value(), ima_show_htable_violations()
and ima_htable_violations_ops respectively to ima_show_counter(),
ima_show_num_violations() and ima_num_violations_ops.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 security/integrity/ima/ima.h       | 11 +++++------
 security/integrity/ima/ima_api.c   |  2 +-
 security/integrity/ima/ima_fs.c    | 20 +++++++++-----------
 security/integrity/ima/ima_kexec.c |  2 +-
 security/integrity/ima/ima_queue.c | 15 ++++++++-------
 5 files changed, 24 insertions(+), 26 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 69e9bf0b82c6..b3ad7eac6a1e 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -324,12 +324,11 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
  */
 extern spinlock_t ima_queue_lock;
 
-struct ima_h_table {
-	atomic_long_t len;	/* number of stored measurements in the list */
-	atomic_long_t violations;
-	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
-};
-extern struct ima_h_table ima_htable;
+/* Total number of measurement list records since hard boot. */
+extern atomic_long_t ima_num_records;
+/* Total number of violations since hard boot. */
+extern atomic_long_t ima_num_violations;
+extern struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE];
 
 static inline unsigned int ima_hash_key(u8 *digest)
 {
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 0916f24f005f..122d127e108d 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -146,7 +146,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 	int result;
 
 	/* can overflow, only indicator */
-	atomic_long_inc(&ima_htable.violations);
+	atomic_long_inc(&ima_num_violations);
 
 	result = ima_alloc_init_template(&event_data, &entry, NULL);
 	if (result < 0) {
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ca4931a95098..523d3e81f631 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -38,8 +38,8 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
 
 static int valid_policy = 1;
 
-static ssize_t ima_show_htable_value(char __user *buf, size_t count,
-				     loff_t *ppos, atomic_long_t *val)
+static ssize_t ima_show_counter(char __user *buf, size_t count, loff_t *ppos,
+				atomic_long_t *val)
 {
 	char tmpbuf[32];	/* greater than largest 'long' string value */
 	ssize_t len;
@@ -48,15 +48,14 @@ static ssize_t ima_show_htable_value(char __user *buf, size_t count,
 	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
 }
 
-static ssize_t ima_show_htable_violations(struct file *filp,
-					  char __user *buf,
-					  size_t count, loff_t *ppos)
+static ssize_t ima_show_num_violations(struct file *filp, char __user *buf,
+				       size_t count, loff_t *ppos)
 {
-	return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+	return ima_show_counter(buf, count, ppos, &ima_num_violations);
 }
 
-static const struct file_operations ima_htable_violations_ops = {
-	.read = ima_show_htable_violations,
+static const struct file_operations ima_num_violations_ops = {
+	.read = ima_show_num_violations,
 	.llseek = generic_file_llseek,
 };
 
@@ -64,8 +63,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
 {
-	return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
-
+	return ima_show_counter(buf, count, ppos, &ima_num_records);
 }
 
 static const struct file_operations ima_measurements_count_ops = {
@@ -545,7 +543,7 @@ int __init ima_fs_init(void)
 	}
 
 	dentry = securityfs_create_file("violations", S_IRUSR | S_IRGRP,
-				   ima_dir, NULL, &ima_htable_violations_ops);
+				   ima_dir, NULL, &ima_num_violations_ops);
 	if (IS_ERR(dentry)) {
 		ret = PTR_ERR(dentry);
 		goto out;
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 36a34c54de58..77ad370dbc37 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -43,7 +43,7 @@ void ima_measure_kexec_event(const char *event_name)
 	int n;
 
 	buf_size = ima_get_binary_runtime_size();
-	len = atomic_long_read(&ima_htable.len);
+	len = atomic_long_read(&ima_num_records);
 
 	n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
 		      "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 319522450854..6bdaefc790c3 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -32,11 +32,12 @@ static unsigned long binary_runtime_size;
 static unsigned long binary_runtime_size = ULONG_MAX;
 #endif
 
+atomic_long_t ima_num_records = ATOMIC_LONG_INIT(0);
+atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
+
 /* key: inode (before secure-hashing a file) */
-struct ima_h_table ima_htable = {
-	.len = ATOMIC_LONG_INIT(0),
-	.violations = ATOMIC_LONG_INIT(0),
-	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+struct hlist_head ima_htable[IMA_MEASURE_HTABLE_SIZE] = {
+	[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
 };
 
 /* mutex protects atomicity of extending measurement list
@@ -61,7 +62,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
 
 	key = ima_hash_key(digest_value);
 	rcu_read_lock();
-	hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
+	hlist_for_each_entry_rcu(qe, &ima_htable[key], hnext) {
 		rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
 			    digest_value, hash_digest_size[ima_hash_algo]);
 		if ((rc == 0) && (qe->entry->pcr == pcr)) {
@@ -113,10 +114,10 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
 	INIT_LIST_HEAD(&qe->later);
 	list_add_tail_rcu(&qe->later, &ima_measurements);
 
-	atomic_long_inc(&ima_htable.len);
+	atomic_long_inc(&ima_num_records);
 	if (update_htable) {
 		key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
-		hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+		hlist_add_head_rcu(&qe->hnext, &ima_htable[key]);
 	}
 
 	if (binary_runtime_size != ULONG_MAX) {
-- 
2.43.0


^ permalink raw reply related

* [PATCH v6 00/12] ima: Exporting and deleting IMA measurement records from kernel memory
From: Roberto Sassu @ 2026-06-02 11:13 UTC (permalink / raw)
  To: corbet, skhan, zohar, dmitry.kasatkin, eric.snowberg, paul,
	jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, chenste, nramas, Roberto Sassu

From: Roberto Sassu <roberto.sassu@huawei.com>

Introduction
============

The IMA measurements list is currently stored in the kernel memory.
Memory occupation grows linearly with the number of records, and can
become a problem especially in environments with reduced resources.

While there is an advantage in keeping the IMA measurements list in
kernel memory, so that it is always available for reading from the
securityfs interfaces, storing it elsewhere would make it possible to
free precious memory for other kernel usage.

The IMA measurements list needs to be retained and safely stored for new
attestation servers to validate it. Assuming the IMA measurements list
is properly saved, storing it outside the kernel does not introduce
security issues, since its integrity is anyway protected by the TPM.

Hence, the new IMA staging mechanism is introduced to export IMA
measurements to user space and delete them from kernel space.

Staging consists in atomically moving the current measurements list to a
temporary list, so that measurements can be deleted afterwards. The
staging operation locks the hot path (racing with addition of new
measurements) for a very short time, only for swapping the list
pointers. Deletion of the measurements instead is done locklessly, away
from the hot path.

There are two flavors of the staging mechanism. In the staging with
prompt, all current measurements are staged, read and deleted upon
confirmation. In the staging and deleting flavor, N measurements are
staged from the beginning of the current measurements list and
immediately deleted without confirmation.


Usage
=====

The IMA staging mechanism can be enabled from the kernel configuration
with the CONFIG_IMA_STAGING option. This option prevents inadvertently
removing the IMA measurement list on systems which do not properly save
it.

If the option is enabled, IMA duplicates the current securityfs
measurements interfaces (both binary and ASCII), by adding the _staged
file suffix. Both the original and the staging interfaces gain the write
permission for the root user and group, but require the process to have
CAP_SYS_ADMIN set.

The staging mechanism supports two flavors.

Staging with prompt
~~~~~~~~~~~~~~~~~~~

The current measurement list is moved to a temporary staging area,
allowing it to be saved to external storage, before being deleted upon
confirmation.

This staging process is achieved with the following steps.

  1.  echo A > <_staged interface>: the user requests IMA to stage the
      entire measurements list;
  2.  cat <_staged interface>: the user reads the staged measurements;
  3.  echo D > <_staged interface>: the user requests IMA to delete
      staged measurements.

Staging and deleting
~~~~~~~~~~~~~~~~~~~~

N measurements are staged to a temporary staging area, and immediately
deleted without further confirmation.

This staging process is achieved with the following steps.

  1.  cat <original interface>: the user reads the current measurements
      list and determines what the value N for staging should be;
  2.  echo N > <original interface>: the user requests IMA to delete N
      measurements from the current measurements list.


Management of Staged Measurements
=================================

Since with the staging mechanism measurement records are removed from
the kernel, the staged measurements need to be saved in a storage and
concatenated together, so that they can be presented to remote
attestation agents as if staging was never done. This task can be
accomplished by a system service.


Patch set content
=================

Patches 1-8 are preparatory patches to quickly replace the hash table,
maintain separate counters for the different measurements list types,
mediate access to the measurements list interface, and simplify the staging
patches.

Patch 9 introduces the staging with prompt flavor. Patch 10 makes it
possible to flush the hash table when deleting all the staged measurements.
Patch 11 introduces the staging and deleting flavor. Patch 12 adds the
documentation of the staging mechanism.


Changelog
=========

v5:
 - Add motivation for the ima_flush_htable= kernel option (suggested by
   Mimi)
 - New documentation title and fixes (suggested by Mimi)
 - Allow stage all command on the _staged interface instead of the original
 - Set CONFIG_IMA_STAGING default to n (suggested by Mimi)
 - Rename ima_num_entries to ima_num_records (suggested by Mimi)
 - Comment for ima_num_records and ima_num_violations (suggested by Mimi)
 - Add overflow check in ima_measure_lock()
 - Allow a writer to open for write or read/write the other staging
   interfaces
 - Ignore ppos in _ima_measurements_write()
 - Implement lockless kexec measurement lists dump by denying
   staging/delete after measurement suspend (collapse patch 12 into 9 and
   11)
 - Refuse delete based on measurement suspend instead of using
   ima_copied_flags (suggested by Mimi)
 - Add staging/deleting functions documentation

v4:
 - Add write permission to the original measurement interface, and move
   the A and N staging commands to that interface
 - Explain better the two staging flavors and highlight that the staging
   and delete only stages measurements internally
 - Rename ima_queue_staged_delete_partial() to ima_queue_delete_partial()
 - Replace ima_staged_measurements_prepended with per measurements list
   flag to avoid copying staged and active list measurements twice
 - Optimize the staging and deleting flavor by locklessly determining the
   cut position in the active list, and immediately deleting entries
   without explicit staging and splicing (suggested by Steven Chen)

v3:
 - Add Kconfig option to enable the staging mechanism (suggested by Mimi)
 - Change the meaning of BINARY_STAGED to be just the staged measurements
 - Separate the two staging flavors in two different functions:
   ima_queue_staged_delete_all() for staging with prompt,
   ima_queue_staged_delete_partial() for staging and deleting
 - Delete N entries without staging first (suggested by Mimi)
 - Avoid duplicate staged entries if there is contention between the
   measurements list interfaces and kexec

v2:
 - New patch to move measurements and violation counters outside the
   ima_h_table structure
 - New patch to quickly replace the hash table
 - Forbid partial deletion when flushing hash table (suggested by Mimi)
 - Ignore ima_flush_htable if CONFIG_IMA_DISABLE_HTABLE is enabled
 - BINARY_SIZE_* renamed to BINARY_* for better clarity
 - Removed ima_measurements_staged_exist and testing list empty instead
 - ima_queue_stage_trim() and ima_queue_delete_staged_trimmed() renamed to
   ima_queue_stage() and ima_queue_delete_staged()
 - New delete interval [1, ULONG_MAX - 1]
 - Rename ima_measure_lock to ima_measure_mutex
 - Move seq_open() and seq_release() outside the ima_measure_mutex lock
 - Drop ima_measurements_staged_read() and use seq_read() instead
 - Optimize create_securityfs_measurement_lists() changes
 - New file name format with _staged suffix at the end of the file name
 - Use _rcu list variant in ima_dump_measurement_list()
 - Remove support for direct trimming and splice the remaining entries to
   the active list (suggested by Mimi)
 - Hot swap the hash table if flushing is requested

v1:
 - Support for direct trimming without staging
 - Support unstaging on kexec (requested by Gregory Lumen)

Roberto Sassu (12):
  ima: Remove ima_h_table structure
  ima: Replace static htable queue with dynamically allocated array
  ima: Introduce per binary measurements list type ima_num_records
    counter
  ima: Introduce per binary measurements list type binary_runtime_size
    value
  ima: Introduce _ima_measurements_start() and _ima_measurements_next()
  ima: Mediate open/release method of the measurements list
  ima: Use snprintf() in create_securityfs_measurement_lists
  ima: Introduce ima_dump_measurement()
  ima: Add support for staging measurements with prompt
  ima: Add support for flushing the hash table when staging measurements
  ima: Support staging and deleting N measurements records
  doc: security: Add documentation of exporting and deleting IMA
    measurements

 .../admin-guide/kernel-parameters.txt         |   6 +
 Documentation/security/IMA-export-delete.rst  | 190 ++++++++++
 Documentation/security/index.rst              |   1 +
 MAINTAINERS                                   |   2 +
 security/integrity/ima/Kconfig                |  15 +
 security/integrity/ima/ima.h                  |  29 +-
 security/integrity/ima/ima_api.c              |   2 +-
 security/integrity/ima/ima_fs.c               | 346 ++++++++++++++++--
 security/integrity/ima/ima_init.c             |   5 +
 security/integrity/ima/ima_kexec.c            |  42 ++-
 security/integrity/ima/ima_queue.c            | 329 +++++++++++++++--
 11 files changed, 894 insertions(+), 73 deletions(-)
 create mode 100644 Documentation/security/IMA-export-delete.rst

-- 
2.43.0


^ permalink raw reply

* Re: [PATCH v4 3/3] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Sudeep Holla @ 2026-06-02  9:57 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: Yeoreum Yun, Sudeep Holla, linux-security-module, linux-kernel,
	linux-integrity, paul, zohar, roberto.sassu, noodles, jmorris,
	serge, dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <ah428rLnpoIersnQ@kernel.org>

On Tue, Jun 02, 2026 at 04:50:42AM +0300, Jarkko Sakkinen wrote:
> On Mon, Jun 01, 2026 at 09:54:08AM +0100, Sudeep Holla wrote:
> > On Mon, Jun 01, 2026 at 08:17:13AM +0100, Yeoreum Yun wrote:
> > > Hi Jarkko,
> > > 
> > > Sorry for late answer.
> > > 
> > > > On Mon, May 25, 2026 at 08:54:04AM +0100, Yeoreum Yun wrote:
> > > > > commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
> > > > > probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
> > > > > 
> > > > > However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
> > > > > initialises IMA at the late_initcall_sync level, so this change is no
> > > > > longer required.
> > > > > 
> > > > > Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> > > > > ---
> > > > >  drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
> > > > >  1 file changed, 3 insertions(+), 15 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
> > > > > index 99f1c1e5644b..025c4d4b17ca 100644
> > > > > --- a/drivers/char/tpm/tpm_crb_ffa.c
> > > > > +++ b/drivers/char/tpm/tpm_crb_ffa.c
> > > > > @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
> > > > >   */
> > > > >  int tpm_crb_ffa_init(void)
> > > > >  {
> > > > > -	int ret = 0;
> > > > > -
> > > > > -	if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
> > > > > -		ret = ffa_register(&tpm_crb_ffa_driver);
> > > > > -		if (ret) {
> > > > > -			tpm_crb_ffa = ERR_PTR(-ENODEV);
> > > > > -			return ret;
> > > > > -		}
> > > > > -	}
> > > > > -
> > > > >  	if (!tpm_crb_ffa)
> > > > > -		ret = -ENOENT;
> > > > > +		return -ENOENT;
> > > > >  
> > > > >  	if (IS_ERR_VALUE(tpm_crb_ffa))
> > > > > -		ret = -ENODEV;
> > > > > +		return -ENODEV;
> > > > >  
> > > > > -	return ret;
> > > > > +	return 0;
> > > > >  }
> > > > >  EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
> > > > >  
> > > > > @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
> > > > >  	.id_table = tpm_crb_ffa_device_id,
> > > > >  };
> > > > >  
> > > > > -#ifdef MODULE
> > > > >  module_ffa_driver(tpm_crb_ffa_driver);
> > > > > -#endif
> > > > >  
> > > > >  MODULE_AUTHOR("Arm");
> > > > >  MODULE_DESCRIPTION("TPM CRB FFA driver");
> > > > > -- 
> > > > > LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
> > > > > 
> > > > 
> > > > How we would sync up this patch? Through which tree etc.
> > > 
> > > IMHO, the IMA relevant thing would be into IMA tree,
> > > However I think this patch would be much easier to sync into Sudeep's
> > > FF-A tree where ff-a initilisation is reverted to device_initcall
> > > unless you're uncomfortable.
> > > 
> > > For this, It might be better to split this patch from this series
> > > since by above and defer probe of ff-a would make a register failure
> > > of registering tpm_crb_ffa driver which is built-in.
> > > 
> > > @Sudeep what do you think?
> > > 
> > 
> > IIRC, there is/was no dependency between these and FF-A patches that are
> > queued in terms of build. I agree there may be dependency to get all the
> > functionality but we can resort to linux-next for that. FF-A is not enabled
> > in the defconfig, so anyone working on FF-A + TPM must enable then and can
> > rely on -next IMHO.
> > 
> > That said, I have already sent PR for FF-A to SoC team and it is already
> > queued for v7.2. I don't have any other plans unless they are fixes.
> 
> Works for me.
> 

Sorry if I was not clear. I haven't included any TPM patches in this series
as part of my FF-A pull request queued for v7.2. So I was asking to route this
via your tree.

-- 
Regards,
Sudeep

^ permalink raw reply

* Re: [PATCH] KEYS: Use acquire when reading state in keyring search
From: Gui-Dong Han @ 2026-06-02  9:42 UTC (permalink / raw)
  To: Jarkko Sakkinen
  Cc: dhowells, keyrings, ebiggers, linux-security-module, linux-kernel,
	baijiaju1990
In-Reply-To: <aho3CYdv8lQd2s85@kernel.org>

On Sat, May 30, 2026 at 9:02 AM Jarkko Sakkinen <jarkko@kernel.org> wrote:
>
> On Fri, May 29, 2026 at 11:34:06AM +0800, Gui-Dong Han wrote:
> > The negative-key race fix added release/acquire ordering for key use.
> >
> > Publish payload before state; read state before payload.
> >
> > keyring_search_iterator() still uses READ_ONCE() before match callbacks.
> > An asymmetric match callback calls asymmetric_key_ids(), which reads
> > key->payload.data[asym_key_ids].
> >
> > Use key_read_state() there to complete that ordering.
>
> OK, so... I'm having a bit trouble understanding the exact concurrency
> scenario you're trying to describe despite I think I get the fix itself
> i.e. it is not pairing with mark_key_instantiated?

Yes, it is intended to pair with mark_key_instantiated().

>
> I'm a bit puzzled too why this was not done already in the original
> commit despite introducing all the primitives.

The original commit added the right primitives, but this site was easy to
miss because the payload access is hidden behind ctx->match_data.cmp().
For example, the asymmetric match callbacks call asymmetric_key_ids(),
which reads key->payload.data[asym_key_ids].

I also checked the two pre-existing issues reported by Sashiko [1].
The uninitialized-read case does not look possible from the code, but the
cached-error issue is real and was dynamically reproduced.  I can send a
separate patch for that if useful.

Thanks.

[1] https://sashiko.dev/#/patchset/20260529033406.20673-1-hanguidong02%40gmail.com

>
> >
> > Fixes: 363b02dab09b ("KEYS: Fix race between updating and finding a negative key")
> > Signed-off-by: Gui-Dong Han <hanguidong02@gmail.com>
> > ---
> > Found by auditing READ_ONCE() used for synchronization.
> > A similar fix can be found in 8df672bfe3ec.
> > ---
> >  security/keys/keyring.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/security/keys/keyring.c b/security/keys/keyring.c
> > index b39038f7dd31..243fb1636f10 100644
> > --- a/security/keys/keyring.c
> > +++ b/security/keys/keyring.c
> > @@ -576,7 +576,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data)
> >       struct keyring_search_context *ctx = iterator_data;
> >       const struct key *key = keyring_ptr_to_key(object);
> >       unsigned long kflags = READ_ONCE(key->flags);
> > -     short state = READ_ONCE(key->state);
> > +     short state = key_read_state(key);
> >
> >       kenter("{%d}", key->serial);
> >
> > --
> > 2.34.1
> >
>
> BR, Jarkko

^ permalink raw reply

* Re: [PATCH v4 0/2] Delete task_euid()
From: Alice Ryhl @ 2026-06-02  6:15 UTC (permalink / raw)
  To: Paul Moore
  Cc: Serge Hallyn, Jonathan Corbet, Greg Kroah-Hartman, Shuah Khan,
	Alex Shi, Yanteng Si, Dongliang Mu, Miguel Ojeda, Boqun Feng,
	Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
	Trevor Gross, Danilo Krummrich, Jann Horn, linux-security-module,
	linux-doc, linux-kernel, rust-for-linux
In-Reply-To: <CAHC9VhR5Ca+WyP2OiNGtL1SqHn0SwLe=M9SB8D2bxtdS52quhg@mail.gmail.com>

On Mon, Jun 01, 2026 at 07:13:37PM -0400, Paul Moore wrote:
> On Fri, May 29, 2026 at 5:33 AM Alice Ryhl <aliceryhl@google.com> wrote:
> >
> > The task_euid() method is a very weird method, and Binder was the only
> > user. As of commit 65b672152289 ("binder: use current_euid() for
> > transaction sender identity") Binder doesn't use task_euid() anymore,
> > so we can delete this method.
> 
> Given the problems from last time, it seems like it might be prudent
> to let the commit have some time to "breathe" in a proper release, I'd
> suggest merging this not for the upcoming v7.2 merge window but
> instead waiting for v7.3.

Sure, that makes sense. I'll resend after the merge window.

> > My suggestion would be to merge this through the LSM tree.
> 
> That's fine with me.  I'd also suggest updating the commit description
> in patch 1/2 to indicate that binder is no longer using task_euid();
> it currently reads like it is still being used.

I guess this occurred because when patch 1 was written, it really *was*
still being used. Perhaps we could pick up only patch 1 now since even
if we run into problems and Binder has to go back to using task_euid(),
clarifying the docs is still useful.

Alice

^ permalink raw reply

* Re: [PATCH 01/11] hornet: fix TOCTOU in signed program verification
From: Fan Wu @ 2026-06-02  4:01 UTC (permalink / raw)
  To: Paul Moore
  Cc: Fan Wu, Blaise Boscaccy, Jonathan Corbet, Shuah Khan,
	James Morris, Serge E. Hallyn, Eric Biggers, James.Bottomley,
	linux-security-module
In-Reply-To: <CAHC9VhSt1_XmKfO7SA_5qiUUpq5RWa98yACHSwh9KMMGQTJjbg@mail.gmail.com>

On Mon, Jun 1, 2026 at 8:26 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Fri, May 29, 2026 at 9:11 PM Fan Wu <wufan@kernel.org> wrote:
> > On Wed, May 27, 2026 at 8:09 PM Blaise Boscaccy
> > <bboscaccy@linux.microsoft.com> wrote:
> > >
> > > The signature verification path was vulnerable to a time-of-check vs
> > > time-of-use race at both the program load and program run hook sites:
> > > between the moment a map's contents were hashed for signature
> > > verification and the moment the program run hook re-verified them, an
> > > attacker with sufficient privileges could swap or mutate the map
> > > contents.
> > >
> > > Close the race by snapshotting the map hashes during program load,
> > > attaching them to the program, and re-verifying them from the
> > > security_bpf_prog hook against prog->aux->used_maps. Because used_maps
> > > is the same map set the verifier and runtime resolve against, there is
> > > no longer a window in which the verified set and the executed set can
> > > diverge.
> > >
> > > Since we are no longer targeting the fd_array passed in, drop the map
> > > index data entirely and check for whether or not the set of requested
> > > map hashes is a subset of prog->aux->used_maps.
> > >
> > > Reported-by: Eric Biggers <ebiggers@kernel.org>
> > > Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> > > ---
...
> > similar above, they should be removed for the header and for the ipe policy.
>
> I would prefer to see LSM_INT_VERDICT_FAULT preserved as I think it is
> good to have a verdict indicating a general error in the integrity
> verification code, but I do agree with Fan that removing
> VERDICT_UNEXPECTED is probably a good thing to do.
>
> If Fan is insistent on removal of VERDICT_FAULT from the IPE code
> that's fine, just leave it in the LSM header.
>
> Can you get a patch out for this ASAP Blaise?
>
> --
> paul-moore.com

I'm good with keeping LSM_INT_VERDICT_FAULT.

-Fan

^ permalink raw reply

* Re: [PATCH 02/11] hornet: invert map set check logic
From: Paul Moore @ 2026-06-02  3:36 UTC (permalink / raw)
  To: Fan Wu
  Cc: Blaise Boscaccy, Jonathan Corbet, Shuah Khan, James Morris,
	Serge E. Hallyn, Eric Biggers, James.Bottomley,
	linux-security-module
In-Reply-To: <CAKtyLkHtjzP3YsDr5-Gw0GRFB7Ch2h1amZzxy5taTt_+3gSzwQ@mail.gmail.com>

On Fri, May 29, 2026 at 8:57 PM Fan Wu <wufan@kernel.org> wrote:
>
> On Wed, May 27, 2026 at 8:09 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
> >
> > In a multi-map hash verification scenario, a logic bug may have
> > allowed an attacker to provide duplicate maps to satisfy the hash
> > check count. Instead, invert the logic to verify each map discretely
> >
> > Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> > ---
>
> I just realized there is no audit event if hornet_check_prog_maps()
> fails, probably should add one.

Maybe, but I think it is important to remember that not all LSMs use
audit for reporting, and Hornet is doing some new things from an LSM
perspective.  I think for right now it would be sufficient to use a
pr_notice() or a pr_notice_ratelimited() (if we are worried about
unpriv log spam) message in hornet_check_prog_maps().  Hornet can
always add proper audit support at a later date if deemed necessary.

Blaise, do you want to submit a patch to add pr_notice{_ratelimited}()
in the case of denial in hornet_check_prog_maps()?

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH 01/11] hornet: fix TOCTOU in signed program verification
From: Paul Moore @ 2026-06-02  3:25 UTC (permalink / raw)
  To: Fan Wu
  Cc: Blaise Boscaccy, Jonathan Corbet, Shuah Khan, James Morris,
	Serge E. Hallyn, Eric Biggers, James.Bottomley,
	linux-security-module
In-Reply-To: <CAKtyLkEy5QeWmozTr8v_CEFfJTK3WJq3CBr3h_T2=HMr9YfCFA@mail.gmail.com>

On Fri, May 29, 2026 at 9:11 PM Fan Wu <wufan@kernel.org> wrote:
> On Wed, May 27, 2026 at 8:09 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
> >
> > The signature verification path was vulnerable to a time-of-check vs
> > time-of-use race at both the program load and program run hook sites:
> > between the moment a map's contents were hashed for signature
> > verification and the moment the program run hook re-verified them, an
> > attacker with sufficient privileges could swap or mutate the map
> > contents.
> >
> > Close the race by snapshotting the map hashes during program load,
> > attaching them to the program, and re-verifying them from the
> > security_bpf_prog hook against prog->aux->used_maps. Because used_maps
> > is the same map set the verifier and runtime resolve against, there is
> > no longer a window in which the verified set and the executed set can
> > diverge.
> >
> > Since we are no longer targeting the fd_array passed in, drop the map
> > index data entirely and check for whether or not the set of requested
> > map hashes is a subset of prog->aux->used_maps.
> >
> > Reported-by: Eric Biggers <ebiggers@kernel.org>
> > Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> > ---
> >  Documentation/admin-guide/LSM/Hornet.rst |  39 +++-----
> >  scripts/hornet/gen_sig.c                 |  17 +---
> >  security/hornet/hornet.asn1              |   1 -
> >  security/hornet/hornet_lsm.c             | 121 +++--------------------
> >  tools/testing/selftests/hornet/Makefile  |   2 +-
> >  5 files changed, 35 insertions(+), 145 deletions(-)

A reminder to please trim your replies.

> > -static int hornet_verify_hashes(struct hornet_maps *maps,
> > -                               struct hornet_parse_context *ctx,
> > -                               struct bpf_prog *prog)
> > -{
> > -       int map_fd;
> > -       u32 i;
> > -       struct bpf_map *map;
> > -       int err = 0;
> > -       unsigned char hash[SHA256_DIGEST_SIZE];
> > -       struct hornet_prog_security_struct *security = hornet_bpf_prog_security(prog);
> > -
> > -       for (i = 0; i < ctx->hash_count; i++) {
> > -               if (ctx->skips[i])
> > -                       continue;
> > -
> > -               err = copy_from_bpfptr_offset(&map_fd, maps->fd_array,
> > -                                             ctx->indexes[i] * sizeof(map_fd),
> > -                                             sizeof(map_fd));
> > -               if (err != 0)
> > -                       return LSM_INT_VERDICT_FAULT;
>
> It seems this verdict is no longer used.
>
> > -
> > -               CLASS(fd, f)(map_fd);
> > -               if (fd_empty(f))
> > -                       return LSM_INT_VERDICT_FAULT;
> > -               if (unlikely(fd_file(f)->f_op != &bpf_map_fops))
> > -                       return LSM_INT_VERDICT_FAULT;
> > -
> > -               map = fd_file(f)->private_data;
> > -               if (!READ_ONCE(map->frozen))
> > -                       return LSM_INT_VERDICT_FAULT;
> > -
> > -               if (!map->ops->map_get_hash)
> > -                       return LSM_INT_VERDICT_FAULT;
> > -
> > -               if (map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, hash))
> > -                       return LSM_INT_VERDICT_FAULT;
> > -
> > -               err = memcmp(hash, &ctx->hashes[i * SHA256_DIGEST_SIZE],
> > -                             SHA256_DIGEST_SIZE);
> > -               if (err)
> > -                       return LSM_INT_VERDICT_UNEXPECTED;
>
> similar above, they should be removed for the header and for the ipe policy.

I would prefer to see LSM_INT_VERDICT_FAULT preserved as I think it is
good to have a verdict indicating a general error in the integrity
verification code, but I do agree with Fan that removing
VERDICT_UNEXPECTED is probably a good thing to do.

If Fan is insistent on removal of VERDICT_FAULT from the IPE code
that's fine, just leave it in the LSM header.

Can you get a patch out for this ASAP Blaise?

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH net v2 4/4] netlabel: validate CIPSO option against skb tail in netlbl_skbuff_getattr
From: Paul Moore @ 2026-06-02  3:17 UTC (permalink / raw)
  To: Qi Tang
  Cc: davem, kuba, pabeni, edumazet, netdev, fw, lyutoon, stable,
	Simon Horman, linux-security-module
In-Reply-To: <20260524041442.2432071-5-tpluszz77@gmail.com>

On Sun, May 24, 2026 at 12:15 AM Qi Tang <tpluszz77@gmail.com> wrote:
>
> netlbl_skbuff_getattr() locates the CIPSO option in the IPv4 IP header
> via cipso_v4_optptr() and hands the bare pointer to cipso_v4_getattr().
> The consumer re-reads cipso[1] (option length), cipso[6] (tag type),
> and then cipso_v4_parsetag_*() re-reads further bytes from the skb.
>
> __ip_options_compile() validates these bytes only at parse time.  An
> nftables LOCAL_IN payload write reachable from an unprivileged user
> namespace can rewrite them after parse and before the SELinux/Smack
> peer-label consume path (selinux_sock_rcv_skb_compat ->
> selinux_netlbl_sock_rcv_skb -> netlbl_skbuff_getattr).  This is the
> IPv4 analogue of the CALIPSO IPv6 trust-after-modification fixed in
> the previous patch: the tag parsers walk the option using attacker-
> controlled length bytes, producing slab-out-of-bounds reads whose
> contents feed into the MLS access decision.
>
> Validate the option fits within skb_tail_pointer(skb) before invoking
> cipso_v4_getattr().  The pre-tag-walk guard "ptr + 8 > tail" covers
> the CIPSO option header (type + length + DOI = 6 bytes) plus the
> first tag header (type + length = 2 bytes), which are the bytes
> cipso_v4_getattr() reads to dispatch on the tag.  When the bounds
> check fails the packet has been mutated after parse, so return
> -EINVAL rather than fall through to the unlabeled path.
>
> Runtime confirmation (Smack peer-label policy + nft LOCAL_IN
> mutation of tag_len): UdpInDatagrams increments to 1 and recvfrom
> returns the payload, showing netlbl_skbuff_getattr ->
> cipso_v4_getattr -> cipso_v4_parsetag_rbm -> netlbl_bitmap_walk runs
> end-to-end past the option's true bound; with this patch the
> consume path returns -EINVAL at the bounds check and the counter
> stays 0.
>
> Cc: stable@vger.kernel.org
> Reported-by: Qi Tang <tpluszz77@gmail.com>
> Reported-by: Tong Liu <lyutoon@gmail.com>
> Fixes: 04f81f0154e4 ("cipso: don't use IPCB() to locate the CIPSO IP option")
> Signed-off-by: Qi Tang <tpluszz77@gmail.com>
> ---
>  net/netlabel/netlabel_kapi.c | 17 +++++++++++++++--
>  1 file changed, 15 insertions(+), 2 deletions(-)
>
> diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c
> index d0d6220b8d59d..c2d3ea751f4e1 100644
> --- a/net/netlabel/netlabel_kapi.c
> +++ b/net/netlabel/netlabel_kapi.c
> @@ -1393,11 +1393,24 @@ int netlbl_skbuff_getattr(const struct sk_buff *skb,
>         unsigned char *ptr;
>
>         switch (family) {
> -       case AF_INET:
> +       case AF_INET: {
> +               const unsigned char *tail = skb_tail_pointer(skb);
> +               u8 opt_len, tag_len;
> +
>                 ptr = cipso_v4_optptr(skb);
> -               if (ptr && cipso_v4_getattr(ptr, secattr) == 0)
> +               if (!ptr)
> +                       break;
> +               /* CIPSO header (type+len+DOI = 6) + first tag header (type+len = 2) */
> +               if (ptr + 8 > tail)
> +                       return -EINVAL;
> +               opt_len = ptr[1];       /* total CIPSO option length */
> +               tag_len = ptr[7];       /* first tag length */
> +               if (ptr + opt_len > tail || ptr + 6 + tag_len > tail)
> +                       return -EINVAL;
> +               if (cipso_v4_getattr(ptr, secattr) == 0)
>                         return 0;

I'd strongly prefer if you moved the tag length check into
cipso_v4_getattr().  As you've already validated the CIPSO option
length field it should be a fairly easy check, no need to test it
against the skb's tail pointer, just ensure the tag length doesn't go
past the end of the CIPSO option.

>                 break;
> +       }
>  #if IS_ENABLED(CONFIG_IPV6)
>         case AF_INET6: {
>                 const unsigned char *tail = skb_tail_pointer(skb);
> --
> 2.47.3

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH net v2] netlabel: validate unlabeled mask attribute length
From: Paul Moore @ 2026-06-02  3:08 UTC (permalink / raw)
  To: Jakub Kicinski
  Cc: Chenguang Zhao, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, netdev, linux-security-module
In-Reply-To: <20260601193805.011c7f15@kernel.org>

On Mon, Jun 1, 2026 at 10:38 PM Jakub Kicinski <kuba@kernel.org> wrote:
> On Thu, 28 May 2026 09:59:13 +0800 Chenguang Zhao wrote:
> > netlbl_unlabel_addrinfo_get() checked the address length
> > but allowed shorter mask attributes to pass through to
> > fixed-size address reads.
> >
> > netlbl_unlabel_addrinfo_get() only rejected a mask
> > length mismatch when the address attribute length
> > was also invalid.  A crafted Generic Netlink request
> > could therefore provide a valid IPv4/IPv6 address
> > attribute with a shorter mask attribute.
> >
> > NLA_BINARY policy lengths are maximum lengths,
> > not exact lengths, so the short mask can pass
> > policy validation.  The mask is later read as
> > a full struct in_addr or struct in6_addr.
> > Require both address and mask attributes to
> > have the exact expected size.
>
> Sorry, didn't look at this until Paul responded.
>
> NLA_BINARY does _default_ to maximum lengths.
> But you can use NLA_POLICY_EXACT_LEN() to have the policy do the right
> thing. Using the policy is preferred - less code, and policy
> introspection informs user space about the expectations.

Thanks, I didn't know NLA_POLICY_EXACT_LEN() existed, and yes, I
agree, that would be a much better way to solve this problem.

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH net v2 3/4] netlabel: validate CALIPSO option against skb tail in netlbl_skbuff_getattr
From: Paul Moore @ 2026-06-02  3:02 UTC (permalink / raw)
  To: Qi Tang
  Cc: davem, kuba, pabeni, edumazet, netdev, fw, lyutoon, stable,
	Simon Horman, Huw Davies, linux-security-module
In-Reply-To: <20260524041442.2432071-4-tpluszz77@gmail.com>

On Sun, May 24, 2026 at 12:15 AM Qi Tang <tpluszz77@gmail.com> wrote:
>
> netlbl_skbuff_getattr() locates the CALIPSO option in the IPv6 HBH
> header via calipso_optptr() and hands the bare pointer to
> calipso_getattr() -> calipso_opt_getattr().  The consumer re-reads
> calipso[1] (option data length) and calipso[6] (cat_len/4) and walks
> calipso + 10 for cat_len bytes via netlbl_bitmap_walk().
>
> ipv6_hop_calipso() validates these bytes only at parse time inside
> ipv6_parse_hopopts().  An nftables PRE_ROUTING payload write reachable
> from an unprivileged user namespace can rewrite both bytes between
> parse and the SELinux peer-label consume path
> (selinux_sock_rcv_skb_compat -> selinux_netlbl_sock_rcv_skb ->
> netlbl_skbuff_getattr).  The self-consistency check
> (cat_len + 8 > len) inside calipso_opt_getattr() is defeated by
> mutating both bytes consistently, allowing a ~232-byte
> slab-out-of-bounds read from calipso + 10 whose set bits become MLS
> categories driving the access decision.
>
> netlbl_skbuff_getattr() has the skb; gate the consume on the option
> fitting within skb_tail_pointer().  The IPv6 option layout is
> type(1) + length(1) + length bytes of data, so requiring
> ptr + 2 + ptr[1] <= skb_tail covers the option and its embedded
> bitmap.  When the bounds check fails the packet has been mutated
> after parse, so return -EINVAL rather than fall through to the
> unlabeled path.
>
> Runtime confirmation (SELinux compat path with selinux=1 enforcing=0
> and a CALIPSO DOI added via netlabelctl): Udp6InDatagrams increments
> to 1 with the mutated cat_len, showing
> selinux_socket_sock_rcv_skb -> netlbl_skbuff_getattr ->
> calipso_opt_getattr -> netlbl_bitmap_walk runs end-to-end past the
> option's true bound; with this patch the consume path returns
> -EINVAL at the bounds check and the counter stays 0.
>
> Cc: stable@vger.kernel.org
> Reported-by: Qi Tang <tpluszz77@gmail.com>
> Reported-by: Tong Liu <lyutoon@gmail.com>
> Fixes: 2917f57b6bc1 ("calipso: Allow the lsm to label the skbuff directly.")
> Signed-off-by: Qi Tang <tpluszz77@gmail.com>
> ---
>  net/netlabel/netlabel_kapi.c | 15 +++++++++++++--
>  1 file changed, 13 insertions(+), 2 deletions(-)

Thanks for the fix.

Acked-by: Paul Moore <paul@paul-moore.com>

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH net v2] netlabel: validate unlabeled mask attribute length
From: Jakub Kicinski @ 2026-06-02  2:38 UTC (permalink / raw)
  To: Chenguang Zhao
  Cc: Paul Moore, David S. Miller, Eric Dumazet, Paolo Abeni,
	Simon Horman, netdev, linux-security-module
In-Reply-To: <20260528015913.190970-1-zhaochenguang@kylinos.cn>

On Thu, 28 May 2026 09:59:13 +0800 Chenguang Zhao wrote:
> netlbl_unlabel_addrinfo_get() checked the address length
> but allowed shorter mask attributes to pass through to
> fixed-size address reads.
> 
> netlbl_unlabel_addrinfo_get() only rejected a mask
> length mismatch when the address attribute length
> was also invalid.  A crafted Generic Netlink request
> could therefore provide a valid IPv4/IPv6 address
> attribute with a shorter mask attribute.
> 
> NLA_BINARY policy lengths are maximum lengths,
> not exact lengths, so the short mask can pass
> policy validation.  The mask is later read as
> a full struct in_addr or struct in6_addr.
> Require both address and mask attributes to
> have the exact expected size.

Sorry, didn't look at this until Paul responded.

NLA_BINARY does _default_ to maximum lengths.
But you can use NLA_POLICY_EXACT_LEN() to have the policy do the right
thing. Using the policy is preferred - less code, and policy
introspection informs user space about the expectations.
-- 
pw-bot: cr

^ permalink raw reply

* Re: [PATCH v5 4/4] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Jarkko Sakkinen @ 2026-06-02  2:29 UTC (permalink / raw)
  To: Yeoreum Yun
  Cc: linux-security-module, linux-kernel, linux-integrity, paul, zohar,
	roberto.sassu, noodles, sudeep.holla, jmorris, serge,
	dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <20260601142749.3379697-5-yeoreum.yun@arm.com>

On Mon, Jun 01, 2026 at 03:27:49PM +0100, Yeoreum Yun wrote:
> commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
> probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
> 
> However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
> initialises IMA at the late_initcall_sync level, so this change is no
> longer required.
> 
> Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>

> ---
>  drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
>  1 file changed, 3 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
> index 99f1c1e5644b..025c4d4b17ca 100644
> --- a/drivers/char/tpm/tpm_crb_ffa.c
> +++ b/drivers/char/tpm/tpm_crb_ffa.c
> @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
>   */
>  int tpm_crb_ffa_init(void)
>  {
> -	int ret = 0;
> -
> -	if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
> -		ret = ffa_register(&tpm_crb_ffa_driver);
> -		if (ret) {
> -			tpm_crb_ffa = ERR_PTR(-ENODEV);
> -			return ret;
> -		}
> -	}
> -
>  	if (!tpm_crb_ffa)
> -		ret = -ENOENT;
> +		return -ENOENT;
>  
>  	if (IS_ERR_VALUE(tpm_crb_ffa))
> -		ret = -ENODEV;
> +		return -ENODEV;
>  
> -	return ret;
> +	return 0;
>  }
>  EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
>  
> @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
>  	.id_table = tpm_crb_ffa_device_id,
>  };
>  
> -#ifdef MODULE
>  module_ffa_driver(tpm_crb_ffa_driver);
> -#endif
>  
>  MODULE_AUTHOR("Arm");
>  MODULE_DESCRIPTION("TPM CRB FFA driver");
> -- 
> LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
> 

BR, Jarkko

^ permalink raw reply

* Re: [PATCH net v2] netlabel: validate unlabeled mask attribute length
From: Paul Moore @ 2026-06-02  2:25 UTC (permalink / raw)
  To: Chenguang Zhao
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, netdev, linux-security-module
In-Reply-To: <20260528015913.190970-1-zhaochenguang@kylinos.cn>

On Wed, May 27, 2026 at 9:59 PM Chenguang Zhao <zhaochenguang@kylinos.cn> wrote:
>
> netlbl_unlabel_addrinfo_get() checked the address length
> but allowed shorter mask attributes to pass through to
> fixed-size address reads.
>
> netlbl_unlabel_addrinfo_get() only rejected a mask
> length mismatch when the address attribute length
> was also invalid.  A crafted Generic Netlink request
> could therefore provide a valid IPv4/IPv6 address
> attribute with a shorter mask attribute.
>
> NLA_BINARY policy lengths are maximum lengths,
> not exact lengths, so the short mask can pass
> policy validation.  The mask is later read as
> a full struct in_addr or struct in6_addr.
> Require both address and mask attributes to
> have the exact expected size.
>
> Fixes: 8cc44579d1bd ("NetLabel: Introduce static network labels for unlabeled connections")
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> v2:
>  - Adjust commit message
>  - Add Fixes and 'net' subject prefix.
> v1:
>  https://lore.kernel.org/all/20260522054521.1169755-1-zhaochenguang@kylinos.cn/
> ---
>  net/netlabel/netlabel_unlabeled.c | 10 ++++++----
>  1 file changed, 6 insertions(+), 4 deletions(-)

Looks good to me, thanks!

Acked-by: Paul Moore <paul@paul-moore.com>

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH RESEND 1/1] yama: clean-up ptrace relations upon activating YAMA_SCOPE_NO_ATTACH
From: Paul Moore @ 2026-06-02  1:59 UTC (permalink / raw)
  To: Ethan Ferguson, kees; +Cc: jmorris, serge, linux-security-module, linux-kernel
In-Reply-To: <20260526153542.105483-2-ethan.ferguson@zetier.com>

On Tue, May 26, 2026 at 11:35 AM Ethan Ferguson
<ethan.ferguson@zetier.com> wrote:
>
> Clean up ptracer_relations upon YAMA_SCOPE_NO_ATTACH, and prevent
> further modification by processes.
>
> Signed-off-by: Ethan Ferguson <ethan.ferguson@zetier.com>
>
> ---
>  security/yama/yama_lsm.c | 23 ++++++++++++++++++-----
>  1 file changed, 18 insertions(+), 5 deletions(-)

Kees, I've got no comment on the patch itself, just wanted to make
sure you saw the resend.

> diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
> index cef3776cf3b2..3b7c5384e6bc 100644
> --- a/security/yama/yama_lsm.c
> +++ b/security/yama/yama_lsm.c
> @@ -26,6 +26,7 @@
>  #define YAMA_SCOPE_NO_ATTACH   3
>
>  static int ptrace_scope = YAMA_SCOPE_RELATIONAL;
> +static int max_scope = YAMA_SCOPE_NO_ATTACH;
>
>  /* describe a ptrace relationship for potential exception */
>  struct ptrace_relation {
> @@ -119,7 +120,7 @@ static void yama_relation_cleanup(struct work_struct *work)
>         spin_lock(&ptracer_relations_lock);
>         rcu_read_lock();
>         list_for_each_entry_rcu(relation, &ptracer_relations, node) {
> -               if (relation->invalid) {
> +               if (relation->invalid || ptrace_scope == max_scope) {
>                         list_del_rcu(&relation->node);
>                         kfree_rcu(relation, rcu);
>                 }
> @@ -204,7 +205,8 @@ static void yama_ptracer_del(struct task_struct *tracer,
>   */
>  static void yama_task_free(struct task_struct *task)
>  {
> -       yama_ptracer_del(task, task);
> +       if (ptrace_scope <= max_scope)
> +               yama_ptracer_del(task, task);
>  }
>
>  /**
> @@ -224,6 +226,9 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
>         int rc = -ENOSYS;
>         struct task_struct *myself;
>
> +       if (ptrace_scope == max_scope)
> +               return -EPERM;
> +
>         switch (option) {
>         case PR_SET_PTRACER:
>                 /* Since a thread can call prctl(), find the group leader
> @@ -432,6 +437,7 @@ static struct security_hook_list yama_hooks[] __ro_after_init = {
>  static int yama_dointvec_minmax(const struct ctl_table *table, int write,
>                                 void *buffer, size_t *lenp, loff_t *ppos)
>  {
> +       int ret;
>         struct ctl_table table_copy;
>
>         if (write && !capable(CAP_SYS_PTRACE))
> @@ -442,10 +448,17 @@ static int yama_dointvec_minmax(const struct ctl_table *table, int write,
>         if (*(int *)table_copy.data == *(int *)table_copy.extra2)
>                 table_copy.extra1 = table_copy.extra2;
>
> -       return proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
> -}
> +       ret = proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos);
> +       if (ret < 0)
> +               return ret;
>
> -static int max_scope = YAMA_SCOPE_NO_ATTACH;
> +       /* If max_scope was just activated in this call */
> +       if (*(int *)table_copy.data == *(int *)table_copy.extra2 &&
> +           table_copy.extra1 != table_copy.extra2)
> +               schedule_work(&yama_relation_work);
> +
> +       return 0;
> +}
>
>  static const struct ctl_table yama_sysctl_table[] = {
>         {
> --
> 2.43.0

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH v4 3/3] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Jarkko Sakkinen @ 2026-06-02  1:55 UTC (permalink / raw)
  To: Yeoreum Yun
  Cc: Sudeep Holla, linux-security-module, linux-kernel,
	linux-integrity, paul, zohar, roberto.sassu, noodles, jmorris,
	serge, dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <ah1YjJJmrkecklko@e129823.arm.com>

On Mon, Jun 01, 2026 at 11:01:48AM +0100, Yeoreum Yun wrote:
> > On Mon, Jun 01, 2026 at 08:17:13AM +0100, Yeoreum Yun wrote:
> > > Hi Jarkko,
> > > 
> > > Sorry for late answer.
> > > 
> > > > On Mon, May 25, 2026 at 08:54:04AM +0100, Yeoreum Yun wrote:
> > > > > commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
> > > > > probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
> > > > > 
> > > > > However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
> > > > > initialises IMA at the late_initcall_sync level, so this change is no
> > > > > longer required.
> > > > > 
> > > > > Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> > > > > ---
> > > > >  drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
> > > > >  1 file changed, 3 insertions(+), 15 deletions(-)
> > > > > 
> > > > > diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
> > > > > index 99f1c1e5644b..025c4d4b17ca 100644
> > > > > --- a/drivers/char/tpm/tpm_crb_ffa.c
> > > > > +++ b/drivers/char/tpm/tpm_crb_ffa.c
> > > > > @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
> > > > >   */
> > > > >  int tpm_crb_ffa_init(void)
> > > > >  {
> > > > > -	int ret = 0;
> > > > > -
> > > > > -	if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
> > > > > -		ret = ffa_register(&tpm_crb_ffa_driver);
> > > > > -		if (ret) {
> > > > > -			tpm_crb_ffa = ERR_PTR(-ENODEV);
> > > > > -			return ret;
> > > > > -		}
> > > > > -	}
> > > > > -
> > > > >  	if (!tpm_crb_ffa)
> > > > > -		ret = -ENOENT;
> > > > > +		return -ENOENT;
> > > > >  
> > > > >  	if (IS_ERR_VALUE(tpm_crb_ffa))
> > > > > -		ret = -ENODEV;
> > > > > +		return -ENODEV;
> > > > >  
> > > > > -	return ret;
> > > > > +	return 0;
> > > > >  }
> > > > >  EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
> > > > >  
> > > > > @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
> > > > >  	.id_table = tpm_crb_ffa_device_id,
> > > > >  };
> > > > >  
> > > > > -#ifdef MODULE
> > > > >  module_ffa_driver(tpm_crb_ffa_driver);
> > > > > -#endif
> > > > >  
> > > > >  MODULE_AUTHOR("Arm");
> > > > >  MODULE_DESCRIPTION("TPM CRB FFA driver");
> > > > > -- 
> > > > > LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
> > > > > 
> > > > 
> > > > How we would sync up this patch? Through which tree etc.
> > > 
> > > IMHO, the IMA relevant thing would be into IMA tree,
> > > However I think this patch would be much easier to sync into Sudeep's
> > > FF-A tree where ff-a initilisation is reverted to device_initcall
> > > unless you're uncomfortable.
> > > 
> > > For this, It might be better to split this patch from this series
> > > since by above and defer probe of ff-a would make a register failure
> > > of registering tpm_crb_ffa driver which is built-in.
> > > 
> > > @Sudeep what do you think?
> > > 
> > 
> > IIRC, there is/was no dependency between these and FF-A patches that are
> > queued in terms of build. I agree there may be dependency to get all the
> > functionality but we can resort to linux-next for that. FF-A is not enabled
> > in the defconfig, so anyone working on FF-A + TPM must enable then and can
> > rely on -next IMHO.
> > 
> > That said, I have already sent PR for FF-A to SoC team and it is already
> > queued for v7.2. I don't have any other plans unless they are fixes.
> 
> Thanks. Then I think it's enough to merge this patch to TPM tree
> when this patchset is approved once.

Oops, sorry I've forgot to tag this although I've mentally accepted
this (which does not unfortunately help move things forward):

It's probably better just put SOB to the tail, or least messiest
I think:

Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>

> 
> -- 
> Sincerely,
> Yeoreum Yun

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v4 3/3] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Jarkko Sakkinen @ 2026-06-02  1:50 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Yeoreum Yun, linux-security-module, linux-kernel, linux-integrity,
	paul, zohar, roberto.sassu, noodles, jmorris, serge,
	dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <20260601-shiny-steel-jellyfish-b38b6e@sudeepholla>

On Mon, Jun 01, 2026 at 09:54:08AM +0100, Sudeep Holla wrote:
> On Mon, Jun 01, 2026 at 08:17:13AM +0100, Yeoreum Yun wrote:
> > Hi Jarkko,
> > 
> > Sorry for late answer.
> > 
> > > On Mon, May 25, 2026 at 08:54:04AM +0100, Yeoreum Yun wrote:
> > > > commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
> > > > probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
> > > > 
> > > > However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
> > > > initialises IMA at the late_initcall_sync level, so this change is no
> > > > longer required.
> > > > 
> > > > Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> > > > ---
> > > >  drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
> > > >  1 file changed, 3 insertions(+), 15 deletions(-)
> > > > 
> > > > diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
> > > > index 99f1c1e5644b..025c4d4b17ca 100644
> > > > --- a/drivers/char/tpm/tpm_crb_ffa.c
> > > > +++ b/drivers/char/tpm/tpm_crb_ffa.c
> > > > @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
> > > >   */
> > > >  int tpm_crb_ffa_init(void)
> > > >  {
> > > > -	int ret = 0;
> > > > -
> > > > -	if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
> > > > -		ret = ffa_register(&tpm_crb_ffa_driver);
> > > > -		if (ret) {
> > > > -			tpm_crb_ffa = ERR_PTR(-ENODEV);
> > > > -			return ret;
> > > > -		}
> > > > -	}
> > > > -
> > > >  	if (!tpm_crb_ffa)
> > > > -		ret = -ENOENT;
> > > > +		return -ENOENT;
> > > >  
> > > >  	if (IS_ERR_VALUE(tpm_crb_ffa))
> > > > -		ret = -ENODEV;
> > > > +		return -ENODEV;
> > > >  
> > > > -	return ret;
> > > > +	return 0;
> > > >  }
> > > >  EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
> > > >  
> > > > @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
> > > >  	.id_table = tpm_crb_ffa_device_id,
> > > >  };
> > > >  
> > > > -#ifdef MODULE
> > > >  module_ffa_driver(tpm_crb_ffa_driver);
> > > > -#endif
> > > >  
> > > >  MODULE_AUTHOR("Arm");
> > > >  MODULE_DESCRIPTION("TPM CRB FFA driver");
> > > > -- 
> > > > LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
> > > > 
> > > 
> > > How we would sync up this patch? Through which tree etc.
> > 
> > IMHO, the IMA relevant thing would be into IMA tree,
> > However I think this patch would be much easier to sync into Sudeep's
> > FF-A tree where ff-a initilisation is reverted to device_initcall
> > unless you're uncomfortable.
> > 
> > For this, It might be better to split this patch from this series
> > since by above and defer probe of ff-a would make a register failure
> > of registering tpm_crb_ffa driver which is built-in.
> > 
> > @Sudeep what do you think?
> > 
> 
> IIRC, there is/was no dependency between these and FF-A patches that are
> queued in terms of build. I agree there may be dependency to get all the
> functionality but we can resort to linux-next for that. FF-A is not enabled
> in the defconfig, so anyone working on FF-A + TPM must enable then and can
> rely on -next IMHO.
> 
> That said, I have already sent PR for FF-A to SoC team and it is already
> queued for v7.2. I don't have any other plans unless they are fixes.

Works for me.

> 
> -- 
> Regards,
> Sudeep

BR, Jarkko

^ permalink raw reply

* Re: [PATCH v4 3/3] tpm: tpm_crb_ffa: revert defered_probed when tpm_crb_ffa is built-in
From: Jarkko Sakkinen @ 2026-06-02  1:45 UTC (permalink / raw)
  To: Yeoreum Yun
  Cc: linux-security-module, linux-kernel, linux-integrity, paul, zohar,
	roberto.sassu, noodles, sudeep.holla, jmorris, serge,
	dmitry.kasatkin, eric.snowberg, jgg
In-Reply-To: <ah0x+YDypYFzpFqt@e129823.arm.com>

On Mon, Jun 01, 2026 at 08:17:13AM +0100, Yeoreum Yun wrote:
> Hi Jarkko,
> 
> Sorry for late answer.

it's all good, there's been a bug storm so I'm glad that you have not been
around ;-)

BR, Jarkko

> 
> > On Mon, May 25, 2026 at 08:54:04AM +0100, Yeoreum Yun wrote:
> > > commit 746d9e9f62a6 ("tpm: tpm_crb_ffa: try to probe tpm_crb_ffa when it's build_in")
> > > probe tpm_crb_ffa forcefully when it's built-in to integrate with IMA.
> > > 
> > > However, IMA now provides the IMA_INIT_LATE_SYNC build option, which
> > > initialises IMA at the late_initcall_sync level, so this change is no
> > > longer required.
> > > 
> > > Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
> > > ---
> > >  drivers/char/tpm/tpm_crb_ffa.c | 18 +++---------------
> > >  1 file changed, 3 insertions(+), 15 deletions(-)
> > > 
> > > diff --git a/drivers/char/tpm/tpm_crb_ffa.c b/drivers/char/tpm/tpm_crb_ffa.c
> > > index 99f1c1e5644b..025c4d4b17ca 100644
> > > --- a/drivers/char/tpm/tpm_crb_ffa.c
> > > +++ b/drivers/char/tpm/tpm_crb_ffa.c
> > > @@ -177,23 +177,13 @@ static int tpm_crb_ffa_to_linux_errno(int errno)
> > >   */
> > >  int tpm_crb_ffa_init(void)
> > >  {
> > > -	int ret = 0;
> > > -
> > > -	if (!IS_MODULE(CONFIG_TCG_ARM_CRB_FFA)) {
> > > -		ret = ffa_register(&tpm_crb_ffa_driver);
> > > -		if (ret) {
> > > -			tpm_crb_ffa = ERR_PTR(-ENODEV);
> > > -			return ret;
> > > -		}
> > > -	}
> > > -
> > >  	if (!tpm_crb_ffa)
> > > -		ret = -ENOENT;
> > > +		return -ENOENT;
> > >  
> > >  	if (IS_ERR_VALUE(tpm_crb_ffa))
> > > -		ret = -ENODEV;
> > > +		return -ENODEV;
> > >  
> > > -	return ret;
> > > +	return 0;
> > >  }
> > >  EXPORT_SYMBOL_GPL(tpm_crb_ffa_init);
> > >  
> > > @@ -405,9 +395,7 @@ static struct ffa_driver tpm_crb_ffa_driver = {
> > >  	.id_table = tpm_crb_ffa_device_id,
> > >  };
> > >  
> > > -#ifdef MODULE
> > >  module_ffa_driver(tpm_crb_ffa_driver);
> > > -#endif
> > >  
> > >  MODULE_AUTHOR("Arm");
> > >  MODULE_DESCRIPTION("TPM CRB FFA driver");
> > > -- 
> > > LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}
> > > 
> > 
> > How we would sync up this patch? Through which tree etc.
> 
> IMHO, the IMA relevant thing would be into IMA tree,
> However I think this patch would be much easier to sync into Sudeep's
> FF-A tree where ff-a initilisation is reverted to device_initcall
> unless you're uncomfortable.
> 
> For this, It might be better to split this patch from this series
> since by above and defer probe of ff-a would make a register failure
> of registering tpm_crb_ffa driver which is built-in.
> 
> @Sudeep what do you think?
> 
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git/commit/?h=for-next/ffa/updates&id=cc7e8f21b9f0c229d68cf19a837cba82b5ac2d87 [0]
> Link: https://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux.git/commit/?h=for-next/ffa/updates&id=e659fc8e537c7a21d5d693d6f30d8852f2fa8d91 [1]
> 
> -- 
> Sincerely,
> Yeoreum Yun

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox