[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <d6ef2d61a2c31c0ae46741b6bd78f38bc02e6141.camel@huaweicloud.com>
Date: Thu, 11 Dec 2025 11:18:47 +0100
From: Roberto Sassu <roberto.sassu@...weicloud.com>
To: steven chen <chenste@...ux.microsoft.com>, corbet@....net,
zohar@...ux.ibm.com, dmitry.kasatkin@...il.com, eric.snowberg@...cle.com,
paul@...l-moore.com, jmorris@...ei.org, serge@...lyn.com
Cc: linux-doc@...r.kernel.org, linux-kernel@...r.kernel.org,
linux-integrity@...r.kernel.org, linux-security-module@...r.kernel.org,
gregorylumen@...ux.microsoft.com, nramas@...ux.microsoft.com, Roberto Sassu
<roberto.sassu@...wei.com>
Subject: Re: [RFC][PATCH] ima: Add support for staging measurements for
deletion
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 <roberto.sassu@...wei.com>
> >
> > 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/20251210235314.3341-1-chenste@linux.microsoft.com/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 <roberto.sassu@...wei.com>
> > ---
> > .../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;
>
Powered by blists - more mailing lists