On Thu, 2026-03-26 at 15:44 -0700, steven chen wrote:
> On 3/26/2026 10:30 AM, Roberto Sassu wrote:
> > From: Roberto Sassu <[email protected]>
> > 
> > Introduce the ability of staging the IMA measurement list and deleting them
> > with a prompt.
> > 
> > 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. In the event of kexec(),
> > staging is reverted and staged entries will be carried over to the new
> > kernel.
> > 
> > Introduce ascii_runtime_measurements_<algo>_staged and
> > binary_runtime_measurements_<algo>_staged interfaces to stage and delete
> > the measurements. Use 'echo A > <IMA interface>' and
> > 'echo D > <IMA interface>' to respectively stage and delete the entire
> > measurements list. Locking of these interfaces is also mediated with a call
> > to _ima_measurements_open() and with ima_measurements_release().
> > 
> > Implement the staging functionality by introducing the new global
> > measurements list ima_measurements_staged, and ima_queue_stage() and
> > ima_queue_delete_staged_all() to respectively move measurements from the
> > current measurements list to the staged one, and to move staged
> > measurements to the ima_measurements_trim list for deletion. Introduce
> > ima_queue_delete() to delete the measurements.
> > 
> > Finally, introduce the BINARY_STAGED AND BINARY_FULL binary measurements
> > list types, to maintain the counters and the binary size of staged
> > measurements and the full measurements list (including entries that were
> > staged). BINARY still represents the current binary measurements list.
> > 
> > Use the binary size for the BINARY + BINARY_STAGED types in
> > ima_add_kexec_buffer(), since both measurements list types are copied to
> > the secondary kernel during kexec. Use BINARY_FULL in
> > ima_measure_kexec_event(), to generate a critical data record.
> > 
> > It should be noted that the BINARY_FULL counter is not passed through
> > kexec. Thus, the number of entries included in the kexec critical data
> > records refers to the entries since the previous kexec 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
> > Suggested-by: Gregory Lumen <[email protected]> (staging 
> > revert)
> > Signed-off-by: Roberto Sassu <[email protected]>
> > ---
> >   security/integrity/ima/Kconfig     |  13 +++
> >   security/integrity/ima/ima.h       |   8 +-
> >   security/integrity/ima/ima_fs.c    | 167 ++++++++++++++++++++++++++---
> >   security/integrity/ima/ima_kexec.c |  22 +++-
> >   security/integrity/ima/ima_queue.c |  97 ++++++++++++++++-
> >   5 files changed, 286 insertions(+), 21 deletions(-)
> > 
> > diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> > index 976e75f9b9ba..e714726f3384 100644
> > --- a/security/integrity/ima/Kconfig
> > +++ b/security/integrity/ima/Kconfig
> > @@ -332,4 +332,17 @@ config IMA_KEXEC_EXTRA_MEMORY_KB
> >       If set to the default value of 0, an extra half page of memory for 
> > those
> >       additional measurements will be allocated.
> >   
> > +config IMA_STAGING
> > +   bool "Support for staging the measurements list"
> > +   default y
> > +   help
> > +     Add support for staging the measurements list.
> > +
> > +     It allows user space to stage the measurements list for deletion and
> > +     to delete the staged measurements after confirmation.
> > +
> > +     On kexec, staging is reverted and staged measurements are prepended
> > +     to the current measurements list when measurements are copied to the
> > +     secondary kernel.
> > +
> >   endif
> > diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> > index 97b7d6024b5d..65db152a0a24 100644
> > --- a/security/integrity/ima/ima.h
> > +++ b/security/integrity/ima/ima.h
> > @@ -30,9 +30,11 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 
> > 10 };
> >   
> >   /*
> >    * BINARY: current binary measurements list
> > + * BINARY_STAGED: staged binary measurements list
> > + * BINARY_FULL: binary measurements list since IMA init (lost after kexec)
> >    */
> >   enum binary_lists {
> > -   BINARY, BINARY__LAST
> > +   BINARY, BINARY_STAGED, BINARY_FULL, BINARY__LAST
> >   };
> >   
> >   /* digest size for IMA, fits SHA1 or MD5 */
> > @@ -125,6 +127,7 @@ 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. */
> >   
> >   /* Some details preceding the binary serialized measurement list */
> >   struct ima_kexec_hdr {
> > @@ -314,6 +317,8 @@ 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(void);
> > +int ima_queue_staged_delete_all(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);
> > @@ -334,6 +339,7 @@ extern spinlock_t ima_queue_lock;
> >   extern atomic_long_t ima_num_entries[BINARY__LAST];
> >   extern atomic_long_t ima_num_violations;
> >   extern struct hlist_head __rcu *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 7709a4576322..39d9128e9f22 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -24,6 +24,13 @@
> >   
> >   #include "ima.h"
> >   
> > +/*
> > + * Requests:
> > + * 'A\n': stage the entire measurements list
> > + * 'D\n': delete all staged measurements
> > + */
> > +#define STAGED_REQ_LENGTH 21
> > +
> >   static DEFINE_MUTEX(ima_write_mutex);
> >   static DEFINE_MUTEX(ima_measure_mutex);
> >   static long ima_measure_users;
> > @@ -97,6 +104,11 @@ 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)
> >   {
> > @@ -118,6 +130,12 @@ 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)
> >   {
> >   }
> > @@ -283,6 +301,68 @@ static const struct file_operations 
> > ima_measurements_ops = {
> >     .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_write(struct file *file,
> > +                                        const char __user *buf,
> > +                                        size_t datalen, loff_t *ppos)
> > +{
> > +   char req[STAGED_REQ_LENGTH];
> > +   int ret;
> > +
> > +   if (*ppos > 0 || datalen < 2 || datalen > STAGED_REQ_LENGTH)
> > +           return -EINVAL;
> > +
> > +   if (copy_from_user(req, buf, datalen) != 0)
> > +           return -EFAULT;
> > +
> > +   if (req[datalen - 1] != '\n')
> > +           return -EINVAL;
> > +
> > +   req[datalen - 1] = '\0';
> > +
> > +   switch (req[0]) {
> > +   case 'A':
> > +           if (datalen != 2)
> > +                   return -EINVAL;
> > +
> > +           ret = ima_queue_stage();
> > +           break;
> > +   case 'D':
> > +           if (datalen != 2)
> > +                   return -EINVAL;
> > +
> > +           ret = ima_queue_staged_delete_all();
> > +           break;
> 
> I think the following two steps may not work because of race condition:
> 
> step1: ret = ima_queue_stage(); //this will put all logs in active list into 
> staged list;
> step2: ret = ima_queue_staged_delete_all(); //this will delete all logs in 
> staged list;
> 
> The following is the step of race condition:
>      1. current active log list LA1;
>      2. user agent read the TPM quote QA1 match list LA1;
>      3. new event NewLog is added into active log list LA1+NewLog
>      4. user agent call ima_queue_stage() and generated staged list
>         including LA1+NewLog.
>      5. user agent call ima_queue_staged_delete_all();
>         The new log NewLog in step 3 is also deleted

Please refer to the documentation patch which explains the intended
workflow of this approach (Remote Attestation Agent Workflow).

Roberto

> Next time the attestation will fail if using the active log list in the 
> kernel.
> 
> Thanks,
> 
> Steven
> 
> > +   default:
> > +           ret = -EINVAL;
> > +   }
> > +
> > +   if (ret < 0)
> > +           return ret;
> > +
> > +   return datalen;
> > +}
> > +
> > +static const struct file_operations ima_measurements_staged_ops = {
> > +   .open = ima_measurements_staged_open,
> > +   .read = seq_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)
> >   {
> >     u32 i;
> > @@ -356,6 +436,28 @@ static const struct file_operations 
> > ima_ascii_measurements_ops = {
> >     .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 = seq_read,
> > +   .write = ima_measurements_staged_write,
> > +   .llseek = seq_lseek,
> > +   .release = ima_measurements_release,
> > +};
> > +
> >   static ssize_t ima_read_policy(char *path)
> >   {
> >     void *data = NULL;
> > @@ -459,10 +561,21 @@ static const struct seq_operations ima_policy_seqops 
> > = {
> >   };
> >   #endif
> >   
> > -static int __init create_securityfs_measurement_lists(void)
> > +static int __init create_securityfs_measurement_lists(bool staging)
> >   {
> > +   const struct file_operations *ascii_ops = &ima_ascii_measurements_ops;
> > +   const struct file_operations *binary_ops = &ima_measurements_ops;
> > +   mode_t permissions = S_IRUSR | S_IRGRP;
> > +   const char *file_suffix = "";
> >     int count = NR_BANKS(ima_tpm_chip);
> >   
> > +   if (staging) {
> > +           ascii_ops = &ima_ascii_measurements_staged_ops;
> > +           binary_ops = &ima_measurements_staged_ops;
> > +           file_suffix = "_staged";
> > +           permissions |= (S_IWUSR | S_IWGRP);
> > +   }
> > +
> >     if (ima_sha1_idx >= NR_BANKS(ima_tpm_chip))
> >             count++;
> >   
> > @@ -473,29 +586,32 @@ static int __init 
> > create_securityfs_measurement_lists(void)
> >   
> >             if (algo == HASH_ALGO__LAST)
> >                     snprintf(file_name, sizeof(file_name),
> > -                            "ascii_runtime_measurements_tpm_alg_%x",
> > -                            ima_tpm_chip->allocated_banks[i].alg_id);
> > +                            "ascii_runtime_measurements_tpm_alg_%x%s",
> > +                            ima_tpm_chip->allocated_banks[i].alg_id,
> > +                            file_suffix);
> >             else
> >                     snprintf(file_name, sizeof(file_name),
> > -                            "ascii_runtime_measurements_%s",
> > -                            hash_algo_name[algo]);
> > -           dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
> > +                            "ascii_runtime_measurements_%s%s",
> > +                            hash_algo_name[algo], file_suffix);
> > +           dentry = securityfs_create_file(file_name, permissions,
> >                                             ima_dir, (void *)(uintptr_t)i,
> > -                                           &ima_ascii_measurements_ops);
> > +                                           ascii_ops);
> >             if (IS_ERR(dentry))
> >                     return PTR_ERR(dentry);
> >   
> >             if (algo == HASH_ALGO__LAST)
> >                     snprintf(file_name, sizeof(file_name),
> > -                            "binary_runtime_measurements_tpm_alg_%x",
> > -                            ima_tpm_chip->allocated_banks[i].alg_id);
> > +                            "binary_runtime_measurements_tpm_alg_%x%s",
> > +                            ima_tpm_chip->allocated_banks[i].alg_id,
> > +                            file_suffix);
> >             else
> >                     snprintf(file_name, sizeof(file_name),
> > -                            "binary_runtime_measurements_%s",
> > -                            hash_algo_name[algo]);
> > -           dentry = securityfs_create_file(file_name, S_IRUSR | S_IRGRP,
> > +                            "binary_runtime_measurements_%s%s",
> > +                            hash_algo_name[algo], file_suffix);
> > +
> > +           dentry = securityfs_create_file(file_name, permissions,
> >                                             ima_dir, (void *)(uintptr_t)i,
> > -                                           &ima_measurements_ops);
> > +                                           binary_ops);
> >             if (IS_ERR(dentry))
> >                     return PTR_ERR(dentry);
> >     }
> > @@ -503,6 +619,23 @@ static int __init 
> > create_securityfs_measurement_lists(void)
> >     return 0;
> >   }
> >   
> > +static int __init create_securityfs_staging_links(void)
> > +{
> > +   struct dentry *dentry;
> > +
> > +   dentry = securityfs_create_symlink("binary_runtime_measurements_staged",
> > +           ima_dir, "binary_runtime_measurements_sha1_staged", NULL);
> > +   if (IS_ERR(dentry))
> > +           return PTR_ERR(dentry);
> > +
> > +   dentry = securityfs_create_symlink("ascii_runtime_measurements_staged",
> > +           ima_dir, "ascii_runtime_measurements_sha1_staged", NULL);
> > +   if (IS_ERR(dentry))
> > +           return PTR_ERR(dentry);
> > +
> > +   return 0;
> > +}
> > +
> >   /*
> >    * ima_open_policy: sequentialize access to the policy file
> >    */
> > @@ -595,7 +728,13 @@ int __init ima_fs_init(void)
> >             goto out;
> >     }
> >   
> > -   ret = create_securityfs_measurement_lists();
> > +   ret = create_securityfs_measurement_lists(false);
> > +   if (ret == 0 && IS_ENABLED(CONFIG_IMA_STAGING)) {
> > +           ret = create_securityfs_measurement_lists(true);
> > +           if (ret == 0)
> > +                   ret = create_securityfs_staging_links();
> > +   }
> > +
> >     if (ret != 0)
> >             goto out;
> >   
> > diff --git a/security/integrity/ima/ima_kexec.c 
> > b/security/integrity/ima/ima_kexec.c
> > index d7d0fb639d99..d5503dd5cc9b 100644
> > --- a/security/integrity/ima/ima_kexec.c
> > +++ b/security/integrity/ima/ima_kexec.c
> > @@ -42,8 +42,8 @@ void ima_measure_kexec_event(const char *event_name)
> >     long len;
> >     int n;
> >   
> > -   buf_size = ima_get_binary_runtime_size(BINARY);
> > -   len = atomic_long_read(&ima_num_entries[BINARY]);
> > +   buf_size = ima_get_binary_runtime_size(BINARY_FULL);
> > +   len = atomic_long_read(&ima_num_entries[BINARY_FULL]);
> >   
> >     n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN,
> >                   "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;"
> > @@ -106,13 +106,26 @@ 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() and ima_queue_delete_staged(). */
> > +   mutex_lock(&ima_extend_list_mutex);
> > +
> > +   list_for_each_entry_rcu(qe, &ima_measurements_staged, later,
> > +                           lockdep_is_held(&ima_extend_list_mutex)) {
> >             ret = ima_dump_measurement(&khdr, qe);
> >             if (ret < 0)
> >                     break;
> >     }
> >   
> > +   list_for_each_entry_rcu(qe, &ima_measurements, later,
> > +                           lockdep_is_held(&ima_extend_list_mutex)) {
> > +           if (!ret)
> > +                   ret = ima_dump_measurement(&khdr, qe);
> > +           if (ret < 0)
> > +                   break;
> > +   }
> > +
> > +   mutex_unlock(&ima_extend_list_mutex);
> > +
> >     /*
> >      * fill in reserved space with some buffer details
> >      * (eg. version, buffer size, number of measurements)
> > @@ -167,6 +180,7 @@ void ima_add_kexec_buffer(struct kimage *image)
> >             extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024;
> >   
> >     binary_runtime_size = ima_get_binary_runtime_size(BINARY) +
> > +                         ima_get_binary_runtime_size(BINARY_STAGED) +
> >                           extra_memory;
> >   
> >     if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
> > diff --git a/security/integrity/ima/ima_queue.c 
> > b/security/integrity/ima/ima_queue.c
> > index b6d10dceb669..50519ed837d4 100644
> > --- a/security/integrity/ima/ima_queue.c
> > +++ b/security/integrity/ima/ima_queue.c
> > @@ -26,6 +26,7 @@
> >   static struct tpm_digest *digests;
> >   
> >   LIST_HEAD(ima_measurements);      /* list of all measurements */
> > +LIST_HEAD(ima_measurements_staged); /* list of staged measurements */
> >   #ifdef CONFIG_IMA_KEXEC
> >   static unsigned long binary_runtime_size[BINARY__LAST];
> >   #else
> > @@ -45,11 +46,11 @@ atomic_long_t ima_num_violations = ATOMIC_LONG_INIT(0);
> >   /* key: inode (before secure-hashing a file) */
> >   struct hlist_head __rcu *ima_htable;
> >   
> > -/* mutex protects atomicity of extending measurement list
> > +/* mutex protects atomicity of extending and staging measurement list
> >    * 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.
> > @@ -174,12 +175,16 @@ static int ima_add_digest_entry(struct 
> > ima_template_entry *entry,
> >                             lockdep_is_held(&ima_extend_list_mutex));
> >   
> >     atomic_long_inc(&ima_num_entries[BINARY]);
> > +   atomic_long_inc(&ima_num_entries[BINARY_FULL]);
> > +
> >     if (update_htable) {
> >             key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
> >             hlist_add_head_rcu(&qe->hnext, &htable[key]);
> >     }
> >   
> >     ima_update_binary_runtime_size(entry, BINARY);
> > +   ima_update_binary_runtime_size(entry, BINARY_FULL);
> > +
> >     return 0;
> >   }
> >   
> > @@ -280,6 +285,94 @@ int ima_add_template_entry(struct ima_template_entry 
> > *entry, int violation,
> >     return result;
> >   }
> >   
> > +int ima_queue_stage(void)
> > +{
> > +   int ret = 0;
> > +
> > +   mutex_lock(&ima_extend_list_mutex);
> > +   if (!list_empty(&ima_measurements_staged)) {
> > +           ret = -EEXIST;
> > +           goto out_unlock;
> > +   }
> > +
> > +   if (list_empty(&ima_measurements)) {
> > +           ret = -ENOENT;
> > +           goto out_unlock;
> > +   }
> > +
> > +   list_replace(&ima_measurements, &ima_measurements_staged);
> > +   INIT_LIST_HEAD(&ima_measurements);
> > +
> > +   atomic_long_set(&ima_num_entries[BINARY_STAGED],
> > +                   atomic_long_read(&ima_num_entries[BINARY]));
> > +   atomic_long_set(&ima_num_entries[BINARY], 0);
> > +
> > +   if (IS_ENABLED(CONFIG_IMA_KEXEC)) {
> > +           binary_runtime_size[BINARY_STAGED] =
> > +                                   binary_runtime_size[BINARY];
> > +           binary_runtime_size[BINARY] = 0;
> > +   }
> > +out_unlock:
> > +   mutex_unlock(&ima_extend_list_mutex);
> > +   return ret;
> > +}
> > +
> > +static void ima_queue_delete(struct list_head *head);
> > +
> > +int ima_queue_staged_delete_all(void)
> > +{
> > +   LIST_HEAD(ima_measurements_trim);
> > +
> > +   mutex_lock(&ima_extend_list_mutex);
> > +   if (list_empty(&ima_measurements_staged)) {
> > +           mutex_unlock(&ima_extend_list_mutex);
> > +           return -ENOENT;
> > +   }
> > +
> > +   list_replace(&ima_measurements_staged, &ima_measurements_trim);
> > +   INIT_LIST_HEAD(&ima_measurements_staged);
> > +
> > +   atomic_long_set(&ima_num_entries[BINARY_STAGED], 0);
> > +
> > +   if (IS_ENABLED(CONFIG_IMA_KEXEC))
> > +           binary_runtime_size[BINARY_STAGED] = 0;
> > +
> > +   mutex_unlock(&ima_extend_list_mutex);
> > +
> > +   ima_queue_delete(&ima_measurements_trim);
> > +   return 0;
> > +}
> > +
> > +static void ima_queue_delete(struct list_head *head)
> > +{
> > +   struct ima_queue_entry *qe, *qe_tmp;
> > +   unsigned int i;
> > +
> > +   list_for_each_entry_safe(qe, qe_tmp, head, later) {
> > +           /*
> > +            * Safe to free template_data here without synchronize_rcu()
> > +            * because the only htable reader, ima_lookup_digest_entry(),
> > +            * accesses only entry->digests, not template_data. If new
> > +            * htable readers are added that access template_data, a
> > +            * synchronize_rcu() is required here.
> > +            */
> > +           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);
> > +
> > +           /* No leak if condition is false, referenced by ima_htable. */
> > +           if (IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
> > +                   kfree(qe->entry->digests);
> > +                   kfree(qe->entry);
> > +                   kfree(qe);
> > +           }
> > +   }
> > +}
> > +
> >   int ima_restore_measurement_entry(struct ima_template_entry *entry)
> >   {
> >     int result = 0;
> 


Reply via email to