On Wed, 2025-12-10 at 16:03 -0800, steven chen wrote: > On 12/9/2025 2:17 AM, Roberto Sassu wrote: > > From: Roberto Sassu <[email protected]> > > > > Introduce the ability of staging the entire of the IMA measurement list, or > > a portion, for deletion. Staging means moving the current content of the > > measurement list to a separate location, and allowing users to read and > > delete it. This causes the measurement list to be atomically truncated > > before new measurements can be added. Staging can be done only once at a > > time. > > > > User space is responsible to concatenate the staged IMA measurements list > > portions following the temporal order in which the operations were done, > > together with the current measurement list. Then, it can send the collected > > data to the remote verifiers. > > > > The benefit of this solution is the ability to free precious kernel memory, > > in exchange of delegating user space to reconstruct the full measurement > > list from the chunks. No trust needs to be given to user space, since the > > integrity of the measurement list is protected by the TPM. > > > > By default, staging the measurements list for deletion does not alter the > > hash table. When staging is done, IMA is still able to detect collisions on > > the staged and later deleted measurement entries, by keeping the entry > > digests (only template data are freed). > > > > However, since during the measurements list serialization only the SHA1 > > digest is passed, and since there are no template data to recalculate the > > other digests from, the hash table is currently not populated with digests > > from staged/deleted entries after kexec(). > > > > Introduce the new kernel option ima_flush_htable to decide whether or not > > the digests of staged measurement entries are flushed from the hash table. > > > > Then, introduce ascii_runtime_measurements_staged_<algo> and > > binary_runtime_measurement_staged_<algo> interfaces to stage/delete the > > measurements. Use 'echo A > <IMA interface>' and 'echo D > <IMA interface>' > > to respectively stage and delete the entire measurements list. Use > > 'echo N > <IMA interface>', with N between 1 and ULONG_MAX, to stage the > > selected portion of the measurements list. > > > > The ima_measure_users counter (protected by the ima_measure_lock mutex) has > > been introduced to protect access to the measurement list and the staged > > part. The open method of all the measurement interfaces has been extended > > to allow only one writer at a time or, in alternative, multiple readers. > > The write permission is used to stage/delete the measurements, the read > > permission to read them. Write requires also the CAP_SYS_ADMIN capability. > > Hi Roberto, > > I released version 2 of trim N entries patch as bellow: > > [PATCH v2 0/1] Trim N entries of IMA event logs > <https://lore.kernel.org/linux-integrity/[email protected]/T/#t> > > I adapted some of your idea and I think trim N has following advantages: > 1: less measurement list hold time than your current implementation > 2. operation much simple for user space > 3. less kernel code change > 4. no potential issue as Gregory mentioned.
Please have a look at: https://marc.info/?l=linux-integrity&m=176545085325473&w=2 and let me know if I'm missing something. Thanks Roberto > Thanks, > > Steven > > > Finally, introduce the _notrim version of the run-time measurements count > > and the binary measurements list size, to display them in the kexec-related > > critical data records. > > > > Note: This code derives from the Alt-IMA Huawei project, and is being > > released under the dual license model (GPL-2.0 OR MIT). > > > > Link: https://github.com/linux-integrity/linux/issues/1 > > Signed-off-by: Roberto Sassu <[email protected]> > > --- > > .../admin-guide/kernel-parameters.txt | 4 + > > security/integrity/ima/ima.h | 10 +- > > security/integrity/ima/ima_fs.c | 222 +++++++++++++++++- > > security/integrity/ima/ima_kexec.c | 13 +- > > security/integrity/ima/ima_queue.c | 111 ++++++++- > > 5 files changed, 340 insertions(+), 20 deletions(-) > > > > diff --git a/Documentation/admin-guide/kernel-parameters.txt > > b/Documentation/admin-guide/kernel-parameters.txt > > index 6c42061ca20e..355d8930e3ac 100644 > > --- a/Documentation/admin-guide/kernel-parameters.txt > > +++ b/Documentation/admin-guide/kernel-parameters.txt > > @@ -2215,6 +2215,10 @@ > > Use the canonical format for the binary runtime > > measurements, instead of host native format. > > > > + ima_flush_htable [IMA] > > + Flush the measurement list hash table when staging all > > + or a part of it for deletion. > > + > > ima_hash= [IMA] > > Format: { md5 | sha1 | rmd160 | sha256 | sha384 > > | sha512 | ... } > > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h > > index e3d71d8d56e3..d7aa4a0f79b1 100644 > > --- a/security/integrity/ima/ima.h > > +++ b/security/integrity/ima/ima.h > > @@ -117,6 +117,8 @@ struct ima_queue_entry { > > struct ima_template_entry *entry; > > }; > > extern struct list_head ima_measurements; /* list of all measurements */ > > +extern struct list_head ima_measurements_staged; /* list of staged meas. */ > > +extern bool ima_measurements_staged_exist; /* If there are staged meas. */ > > > > /* Some details preceding the binary serialized measurement list */ > > struct ima_kexec_hdr { > > @@ -281,10 +283,12 @@ struct ima_template_desc > > *ima_template_desc_current(void); > > struct ima_template_desc *ima_template_desc_buf(void); > > struct ima_template_desc *lookup_template_desc(const char *name); > > bool ima_template_has_modsig(const struct ima_template_desc > > *ima_template); > > +int ima_queue_stage(unsigned long req_value); > > +int ima_queue_delete_staged(void); > > 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); > > -unsigned long ima_get_binary_runtime_size(void); > > +unsigned long ima_get_binary_runtime_size(bool notrim); > > int ima_init_template(void); > > void ima_init_template_list(void); > > int __init ima_init_digests(void); > > @@ -298,11 +302,13 @@ 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 len; /* current num of stored meas. in the list */ > > + atomic_long_t len_notrim; /* total num of stored meas. in the list */ > > atomic_long_t violations; > > struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; > > }; > > extern struct ima_h_table ima_htable; > > +extern struct mutex ima_extend_list_mutex; > > > > static inline unsigned int ima_hash_key(u8 *digest) > > { > > diff --git a/security/integrity/ima/ima_fs.c > > b/security/integrity/ima/ima_fs.c > > index 87045b09f120..321c98ae0e55 100644 > > --- a/security/integrity/ima/ima_fs.c > > +++ b/security/integrity/ima/ima_fs.c > > @@ -24,7 +24,12 @@ > > > > #include "ima.h" > > > > +/* Requests: ('A', [1, ULONG_MAX])\n (stage all/N) or D\n (delete staged) > > */ > > +#define STAGED_REQ_LENGTH 21 > > + > > static DEFINE_MUTEX(ima_write_mutex); > > +static DEFINE_MUTEX(ima_measure_lock); > > +static long ima_measure_users; > > > > bool ima_canonical_fmt; > > static int __init default_canonical_fmt_setup(char *str) > > @@ -74,14 +79,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; > > @@ -91,7 +97,18 @@ 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_staged_start(struct seq_file *m, loff_t *pos) > > +{ > > + return _ima_measurements_start(m, pos, &ima_measurements_staged); > > +} > > + > > +static void *_ima_measurements_next(struct seq_file *m, void *v, loff_t > > *pos, > > + struct list_head *head) > > { > > struct ima_queue_entry *qe = v; > > > > @@ -103,7 +120,18 @@ 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_staged_next(struct seq_file *m, void *v, > > + loff_t *pos) > > +{ > > + return _ima_measurements_next(m, v, pos, &ima_measurements_staged); > > } > > > > static void ima_measurements_stop(struct seq_file *m, void *v) > > @@ -202,16 +230,138 @@ static const struct seq_operations > > ima_measurments_seqops = { > > .show = ima_measurements_show > > }; > > > > +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; > > + > > + mutex_lock(&ima_measure_lock); > > + if ((write && ima_measure_users != 0) || > > + (!write && ima_measure_users < 0)) { > > + mutex_unlock(&ima_measure_lock); > > + return -EBUSY; > > + } > > + > > + ret = seq_open(file, seq_ops); > > + if (ret < 0) { > > + mutex_unlock(&ima_measure_lock); > > + return ret; > > + } > > + > > + if (write) > > + ima_measure_users--; > > + else > > + ima_measure_users++; > > + > > + mutex_unlock(&ima_measure_lock); > > + 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; > > + > > + mutex_lock(&ima_measure_lock); > > + ret = seq_release(inode, file); > > + if (!ret) { > > + if (write) > > + ima_measure_users++; > > + else > > + ima_measure_users--; > > + } > > + > > + mutex_unlock(&ima_measure_lock); > > + 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, > > +}; > > + > > +static const struct seq_operations ima_measurments_staged_seqops = { > > + .start = ima_measurements_staged_start, > > + .next = ima_measurements_staged_next, > > + .stop = ima_measurements_stop, > > + .show = ima_measurements_show > > +}; > > + > > +static int ima_measurements_staged_open(struct inode *inode, struct file > > *file) > > +{ > > + return _ima_measurements_open(inode, file, > > + &ima_measurments_staged_seqops); > > +} > > + > > +static ssize_t ima_measurements_staged_read(struct file *file, char __user > > *buf, > > + size_t size, loff_t *ppos) > > +{ > > + if (!ima_measurements_staged_exist) > > + return -ENOENT; > > + > > + return seq_read(file, buf, size, ppos); > > +} > > + > > +static ssize_t ima_measurements_staged_write(struct file *file, > > + const char __user *buf, > > + size_t datalen, loff_t *ppos) > > +{ > > + char req[STAGED_REQ_LENGTH], *req_ptr = req; > > + unsigned long req_value; > > + int ret; > > + > > + if (*ppos > 0 || datalen < 2 || datalen > STAGED_REQ_LENGTH) > > + return -EINVAL; > > + > > + ret = copy_from_user(req, buf, datalen); > > + if (ret < 0) > > + return ret; > > + > > + if (strsep(&req_ptr, "\n") == NULL) > > + return -EINVAL; > > + > > + switch (req[0]) { > > + case 'A': > > + if (datalen != 2 || req[1] != '\0') > > + return -EINVAL; > > + > > + ret = ima_queue_stage(ULONG_MAX); > > + break; > > + case 'D': > > + if (datalen != 2 || req[1] != '\0') > > + return -EINVAL; > > + > > + ret = ima_queue_delete_staged(); > > + break; > > + default: > > + ret = kstrtoul(req, 0, &req_value); > > + if (!ret) > > + ret = ima_queue_stage(req_value); > > + } > > + > > + if (ret < 0) > > + return ret; > > + > > + return datalen; > > +} > > + > > +static const struct file_operations ima_measurements_staged_ops = { > > + .open = ima_measurements_staged_open, > > + .read = ima_measurements_staged_read, > > + .write = ima_measurements_staged_write, > > + .llseek = seq_lseek, > > + .release = ima_measurements_release, > > }; > > > > void ima_print_digest(struct seq_file *m, u8 *digest, u32 size) > > @@ -279,14 +429,37 @@ 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 const struct seq_operations ima_ascii_measurements_staged_seqops = { > > + .start = ima_measurements_staged_start, > > + .next = ima_measurements_staged_next, > > + .stop = ima_measurements_stop, > > + .show = ima_ascii_measurements_show > > +}; > > + > > +static int ima_ascii_measurements_staged_open(struct inode *inode, > > + struct file *file) > > +{ > > + return _ima_measurements_open(inode, file, > > + &ima_ascii_measurements_staged_seqops); > > +} > > + > > +static const struct file_operations ima_ascii_measurements_staged_ops = { > > + .open = ima_ascii_measurements_staged_open, > > + .read = ima_measurements_staged_read, > > + .write = ima_measurements_staged_write, > > + .llseek = seq_lseek, > > + .release = ima_measurements_release, > > }; > > > > static ssize_t ima_read_policy(char *path) > > @@ -419,6 +592,25 @@ static int __init > > create_securityfs_measurement_lists(void) > > &ima_measurements_ops); > > if (IS_ERR(dentry)) > > return PTR_ERR(dentry); > > + > > + sprintf(file_name, "ascii_runtime_measurements_staged_%s", > > + hash_algo_name[algo]); > > + dentry = securityfs_create_file(file_name, > > + S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP, > > + ima_dir, (void *)(uintptr_t)i, > > + &ima_ascii_measurements_staged_ops); > > + if (IS_ERR(dentry)) > > + return PTR_ERR(dentry); > > + > > + sprintf(file_name, "binary_runtime_measurements_staged_%s", > > + hash_algo_name[algo]); > > + dentry = securityfs_create_file(file_name, > > + S_IRUSR | S_IRGRP | > > + S_IWUSR | S_IWGRP, > > + ima_dir, (void *)(uintptr_t)i, > > + &ima_measurements_staged_ops); > > + if (IS_ERR(dentry)) > > + return PTR_ERR(dentry); > > } > > > > return 0; > > @@ -528,6 +720,20 @@ int __init ima_fs_init(void) > > goto out; > > } > > > > + dentry = securityfs_create_symlink("binary_runtime_measurements_staged", > > + ima_dir, "binary_runtime_measurements_staged_sha1", NULL); > > + if (IS_ERR(dentry)) { > > + ret = PTR_ERR(dentry); > > + goto out; > > + } > > + > > + dentry = securityfs_create_symlink("ascii_runtime_measurements_staged", > > + ima_dir, "ascii_runtime_measurements_staged_sha1", NULL); > > + if (IS_ERR(dentry)) { > > + ret = PTR_ERR(dentry); > > + goto out; > > + } > > + > > dentry = securityfs_create_file("runtime_measurements_count", > > S_IRUSR | S_IRGRP, ima_dir, NULL, > > &ima_measurements_count_ops); > > diff --git a/security/integrity/ima/ima_kexec.c > > b/security/integrity/ima/ima_kexec.c > > index 7362f68f2d8b..23a20300da7b 100644 > > --- a/security/integrity/ima/ima_kexec.c > > +++ b/security/integrity/ima/ima_kexec.c > > @@ -40,8 +40,8 @@ void ima_measure_kexec_event(const char *event_name) > > long len; > > int n; > > > > - buf_size = ima_get_binary_runtime_size(); > > - len = atomic_long_read(&ima_htable.len); > > + buf_size = ima_get_binary_runtime_size(true); > > + len = atomic_long_read(&ima_htable.len_notrim); > > > > n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, > > "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" > > @@ -93,8 +93,10 @@ static int ima_dump_measurement_list(unsigned long > > *buffer_size, void **buffer, > > > > memset(&khdr, 0, sizeof(khdr)); > > 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) { > > + > > + /* It can race with ima_queue_stage(). */ > > + mutex_lock(&ima_extend_list_mutex); > > + list_for_each_entry(qe, &ima_measurements, later) { > > if (ima_kexec_file.count < ima_kexec_file.size) { > > khdr.count++; > > ima_measurements_show(&ima_kexec_file, qe); > > @@ -103,6 +105,7 @@ static int ima_dump_measurement_list(unsigned long > > *buffer_size, void **buffer, > > break; > > } > > } > > + mutex_unlock(&ima_extend_list_mutex); > > > > /* > > * fill in reserved space with some buffer details > > @@ -157,7 +160,7 @@ 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(false) + 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 590637e81ad1..868f216ac343 100644 > > --- a/security/integrity/ima/ima_queue.c > > +++ b/security/integrity/ima/ima_queue.c > > @@ -22,19 +22,32 @@ > > > > #define AUDIT_CAUSE_LEN_MAX 32 > > > > +bool ima_flush_htable; > > +static int __init ima_flush_htable_setup(char *str) > > +{ > > + 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; > > > > LIST_HEAD(ima_measurements); /* list of all measurements */ > > +LIST_HEAD(ima_measurements_staged); /* list of staged measurements */ > > +bool ima_measurements_staged_exist; /* If there are staged measurements */ > > #ifdef CONFIG_IMA_KEXEC > > static unsigned long binary_runtime_size; > > +static unsigned long binary_runtime_size_notrim; > > #else > > static unsigned long binary_runtime_size = ULONG_MAX; > > +static unsigned long binary_runtime_size_notrim = ULONG_MAX; > > #endif > > > > /* key: inode (before secure-hashing a file) */ > > struct ima_h_table ima_htable = { > > .len = ATOMIC_LONG_INIT(0), > > + .len_notrim = ATOMIC_LONG_INIT(0), > > .violations = ATOMIC_LONG_INIT(0), > > .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT > > }; > > @@ -43,7 +56,7 @@ struct ima_h_table ima_htable = { > > * and extending the TPM PCR aggregate. Since tpm_extend can take > > * long (and the tpm driver uses a mutex), we can't use the spinlock. > > */ > > -static DEFINE_MUTEX(ima_extend_list_mutex); > > +DEFINE_MUTEX(ima_extend_list_mutex); > > > > /* > > * Used internally by the kernel to suspend measurements. > > @@ -114,15 +127,19 @@ static int ima_add_digest_entry(struct > > ima_template_entry *entry, > > list_add_tail_rcu(&qe->later, &ima_measurements); > > > > atomic_long_inc(&ima_htable.len); > > + atomic_long_inc(&ima_htable.len_notrim); > > if (update_htable) { > > key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); > > hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); > > } > > > > - if (binary_runtime_size != ULONG_MAX) { > > + if (binary_runtime_size_notrim != ULONG_MAX) { > > int size; > > > > size = get_binary_runtime_size(entry); > > + binary_runtime_size_notrim = > > + (binary_runtime_size_notrim < ULONG_MAX - size) ? > > + binary_runtime_size_notrim + size : ULONG_MAX; > > binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ? > > binary_runtime_size + size : ULONG_MAX; > > } > > @@ -134,12 +151,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(bool notrim) > > { > > - if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr))) > > + unsigned long *val; > > + > > + mutex_lock(&ima_extend_list_mutex); > > + val = (notrim) ? &binary_runtime_size_notrim : &binary_runtime_size; > > + 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) > > @@ -220,6 +243,84 @@ int ima_add_template_entry(struct ima_template_entry > > *entry, int violation, > > return result; > > } > > > > +int ima_queue_stage(unsigned long req_value) > > +{ > > + unsigned long req_value_copy = req_value, to_remove = 0; > > + struct ima_queue_entry *qe; > > + > > + if (ima_measurements_staged_exist) > > + return -EEXIST; > > + > > + mutex_lock(&ima_extend_list_mutex); > > + if (list_empty(&ima_measurements)) { > > + mutex_unlock(&ima_extend_list_mutex); > > + return -ENOENT; > > + } > > + > > + if (req_value == ULONG_MAX) { > > + list_replace(&ima_measurements, &ima_measurements_staged); > > + INIT_LIST_HEAD(&ima_measurements); > > + atomic_long_set(&ima_htable.len, 0); > > + if (IS_ENABLED(CONFIG_IMA_KEXEC)) > > + binary_runtime_size = 0; > > + } else { > > + list_for_each_entry(qe, &ima_measurements, later) { > > + to_remove += get_binary_runtime_size(qe->entry); > > + if (--req_value_copy == 0) > > + break; > > + } > > + > > + if (req_value_copy > 0) { > > + mutex_unlock(&ima_extend_list_mutex); > > + return -ENOENT; > > + } > > + > > + __list_cut_position(&ima_measurements_staged, &ima_measurements, > > + &qe->later); > > + atomic_long_sub(req_value, &ima_htable.len); > > + if (IS_ENABLED(CONFIG_IMA_KEXEC)) > > + binary_runtime_size -= to_remove; > > + } > > + > > + if (ima_flush_htable) { > > + list_for_each_entry(qe, &ima_measurements_staged, later) > > + /* It can race with ima_lookup_digest_entry(). */ > > + hlist_del_rcu(&qe->hnext); > > + } > > + > > + mutex_unlock(&ima_extend_list_mutex); > > + ima_measurements_staged_exist = true; > > + return 0; > > +} > > + > > +int ima_queue_delete_staged(void) > > +{ > > + struct ima_queue_entry *qe, *qe_tmp; > > + unsigned int i; > > + > > + if (!ima_measurements_staged_exist) > > + return -ENOENT; > > + > > + list_for_each_entry_safe(qe, qe_tmp, &ima_measurements_staged, later) { > > + for (i = 0; i < qe->entry->template_desc->num_fields; i++) { > > + kfree(qe->entry->template_data[i].data); > > + qe->entry->template_data[i].data = NULL; > > + qe->entry->template_data[i].len = 0; > > + } > > + > > + list_del(&qe->later); > > + > > + if (ima_flush_htable) { > > + kfree(qe->entry->digests); > > + kfree(qe->entry); > > + kfree(qe); > > + } > > + } > > + > > + ima_measurements_staged_exist = false; > > + return 0; > > +} > > + > > int ima_restore_measurement_entry(struct ima_template_entry *entry) > > { > > int result = 0; >
