[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <b701686d-212e-4152-9db9-0c56f21e1fdc@linux.microsoft.com>
Date: Thu, 11 Dec 2025 11:20:40 -0800
From: steven chen <chenste@...ux.microsoft.com>
To: Roberto Sassu <roberto.sassu@...weicloud.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 12/11/2025 2:18 AM, Roberto Sassu wrote:
> 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
Hi Roberto,
what does this staging solution do that's not achieved by trim N entries
solution?
You did not address all my comments and your other idea make things more
complex.
Also, Trim N solution is simple and will bring following two good points:
easy for future IMA development
easy for code maintenance
Could you also add your comments on the trim N solution?
Thanks,
Steven
>
>> 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