]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
ima: Add support for flushing the hash table when staging measurements
authorRoberto Sassu <roberto.sassu@huawei.com>
Fri, 5 Jun 2026 17:22:34 +0000 (19:22 +0200)
committerMimi Zohar <zohar@linux.ibm.com>
Mon, 8 Jun 2026 15:43:30 +0000 (11:43 -0400)
During staging and delete, measurements are not completely deallocated.
Their entry digest portion is kept and is still reachable with the hash
table to detect duplicate records. If the number of records is significant,
this reduces the memory saving benefit of staging.

Some users might be interested in achieving the best memory saving (the
measurements are completely deallocated) at the cost of having duplicate
records across the staged measurement lists. Duplicate records are still
avoided within the current measurement list.

Introduce the new kernel option ima_flush_htable to decide whether or not
the digests of staged measurement records are flushed from the hash table,
when they are deleted, to achieve the maximum memory saving.

When the option is enabled, replace the old hash table with a new one,
by calling ima_alloc_replace_htable(), and completely delete the
measurements records.

Note: This code derives from the Alt-IMA Huawei project, whose license is
      GPL-2.0 OR MIT.

Link: https://github.com/linux-integrity/linux/issues/1
Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
Signed-off-by: Mimi Zohar <zohar@linux.ibm.com>
Documentation/admin-guide/kernel-parameters.txt
security/integrity/ima/ima_queue.c

index 4d0f545fb3ec5a1750d9112a851deb8fd976d32d..aad318803f825b6e8303f3251fbeb481383c0006 100644 (file)
@@ -2343,6 +2343,12 @@ Kernel parameters
                        Use the canonical format for the binary runtime
                        measurements, instead of host native format.
 
+       ima_flush_htable  [IMA]
+                       Flush the IMA hash table when deleting all the
+                       staged measurement records, to achieve maximum
+                       memory saving at the cost of having duplicate
+                       records across the staged measurement lists.
+
        ima_hash=       [IMA]
                        Format: { md5 | sha1 | rmd160 | sha256 | sha384
                                   | sha512 | ... }
index cdc21e1b929bb082fc1cdfd85a40b678a91ac10d..df1e81ea7a366bebc3df5e95ebfe392f37513d40 100644 (file)
 
 #define AUDIT_CAUSE_LEN_MAX 32
 
+static bool ima_flush_htable;
+
+static int __init ima_flush_htable_setup(char *str)
+{
+       if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
+               pr_warn("Hash table not enabled, ignoring request to flush\n");
+               return 1;
+       }
+
+       ima_flush_htable = true;
+       return 1;
+}
+__setup("ima_flush_htable", ima_flush_htable_setup);
+
 /* pre-allocated array of tpm_digest structures to extend a PCR */
 static struct tpm_digest *digests;
 
@@ -332,7 +346,7 @@ out_unlock:
        return ret;
 }
 
-static void ima_queue_delete(struct list_head *head);
+static void ima_queue_delete(struct list_head *head, bool flush_htable);
 
 /**
  * ima_queue_staged_delete_all - Delete staged measurements
@@ -350,6 +364,7 @@ static void ima_queue_delete(struct list_head *head);
  */
 int ima_queue_staged_delete_all(void)
 {
+       struct hlist_head *old_queue = NULL;
        LIST_HEAD(ima_measurements_trim);
 
        mutex_lock(&ima_extend_list_mutex);
@@ -371,21 +386,35 @@ int ima_queue_staged_delete_all(void)
        if (IS_ENABLED(CONFIG_IMA_KEXEC))
                binary_runtime_size[BINARY_STAGED] = 0;
 
+       if (ima_flush_htable) {
+               old_queue = ima_alloc_replace_htable();
+               if (IS_ERR(old_queue)) {
+                       mutex_unlock(&ima_extend_list_mutex);
+                       return PTR_ERR(old_queue);
+               }
+       }
+
        mutex_unlock(&ima_extend_list_mutex);
 
-       ima_queue_delete(&ima_measurements_trim);
+       if (ima_flush_htable) {
+               synchronize_rcu();
+               kfree(old_queue);
+       }
+
+       ima_queue_delete(&ima_measurements_trim, ima_flush_htable);
        return 0;
 }
 
 /**
  * ima_queue_delete - Delete measurements
  * @head: List head measurements are deleted from
+ * @flush_htable: Whether or not the hash table is being flushed
  *
  * Delete the measurements from the passed list head completely if the
- * hash table is not enabled, or partially (only the template data), if the
- * hash table is used.
+ * hash table is not enabled or is being flushed, or partially (only the
+ * template data), if the hash table is used.
  */
-static void ima_queue_delete(struct list_head *head)
+static void ima_queue_delete(struct list_head *head, bool flush_htable)
 {
        struct ima_queue_entry *qe, *qe_tmp;
        unsigned int i;
@@ -407,7 +436,7 @@ static void ima_queue_delete(struct list_head *head)
                list_del(&qe->later);
 
                /* No leak if condition is false, referenced by ima_htable. */
-               if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
+               if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) || flush_htable) {
                        kfree(qe->entry->digests);
                        kfree(qe->entry);
                        kfree(qe);