Hi Mimi,
> On Wed, 2026-05-06 at 06:54 +0100, Yeoreum Yun wrote:
> > Hi Mimi,
> >
> > > On Sun, 2026-05-03 at 07:36 -0400, Mimi Zohar wrote:
> > > > On Fri, 2026-05-01 at 12:52 -0400, David Safford wrote:
> > > > > On Thu, Apr 30, 2026 at 5:43 PM Mimi Zohar <[email protected]>
> > > > > wrote:
> > > > > >
> > > > > > On Thu, 2026-04-30 at 10:48 +0100, Yeoreum Yun wrote:
> > > > > > > With above change I confirmed there is no meaurement log
> > > > > > > between boot_aggregate and boot_aggregate_late except
> > > > > > > "kernel_version"
> > > > > > > But this is ignorable since this UTS measurement is done in
> > > > > > > "ima_init_core() (old: ima_init())" and it is part of ima
> > > > > > > initialisation.
> > > > > > >
> > > > > > > 1. ima_policy=tcb
> > > > > > >
> > > > > > > # cat /sys/kernel/security/ima/ascii_runtime_measurements
> > > > > > > 10 0adefe762c149c7cec19da62f0da1297fcfbffff ima-ng
> > > > > > > sha256:0000000000000000000000000000000000000000000000000000000000000000
> > > > > > > boot_aggregate
> > > > > > > 10 4e5d73ebadfd8f850cb93ce4de755ba148a9a7d5 ima-ng
> > > > > > > sha256:0000000000000000000000000000000000000000000000000000000000000000
> > > > > > > boot_aggregate_late
> > > > > > > 10 7c23cc970eceec906f7a41bc2fbde770d7092209 ima-ng
> > > > > > > sha256:72ade6ae3d35cfe5ede7a77b1c0ed1d1782a899445fdcb219c0e994a084a70d5
> > > > > > > /bin/busybox
> > > > > snip
> > > > > > >
> > > > > > > 2. ima_policy=critical_data
> > > > > > >
> > > > > > > # cat /sys/kernel/security/ima/ascii_runtime_measurements
> > > > > > > 10 0adefe762c149c7cec19da62f0da1297fcfbffff ima-ng
> > > > > > > sha256:0000000000000000000000000000000000000000000000000000000000000000
> > > > > > > boot_aggregate
> > > > > > > 10 49ab61dd97ea2f759edcb6c6a3387ac67f0aa576 ima-buf
> > > > > > > sha256:0c907aab3261194f16b0c2a422a82f145bc9b9ecb8fdb633fa43e3e5379f0af2
> > > > > > > kernel_version 372e312e302d7263312b // Ignorable since it's
> > > > > > > generated by ima_init(_core)().
> > > > > > > 10 4e5d73ebadfd8f850cb93ce4de755ba148a9a7d5 ima-ng
> > > > > > > sha256:0000000000000000000000000000000000000000000000000000000000000000
> > > > > > > boot_aggregate_late
> > > > > > >
> > > > > > > Therefore, init_ima() could move into late_initcall_sync like v1
> > > > > > > did:
> > > > > > > -
> > > > > > > https://lore.kernel.org/all/[email protected]/
> > > > > >
> > > > > > Thanks, Yeoreum. It's a bit premature to claim it's "safe" to move
> > > > > > the
> > > > > > initcall. Hopefully others will respond.
> > > > > >
> > > > > > Mimi
> > > > >
> > > > > I have also run with this patch on a number of bare metal and virtual
> > > > > machines,
> > > > > running everything from default Fedora 44 to a version with
> > > > > everything turned on
> > > > > (uefi secure boot, UKI with sdboot stub measurements, IMA measurement
> > > > > and appraisal enabled,
> > > > > all systemd measurements on, and systemd using the TPM for root
> > > > > partition decryption.)
> > > > > I too see only the kernel_version event between the normal and late
> > > > > calls, if ima_policy=critical_data.
> > > >
> > > > Thanks, Dave! Were all the systems you tested x86_64? The next step
> > > > would be
> > > > to test on different arch's (e.g. Z, Power).
> > >
> > > On both Z and PowerVM, there are ~30 measurements between boot_aggregate
> > > and
> > > boot_aggregate_late. For example, on PowerVM:
> > >
> > > # grep -n boot_aggregate
> > > /sys/kernel/security/integrity/ima/ascii_runtime_measurements
> > >
> > > 1:10 f60a05d7354fb34aabc02965216abd3428ea52bb ima-sig
> > > sha256:9887dd089ee19a6517bca10580b02c1bb9aa6cd86c157b6ead8a1c0403f348d5
> > > boot_aggregate
> > > 31:10 e2592b0d61da6300d3db447b143897a9792231ea ima-sig
> > > sha256:9887dd089ee19a6517bca10580b02c1bb9aa6cd86c157b6ead8a1c0403f348d5
> > > boot_aggregate_late
> > >
> > > It would be interesting to the results from a Raspberry Pi 5 as well,
> > > with/without a TPM.
> >
> > Honestly, I find this result hard to accept.
> >
> > This effectively means that there is code invoking IMA measurement during
> > late_initcall().
> > It also implies that if, in the future, a late_initcall is added that
> > performs
> > an IMA measurement before IMA initialization has occurred accoding to order
> > by linker,
> > that measurement could be missed.
>
> Exactly. The results are simply from booting with the builtin "tcb" and
> "critical_data" policies.
>
> $ sudo grubby --args="ima_policy=\"tcb|critical_data\"" --update-kernel
> /boot/vmlinuz-${SUFFIX}
Thanks. but I still wonder what meaasurements there are between
boot_aggregate and boot_aggregate_late.
Might be there would be key measurements if it takes more than
5 mins before generating boot_aggregate_late but this seems rare.
If you don't mind, would you share the contents of the log between
boot_aggregate and boot_aggregate_late?
since I only get a kernel_version in my environment.
And I think we can collect missing measurements before ima_init_core()
into another separate list than ima_measurements in ima_add_template_entry() and
we can extend them after boot_aggreagate log generation at ima_init_core()
then ima initialisation could be moved to late_initcall_sync like
(just for a test and share concept):
-------&<-------
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 9a1117112fb2..737c1ac8ad77 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -67,6 +67,7 @@ extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
extern const char boot_aggregate_late_name[];
+extern bool ima_extend_on;
/* IMA event related data */
struct ima_event_data {
@@ -107,6 +108,7 @@ struct ima_template_desc {
:...skipping...
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 9a1117112fb2..737c1ac8ad77 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -67,6 +67,7 @@ extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
extern const char boot_aggregate_late_name[];
+extern bool ima_extend_on;
/* IMA event related data */
struct ima_event_data {
@@ -107,6 +108,7 @@ struct ima_template_desc {
struct ima_template_entry {
int pcr;
+ int violation;
struct tpm_digest *digests;
struct ima_template_desc *template_desc; /* template descriptor */
u32 template_data_len;
@@ -317,6 +319,7 @@ unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
void ima_init_template_list(void);
int __init ima_init_digests(void);
+int __init ima_extend_deferred(void);
void __init ima_init_reboot_notifier(void);
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data);
diff --git a/security/integrity/ima/ima_init.c
b/security/integrity/ima/ima_init.c
index edd063b99685..f6a2b53c1dcb 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -149,10 +149,22 @@ int __init ima_init_core(bool late)
rc = ima_init_digests();
if (rc != 0)
return rc;
+
+ ima_extend_on = true;
+
rc = ima_add_boot_aggregate(late); /* boot aggregate must be first
entry */
if (rc != 0)
return rc;
+ /* This is just for a test. */
+ if (!late)
+ ima_extend_on = false;
+ else {
+ rc = ima_extend_deferred();
+ if (rc != 0)
+ return rc;
+ }
+
ima_init_policy();
rc = ima_fs_init();
diff --git a/security/integrity/ima/ima_queue.c
b/security/integrity/ima/ima_queue.c
index 319522450854..81f2ee070fee 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -22,10 +22,13 @@
#define AUDIT_CAUSE_LEN_MAX 32
+bool ima_extend_on;
+
/* pre-allocated array of tpm_digest structures to extend a PCR */
static struct tpm_digest *digests;
LIST_HEAD(ima_measurements); /* list of all measurements */
+static LIST_HEAD(ima_extend_deferred_head);
#ifdef CONFIG_IMA_KEXEC
static unsigned long binary_runtime_size;
#else
@@ -91,6 +94,7 @@ static int get_binary_runtime_size(struct ima_template_entry
*entry)
return size;
}
+
/* ima_add_template_entry helper function:
* - Add template entry to the measurement list and hash table, for
* all entries except those carried across kexec.
@@ -98,7 +102,8 @@ static int get_binary_runtime_size(struct ima_template_entry
*entry)
* (Called with ima_extend_list_mutex held.)
*/
static int ima_add_digest_entry(struct ima_template_entry *entry,
- bool update_htable)
+ bool update_htable,
+ struct list_head *measurements_list)
{
struct ima_queue_entry *qe;
unsigned int key;
@@ -111,7 +116,7 @@ static int ima_add_digest_entry(struct ima_template_entry
*entry,
qe->entry = entry;
INIT_LIST_HEAD(&qe->later);
- list_add_tail_rcu(&qe->later, &ima_measurements);
+ list_add_tail_rcu(&qe->later, measurements_list);
atomic_long_inc(&ima_htable.len);
if (update_htable) {
@@ -173,6 +178,7 @@ int ima_add_template_entry(struct ima_template_entry
*entry, int violation,
char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
int audit_info = 1;
int result = 0, tpmresult = 0;
+ struct list_head *measurements_list;
mutex_lock(&ima_extend_list_mutex);
@@ -195,15 +201,21 @@ int ima_add_template_entry(struct ima_template_entry
*entry, int violation,
}
}
+
+ entry->violation = violation;
+ measurements_list = (ima_extend_on) ? &ima_measurements :
+ &ima_extend_deferred_head;
+
result = ima_add_digest_entry(entry,
- !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE));
+ !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE),
+ measurements_list);
if (result < 0) {
audit_cause = "ENOMEM";
audit_info = 0;
goto out;
}
- if (violation) /* invalidate pcr */
+ if (violation) /* invalidate pcr */
digests_arg = digests;
tpmresult = ima_pcr_extend(digests_arg, entry->pcr);
@@ -225,7 +237,7 @@ int ima_restore_measurement_entry(struct ima_template_entry
*entry)
int result = 0;
mutex_lock(&ima_extend_list_mutex);
- result = ima_add_digest_entry(entry, 0);
+ result = ima_add_digest_entry(entry, 0, &ima_measurements);
mutex_unlock(&ima_extend_list_mutex);
return result;
}
@@ -288,3 +300,23 @@ int __init ima_init_digests(void)
return 0;
}
+
+int __init ima_extend_deferred(void)
+{
+ guard(mutex)(&ima_extend_list_mutex);
+ struct ima_queue_entry *qe;
+ struct tpm_digest *digests_arg;
+ int ret = 0;
+
+ list_for_each_entry(qe, &ima_extend_deferred_head, later) {
+ digests_arg = (qe->entry->violation) ? digests :
qe->entry->digests;
+ ret = ima_pcr_extend(digests_arg, qe->entry->pcr);
+ if (ret)
+ /* TODO: audit? */
+ break;
+ }
+
+ list_splice_tail_init(&ima_extend_deferred_head, &ima_measurements);
+
+ return ret;
+}
--
Sincerely,
Yeoreum Yun