* xfs/ima: Regression caching i_version
From: Frederick Lawler @ 2025-12-11 20:29 UTC (permalink / raw)
To: Jeff Layton
Cc: Christian Brauner, Darrick J. Wong, Josef Bacik, Carlos Maiolino,
linux-xfs, linux-kernel, linux-security-module, linux-integrity,
Roberto Sassu, kernel-team
Hi Jeff,
While testing 6.18, I think I found a regression with
commit 1cf7e834a6fb ("xfs: switch to multigrain timestamps") since 6.13
where IMA is no longer able to properly cache i_version when we overlay
tmpfs on top of XFS. Each measurement diff check in function
process_measurement() reports that the i_version is
always set to zero for iint->real_inode.version.
The function ima_collect_measurement() is looking to extract the version
from the cookie on next measurement to cache i_version.
I'm unclear from the commit description what the right approach here is:
update in IMA land by checking for time changes, or do
something else such as adding the cookie back.
Thanks,
Fred
^ permalink raw reply
* Re: [RFC 08/11] security: Hornet LSM
From: Randy Dunlap @ 2025-12-11 20:07 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
linux-security-module, linux-doc, linux-kernel, bpf
In-Reply-To: <20251211021257.1208712-9-bboscaccy@linux.microsoft.com>
On 12/10/25 6:12 PM, Blaise Boscaccy wrote:
> diff --git a/Documentation/admin-guide/LSM/Hornet.rst b/Documentation/admin-guide/LSM/Hornet.rst
> new file mode 100644
> index 0000000000000..0fb5920e9b68f
> --- /dev/null
> +++ b/Documentation/admin-guide/LSM/Hornet.rst
> @@ -0,0 +1,38 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +======
> +Hornet
> +======
> +
> +Hornet is a Linux Security Module that provides extensible signature
> +verification for eBPF programs. This is selectable at build-time with
> +``CONFIG_SECURITY_HORNET``.
> +
> +Overview
> +========
> +
> +Hornet addresses concerns from users who require strict audit
> +trails and verification guarantees, especially in security-sensitive
> +environments. Map hashes for extended verification are passed in via
> +the existing PKCS#7 uapi and verifified by the crypto
verified
and preferably UAPI
> +subsystem. Hornet then calculates the verification state of the
> +program (full, partial, bad, etc) and then invokes a new downstream
etc.)
> +LSM hook to delegate policy decisions.
> +
> +Tooling
> +=======
> +
> +Some tooling is provided to aid with the development of signed eBPF
> +light-skeletons.
> +
> +extract-skel.sh
> +---------------
> +
> +This shell script extracts the instructions and map data used by the
> +light skeleton from the autogenerated header file created by bpftool.
> +
> +gen_sig
> +---------
> +
> +gen_sig creates a pkcs#7 signature of a data payload. Additionally it
> +appends a signed attribute containing a set of hashes.
--
~Randy
^ permalink raw reply
* Re: [RFC][PATCH] ima: Add support for staging measurements for deletion
From: steven chen @ 2025-12-11 19:47 UTC (permalink / raw)
To: Roberto Sassu, Gregory Lumen
Cc: corbet, zohar, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-doc, linux-kernel, linux-integrity,
linux-security-module, nramas, Roberto Sassu, steven chen
In-Reply-To: <d7418d0afa696b8da67e4f25fd0dc1b9d6fd908f.camel@huaweicloud.com>
On 12/11/2025 1:56 AM, Roberto Sassu wrote:
> On Wed, 2025-12-10 at 11:12 -0800, Gregory Lumen wrote:
>> Roberto,
>>
>> The proposed approach appears to be workable. However, if our primary goal
>> here is to enable UM to free kernel memory consumed by the IMA log with an
>> absolute minimum of kernel functionality/change, then I would argue that
>> the proposed Stage-then-delete approach still represents unnecessary
>> complexity when compared to a trim-to-N solution. Specifically:
>>
>> - Any functional benefit offered through the introduction of a staged
>> measurement list could be equally achieved in UM with a trim-to-N solution
>> coupled with the proposed ima_measure_users counter for access locking.
> Ok, let's quantify the complexity of each solution. Let's assume that
> the IMA measurements list has M entries and you want to trim at N < M.
>
> Staging:
>
> 1st. trim at N
>
> (kernel)
> 1. list lock (write side) -> list replace (swap the heads) -> list unlock
> 2. read M -> file (file contains 0..M)
> 3. for each 0..M -> delete entry
>
> (user)
> 1. for each 0..N in file -> replay PCR
> 2. trim at N (keep N + 1..M)
>
>
> 2nd. trim at O
>
> (kernel)
> 1. list lock -> list replace (swap the heads) -> list unlock
> 2. read P -> file (file contains N + 1..P)
> 3. for each M + 1..P -> delete entry
>
> (user)
> 1. for each N + 1..O in file -> replay PCR
> 2. trim at O (keep O + 1..P)
>
>
>
> Trimming:
>
> 1st. trim at N
>
> (kernel)
> 1. list lock (read side) -> for each 0..M -> read in file (file now contains 0..M) -> list unlock
>
> (user)
> 1. for each 0..N -> replay PCR
> 2. discard N + 1..M
>
> (kernel)
>
> 1. list lock (write side) -> for each 0..N -> trim -> list unlock
>
>
> 2nd. trim at O
>
> (kernel)
> 1. list lock (read side) -> for each N + 1..P -> read in file (file now contains N + 1..P) -> list unlock
>
> (user)
> 1. for each N + 1..O -> replay PCR
> 2. discard O + 1..P
>
> (kernel)
>
> 1. list lock (write side) -> for each N + 1..O -> trim -> list unlock
>
>
> You can try to optimize it a bit by prematurely ending the reading
> before M and P, and by replaying the PCR on a partial buffer.
>
>
> But still:
>
> I just swap list heads in the hot path (still need to do the same for
> the hash table, postponed to later), and do the free later once there
> is no contention with new measurements.
>
> In your case you are taking the lock and walking the list two times,
> once as a reader and once as a writer, and discarding measurements in
> user space that you already have.
>
> I think your solution is more complex.
This is not the case, please check the 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>
The following are the steps for trim N solution:
User space reads list without lock
User space decides to trim N entries and send command to kernel
Kernel will lock the list use the same or less time as staged
solution use
All work done.
>
>> - There exists a potential UM measurement-loss race condition introduced
>> by the staging functionality that would not exist with a trim-to-N
>> approach. (Occurs if a kexec call occurs after a UM agent has staged
>> measurements for deletion, but has not completed copying them to
>> userspace). This could be avoided by persisting staged measurements across
>> kexec calls at the cost of making the proposed change larger.
> The solution is to coordinate the staging with kexec in user space.
>
>
> Roberto
^ permalink raw reply
* Re: [RFC][PATCH] ima: Add support for staging measurements for deletion
From: steven chen @ 2025-12-11 19:20 UTC (permalink / raw)
To: Roberto Sassu, corbet, zohar, dmitry.kasatkin, eric.snowberg,
paul, jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu
In-Reply-To: <d6ef2d61a2c31c0ae46741b6bd78f38bc02e6141.camel@huaweicloud.com>
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@huawei.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@huawei.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;
^ permalink raw reply
* Re: [PATCH V2 1/1] IMA event log trimming
From: steven chen @ 2025-12-11 18:41 UTC (permalink / raw)
To: linux-integrity
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, corbet,
serge, paul, jmorris, linux-security-module, anirudhve,
gregorylumen, nramas, sushring, linux-doc, steven chen
In-Reply-To: <20251210235314.3341-2-chenste@linux.microsoft.com>
On 12/10/2025 3:53 PM, steven chen wrote:
> This patch is for trimming N entries of the IMA event logs. It will also
> cleaning the hash table if ima_flush_htable is set.
>
> It provides a userspace interface ima_trim_log that can be used to input
> number N to let kernel to trim N entries of IMA event logs. When read
> this interface, it returns number of entries trimmed last time.
>
> Signed-off-by: steven chen <chenste@linux.microsoft.com>
> ---
> .../admin-guide/kernel-parameters.txt | 4 +
> security/integrity/ima/ima.h | 2 +
> security/integrity/ima/ima_fs.c | 175 +++++++++++++++++-
> security/integrity/ima/ima_queue.c | 64 +++++++
> 4 files changed, 241 insertions(+), 4 deletions(-)
>
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index e92c0056e4e0..cd1a1d0bf0e2 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -2197,6 +2197,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 trim 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..ab0e30ee25ea 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
>
> #ifdef CONFIG_IMA_KEXEC
> void ima_measure_kexec_event(const char *event_name);
> +long ima_purge_event_log(long number_logs);
> #else
> static inline void ima_measure_kexec_event(const char *event_name) {}
> +static inline long ima_purge_event_log(long number_logs) { return 0; }
> #endif
>
> /*
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 87045b09f120..410f7d03c43f 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -21,6 +21,9 @@
> #include <linux/rcupdate.h>
> #include <linux/parser.h>
> #include <linux/vmalloc.h>
> +#include <linux/ktime.h>
> +#include <linux/timekeeping.h>
> +#include <linux/ima.h>
>
> #include "ima.h"
>
> @@ -38,6 +41,14 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>
> static int valid_policy = 1;
>
> +#define IMA_LOG_TRIM_REQ_LENGTH 11
> +#define IMA_LOG_TRIM_EVENT_LEN 256
> +
> +static long trimcount;
> +/* mutex protects atomicity of trimming measurement list requests */
> +static DEFINE_MUTEX(ima_measure_lock);
> +static long ima_measure_users;
> +
> static ssize_t ima_show_htable_value(char __user *buf, size_t count,
> loff_t *ppos, atomic_long_t *val)
> {
> @@ -202,16 +213,65 @@ 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,
> };
>
> void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
> @@ -279,14 +339,111 @@ 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 void ima_measure_trim_event(const long number_logs)
> +{
> + char ima_log_trim_event[IMA_LOG_TRIM_EVENT_LEN];
> + struct timespec64 ts;
> + u64 time_ns;
> + int n;
> +
> + ktime_get_real_ts64(&ts);
> + time_ns = (u64)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
> + n = scnprintf(ima_log_trim_event, IMA_LOG_TRIM_EVENT_LEN,
> + "time=%llu; log trim this time=%lu;",
> + time_ns, number_logs);
> +
> + ima_measure_critical_data("ima_log_trim", "trim ima event logs", ima_log_trim_event, n, false, NULL, 0);
> +}
> +
> +static int ima_log_trim_open(struct inode *inode, struct file *file)
> +{
> + bool write = !!(file->f_mode & FMODE_WRITE);
> +
> + if (!write && capable(CAP_SYS_ADMIN))
> + return 0;
> + else if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> +
> + return _ima_measurements_open(inode, file, &ima_measurments_seqops);
> +}
> +
> +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
> +{
> + char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH]; /* greater than largest 'long' string value */
> + ssize_t len;
> +
> + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
> + return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
> +}
> +
> +static ssize_t ima_log_trim_write(struct file *file,
> + const char __user *buf, size_t datalen, loff_t *ppos)
> +{
> + unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
> + long count, n;
> + int ret;
> +
> + if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + n = (int)datalen;
> +
> + ret = copy_from_user(req, buf, datalen);
> + if (ret < 0)
> + goto out;
> +
> + count = 0;
> + for (int i = 0; i < n; ++i) {
> + if (req[i] < '0' || req[i] > '9') {
> + ret = -EINVAL;
> + goto out;
> + }
> + count = count * 10 + req[i] - '0';
> + }
> + ret = ima_purge_event_log(count);
> +
> + if (ret < 0)
> + goto out;
> +
> + trimcount = ret;
> +
> + if (trimcount > 0)
> + ima_measure_trim_event(trimcount);
> +
> + ret = datalen;
> +out:
> + return ret;
> +}
> +
> +static int ima_log_trim_release(struct inode *inode, struct file *file)
> +{
> + bool write = !!(file->f_mode & FMODE_WRITE);
> + if (!write && capable(CAP_SYS_ADMIN))
> + return 0;
> + else if (!capable(CAP_SYS_ADMIN))
> + return -EPERM;
> +
> + return ima_measurements_release(inode, file);
> +}
> +
> +static const struct file_operations ima_log_trim_ops = {
> + .open = ima_log_trim_open,
> + .read = ima_log_trim_read,
> + .write = ima_log_trim_write,
> + .llseek = generic_file_llseek,
> + .release = ima_log_trim_release
> };
>
> static ssize_t ima_read_policy(char *path)
> @@ -528,6 +685,16 @@ int __init ima_fs_init(void)
> goto out;
> }
>
> + dentry = securityfs_create_file("ima_trim_log",
> + S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
> + ima_dir, NULL, &ima_log_trim_ops);
> + if (IS_ERR(dentry)) {
> + ret = PTR_ERR(dentry);
> + goto out;
> + }
> +
> + trimcount = 0;
> +
> 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_queue.c b/security/integrity/ima/ima_queue.c
> index 590637e81ad1..77ab52469727 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -22,6 +22,14 @@
>
> #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;
>
> @@ -220,6 +228,62 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
> return result;
> }
>
> +/* Delete the IMA event logs */
> +long ima_purge_event_log(long number_logs)
> +{
> + struct ima_queue_entry *qe, *qe_tmp;
> + LIST_HEAD(ima_measurements_staged);
> + unsigned int i;
> + long cur = number_logs;
> +
> + if (number_logs <= 0)
> + return number_logs;
> +
> + mutex_lock(&ima_extend_list_mutex);
> +
> +
> + list_for_each_entry(qe, &ima_measurements, later) {
> + if (--number_logs == 0)
> + break;
> + }
> +
> + if (number_logs > 0) {
> + mutex_unlock(&ima_extend_list_mutex);
> + return -ENOENT;
> + }
> +
> + __list_cut_position(&ima_measurements_staged, &ima_measurements,
> + &qe->later);
> + atomic_long_sub(cur, &ima_htable.len);
> +
> + if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) && 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);
> + }
If the h table can be staged during the locking period and deleted after
unlocking, the time
the list is held will be reduced.
I will work on this, and any suggestions are greatly appreciated.
Thanks,
Steven
> +
> + mutex_unlock(&ima_extend_list_mutex);
> +
> +
> + 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);
> + }
> + }
> +
> + return cur;
> +}
> +
> int ima_restore_measurement_entry(struct ima_template_entry *entry)
> {
> int result = 0;
^ permalink raw reply
* Re: [PATCH V2 1/1] IMA event log trimming
From: steven chen @ 2025-12-11 18:20 UTC (permalink / raw)
To: Roberto Sassu, linux-integrity
Cc: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg, corbet,
serge, paul, jmorris, linux-security-module, anirudhve,
gregorylumen, nramas, sushring, linux-doc, steven chen
In-Reply-To: <561786c2505cae94913809ac0cf3d916e1a50a68.camel@huaweicloud.com>
On 12/11/2025 2:14 AM, Roberto Sassu wrote:
> On Wed, 2025-12-10 at 15:53 -0800, steven chen wrote:
>> This patch is for trimming N entries of the IMA event logs. It will also
>> cleaning the hash table if ima_flush_htable is set.
>>
>> It provides a userspace interface ima_trim_log that can be used to input
>> number N to let kernel to trim N entries of IMA event logs. When read
>> this interface, it returns number of entries trimmed last time.
> I was trying to find a common solution for both approaches, and instead
> you took part of my code and removed the part that you didn't like.
> This is not nice.
Hi Roberto,
The most important is one step trimming N entries and the N come from
user space. Everything
else are helping this target. We need to bring the best to the open
source community and
I acknowledged the source.
Thanks,
Steven
> Let's do the following. Please review my patch, and check if it makes
> you achieve your goal. Then, please send follow-up patches with the
> additional functionality you need (e.g. the new critical data
> measurement entry).
>
> Thanks
>
> Roberto
>
>> Signed-off-by: steven chen <chenste@linux.microsoft.com>
>> ---
>> .../admin-guide/kernel-parameters.txt | 4 +
>> security/integrity/ima/ima.h | 2 +
>> security/integrity/ima/ima_fs.c | 175 +++++++++++++++++-
>> security/integrity/ima/ima_queue.c | 64 +++++++
>> 4 files changed, 241 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
>> index e92c0056e4e0..cd1a1d0bf0e2 100644
>> --- a/Documentation/admin-guide/kernel-parameters.txt
>> +++ b/Documentation/admin-guide/kernel-parameters.txt
>> @@ -2197,6 +2197,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 trim 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..ab0e30ee25ea 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -246,8 +246,10 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
>>
>> #ifdef CONFIG_IMA_KEXEC
>> void ima_measure_kexec_event(const char *event_name);
>> +long ima_purge_event_log(long number_logs);
>> #else
>> static inline void ima_measure_kexec_event(const char *event_name) {}
>> +static inline long ima_purge_event_log(long number_logs) { return 0; }
>> #endif
>>
>> /*
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 87045b09f120..410f7d03c43f 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -21,6 +21,9 @@
>> #include <linux/rcupdate.h>
>> #include <linux/parser.h>
>> #include <linux/vmalloc.h>
>> +#include <linux/ktime.h>
>> +#include <linux/timekeeping.h>
>> +#include <linux/ima.h>
>>
>> #include "ima.h"
>>
>> @@ -38,6 +41,14 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>>
>> static int valid_policy = 1;
>>
>> +#define IMA_LOG_TRIM_REQ_LENGTH 11
>> +#define IMA_LOG_TRIM_EVENT_LEN 256
>> +
>> +static long trimcount;
>> +/* mutex protects atomicity of trimming measurement list requests */
>> +static DEFINE_MUTEX(ima_measure_lock);
>> +static long ima_measure_users;
>> +
>> static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>> loff_t *ppos, atomic_long_t *val)
>> {
>> @@ -202,16 +213,65 @@ 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,
>> };
>>
>> void ima_print_digest(struct seq_file *m, u8 *digest, u32 size)
>> @@ -279,14 +339,111 @@ 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 void ima_measure_trim_event(const long number_logs)
>> +{
>> + char ima_log_trim_event[IMA_LOG_TRIM_EVENT_LEN];
>> + struct timespec64 ts;
>> + u64 time_ns;
>> + int n;
>> +
>> + ktime_get_real_ts64(&ts);
>> + time_ns = (u64)ts.tv_sec * 1000000000ULL + ts.tv_nsec;
>> + n = scnprintf(ima_log_trim_event, IMA_LOG_TRIM_EVENT_LEN,
>> + "time=%llu; log trim this time=%lu;",
>> + time_ns, number_logs);
>> +
>> + ima_measure_critical_data("ima_log_trim", "trim ima event logs", ima_log_trim_event, n, false, NULL, 0);
>> +}
>> +
>> +static int ima_log_trim_open(struct inode *inode, struct file *file)
>> +{
>> + bool write = !!(file->f_mode & FMODE_WRITE);
>> +
>> + if (!write && capable(CAP_SYS_ADMIN))
>> + return 0;
>> + else if (!capable(CAP_SYS_ADMIN))
>> + return -EPERM;
>> +
>> + return _ima_measurements_open(inode, file, &ima_measurments_seqops);
>> +}
>> +
>> +static ssize_t ima_log_trim_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
>> +{
>> + char tmpbuf[IMA_LOG_TRIM_REQ_LENGTH]; /* greater than largest 'long' string value */
>> + ssize_t len;
>> +
>> + len = scnprintf(tmpbuf, sizeof(tmpbuf), "%li\n", trimcount);
>> + return simple_read_from_buffer(buf, size, ppos, tmpbuf, len);
>> +}
>> +
>> +static ssize_t ima_log_trim_write(struct file *file,
>> + const char __user *buf, size_t datalen, loff_t *ppos)
>> +{
>> + unsigned char req[IMA_LOG_TRIM_REQ_LENGTH];
>> + long count, n;
>> + int ret;
>> +
>> + if (*ppos > 0 || datalen > IMA_LOG_TRIM_REQ_LENGTH || datalen < 2) {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> +
>> + n = (int)datalen;
>> +
>> + ret = copy_from_user(req, buf, datalen);
>> + if (ret < 0)
>> + goto out;
>> +
>> + count = 0;
>> + for (int i = 0; i < n; ++i) {
>> + if (req[i] < '0' || req[i] > '9') {
>> + ret = -EINVAL;
>> + goto out;
>> + }
>> + count = count * 10 + req[i] - '0';
>> + }
>> + ret = ima_purge_event_log(count);
>> +
>> + if (ret < 0)
>> + goto out;
>> +
>> + trimcount = ret;
>> +
>> + if (trimcount > 0)
>> + ima_measure_trim_event(trimcount);
>> +
>> + ret = datalen;
>> +out:
>> + return ret;
>> +}
>> +
>> +static int ima_log_trim_release(struct inode *inode, struct file *file)
>> +{
>> + bool write = !!(file->f_mode & FMODE_WRITE);
>> + if (!write && capable(CAP_SYS_ADMIN))
>> + return 0;
>> + else if (!capable(CAP_SYS_ADMIN))
>> + return -EPERM;
>> +
>> + return ima_measurements_release(inode, file);
>> +}
>> +
>> +static const struct file_operations ima_log_trim_ops = {
>> + .open = ima_log_trim_open,
>> + .read = ima_log_trim_read,
>> + .write = ima_log_trim_write,
>> + .llseek = generic_file_llseek,
>> + .release = ima_log_trim_release
>> };
>>
>> static ssize_t ima_read_policy(char *path)
>> @@ -528,6 +685,16 @@ int __init ima_fs_init(void)
>> goto out;
>> }
>>
>> + dentry = securityfs_create_file("ima_trim_log",
>> + S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP,
>> + ima_dir, NULL, &ima_log_trim_ops);
>> + if (IS_ERR(dentry)) {
>> + ret = PTR_ERR(dentry);
>> + goto out;
>> + }
>> +
>> + trimcount = 0;
>> +
>> 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_queue.c b/security/integrity/ima/ima_queue.c
>> index 590637e81ad1..77ab52469727 100644
>> --- a/security/integrity/ima/ima_queue.c
>> +++ b/security/integrity/ima/ima_queue.c
>> @@ -22,6 +22,14 @@
>>
>> #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;
>>
>> @@ -220,6 +228,62 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
>> return result;
>> }
>>
>> +/* Delete the IMA event logs */
>> +long ima_purge_event_log(long number_logs)
>> +{
>> + struct ima_queue_entry *qe, *qe_tmp;
>> + LIST_HEAD(ima_measurements_staged);
>> + unsigned int i;
>> + long cur = number_logs;
>> +
>> + if (number_logs <= 0)
>> + return number_logs;
>> +
>> + mutex_lock(&ima_extend_list_mutex);
>> +
>> +
>> + list_for_each_entry(qe, &ima_measurements, later) {
>> + if (--number_logs == 0)
>> + break;
>> + }
>> +
>> + if (number_logs > 0) {
>> + mutex_unlock(&ima_extend_list_mutex);
>> + return -ENOENT;
>> + }
>> +
>> + __list_cut_position(&ima_measurements_staged, &ima_measurements,
>> + &qe->later);
>> + atomic_long_sub(cur, &ima_htable.len);
>> +
>> + if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE) && 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);
>> +
>> +
>> + 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);
>> + }
>> + }
>> +
>> + return cur;
>> +}
>> +
>> int ima_restore_measurement_entry(struct ima_template_entry *entry)
>> {
>> int result = 0;
^ permalink raw reply
* Re: [RFC][PATCH] ima: Add support for staging measurements for deletion
From: steven chen @ 2025-12-11 18:06 UTC (permalink / raw)
To: Roberto Sassu, Gregory Lumen
Cc: corbet, zohar, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-doc, linux-kernel, linux-integrity,
linux-security-module, nramas, Roberto Sassu, steven chen
In-Reply-To: <75d8b82a2e493ca919926310c5f381221555d82d.camel@huaweicloud.com>
On 12/11/2025 7:24 AM, Roberto Sassu wrote:
> On Thu, 2025-12-11 at 15:50 +0100, Roberto Sassu wrote:
>> On Thu, 2025-12-11 at 10:56 +0100, Roberto Sassu wrote:
>>> On Wed, 2025-12-10 at 11:12 -0800, Gregory Lumen wrote:
>>>> Roberto,
>>>>
>>>> The proposed approach appears to be workable. However, if our primary goal
>>>> here is to enable UM to free kernel memory consumed by the IMA log with an
>>>> absolute minimum of kernel functionality/change, then I would argue that
>>>> the proposed Stage-then-delete approach still represents unnecessary
>>>> complexity when compared to a trim-to-N solution. Specifically:
>> The benefit of the Stage-then-delete is that you don't need to scan the
>> IMA measurements list in advance to determine what to trim, you just
>> trim everything by swapping list head (very fast) and then you can read
>> and delete the measurements out of the hot path.
> I forgot: I will also add in my patch the ability to stage and trim in
> one step, to satisfy your use case.
>
> Roberto
Hi Roberto,
The below is what you want in one step. I think anything more than this
does not bring any extra value.
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>
Steven
>> [...]
>>
>>>> - There exists a potential UM measurement-loss race condition introduced
>>>> by the staging functionality that would not exist with a trim-to-N
>>>> approach. (Occurs if a kexec call occurs after a UM agent has staged
>>>> measurements for deletion, but has not completed copying them to
>>>> userspace). This could be avoided by persisting staged measurements across
>>>> kexec calls at the cost of making the proposed change larger.
>>> The solution is to coordinate the staging with kexec in user space.
>> To avoid requiring coordination in user space, I will try to see if I
>> could improve my patch to prepend the staged entries to the current
>> measurement list, before serializing them for kexec().
>>
>> Roberto
^ permalink raw reply
* [PATCH v1 15/17] KEYS: trusted: Make use of tee bus methods
From: Uwe Kleine-König @ 2025-12-11 17:15 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, James Bottomley, Jarkko Sakkinen,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn
Cc: op-tee, linux-integrity, keyrings, linux-security-module,
linux-kernel
In-Reply-To: <cover.1765472125.git.u.kleine-koenig@baylibre.com>
The tee bus got dedicated callbacks for probe and remove.
Make use of these. This fixes a runtime warning about the driver needing
to be converted to the bus methods.
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
---
security/keys/trusted-keys/trusted_tee.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c
index 3cea9a377955..6e465c8bef5e 100644
--- a/security/keys/trusted-keys/trusted_tee.c
+++ b/security/keys/trusted-keys/trusted_tee.c
@@ -202,9 +202,9 @@ static int optee_ctx_match(struct tee_ioctl_version_data *ver, const void *data)
return 0;
}
-static int trusted_key_probe(struct device *dev)
+static int trusted_key_probe(struct tee_client_device *rng_device)
{
- struct tee_client_device *rng_device = to_tee_client_device(dev);
+ struct device *dev = &rng_device->dev;
int ret;
struct tee_ioctl_open_session_arg sess_arg;
@@ -244,13 +244,11 @@ static int trusted_key_probe(struct device *dev)
return ret;
}
-static int trusted_key_remove(struct device *dev)
+static void trusted_key_remove(struct tee_client_device *dev)
{
unregister_key_type(&key_type_trusted);
tee_client_close_session(pvt_data.ctx, pvt_data.session_id);
tee_client_close_context(pvt_data.ctx);
-
- return 0;
}
static const struct tee_client_device_id trusted_key_id_table[] = {
@@ -261,11 +259,11 @@ static const struct tee_client_device_id trusted_key_id_table[] = {
MODULE_DEVICE_TABLE(tee, trusted_key_id_table);
static struct tee_client_driver trusted_key_driver = {
+ .probe = trusted_key_probe,
+ .remove = trusted_key_remove,
.id_table = trusted_key_id_table,
.driver = {
.name = DRIVER_NAME,
- .probe = trusted_key_probe,
- .remove = trusted_key_remove,
},
};
--
2.47.3
^ permalink raw reply related
* [PATCH v1 14/17] KEYS: trusted: Migrate to use tee specific driver registration function
From: Uwe Kleine-König @ 2025-12-11 17:15 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, James Bottomley, Jarkko Sakkinen,
Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn
Cc: op-tee, linux-integrity, keyrings, linux-security-module,
linux-kernel
In-Reply-To: <cover.1765472125.git.u.kleine-koenig@baylibre.com>
The tee subsystem recently got a set of dedicated functions to register
(and unregister) a tee driver. Make use of them. These care for setting the
driver's bus (so the explicit assignment can be dropped) and the driver
owner (which is an improvement this driver benefits from).
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
---
security/keys/trusted-keys/trusted_tee.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/security/keys/trusted-keys/trusted_tee.c b/security/keys/trusted-keys/trusted_tee.c
index aa3d477de6db..3cea9a377955 100644
--- a/security/keys/trusted-keys/trusted_tee.c
+++ b/security/keys/trusted-keys/trusted_tee.c
@@ -264,7 +264,6 @@ static struct tee_client_driver trusted_key_driver = {
.id_table = trusted_key_id_table,
.driver = {
.name = DRIVER_NAME,
- .bus = &tee_bus_type,
.probe = trusted_key_probe,
.remove = trusted_key_remove,
},
@@ -272,12 +271,12 @@ static struct tee_client_driver trusted_key_driver = {
static int trusted_tee_init(void)
{
- return driver_register(&trusted_key_driver.driver);
+ return tee_client_driver_register(&trusted_key_driver);
}
static void trusted_tee_exit(void)
{
- driver_unregister(&trusted_key_driver.driver);
+ tee_client_driver_unregister(&trusted_key_driver);
}
struct trusted_key_ops trusted_key_tee_ops = {
--
2.47.3
^ permalink raw reply related
* [PATCH v1 00/17] tee: Use bus callbacks instead of driver callbacks
From: Uwe Kleine-König @ 2025-12-11 17:14 UTC (permalink / raw)
To: Jens Wiklander, Sumit Garg, Olivia Mackall, Herbert Xu,
Clément Léger, Alexandre Belloni, Ard Biesheuvel,
Maxime Coquelin, Alexandre Torgue, Sumit Garg, Ilias Apalodimas,
Jan Kiszka, Sudeep Holla, Christophe JAILLET, Michael Chan,
Pavan Chebbi, Rafał Miłecki, James Bottomley,
Jarkko Sakkinen, Mimi Zohar, David Howells, Paul Moore,
James Morris, Serge E. Hallyn, Peter Huewe
Cc: op-tee, linux-kernel, linux-crypto, linux-rtc, linux-efi,
linux-stm32, linux-arm-kernel, Cristian Marussi, arm-scmi, netdev,
linux-mips, linux-integrity, keyrings, linux-security-module,
Jason Gunthorpe
Hello,
the objective of this series is to make tee driver stop using callbacks
in struct device_driver. These were superseded by bus methods in 2006
(commit 594c8281f905 ("[PATCH] Add bus_type probe, remove, shutdown
methods.")) but nobody cared to convert all subsystems accordingly.
Here the tee drivers are converted. The first commit is somewhat
unrelated, but simplifies the conversion (and the drivers). It
introduces driver registration helpers that care about setting the bus
and owner. (The latter is missing in all drivers, so by using these
helpers the drivers become more correct.)
The patches #4 - #17 depend on the first two, so if they should be
applied to their respective subsystem trees these must contain the first
two patches first.
Note that after patch #2 is applied, unconverted drivers provoke a
warning in driver_register(), so it would be good for the user
experience if the whole series goes in during a single merge window. So
I guess an immutable branch containing the frist three patches that can
be merged into the other subsystem trees would be sensible.
After all patches are applied, tee_bus_type can be made private to
drivers/tee as it's not used in other places any more.
Best regards
Uwe
Uwe Kleine-König (17):
tee: Add some helpers to reduce boilerplate for tee client drivers
tee: Add probe, remove and shutdown bus callbacks to tee_client_driver
tee: Adapt documentation to cover recent additions
hwrng: optee - Make use of module_tee_client_driver()
hwrng: optee - Make use of tee bus methods
rtc: optee: Migrate to use tee specific driver registration function
rtc: optee: Make use of tee bus methods
efi: stmm: Make use of module_tee_client_driver()
efi: stmm: Make use of tee bus methods
firmware: arm_scmi: optee: Make use of module_tee_client_driver()
firmware: arm_scmi: Make use of tee bus methods
firmware: tee_bnxt: Make use of module_tee_client_driver()
firmware: tee_bnxt: Make use of tee bus methods
KEYS: trusted: Migrate to use tee specific driver registration
function
KEYS: trusted: Make use of tee bus methods
tpm/tpm_ftpm_tee: Make use of tee specific driver registration
tpm/tpm_ftpm_tee: Make use of tee bus methods
Documentation/driver-api/tee.rst | 18 +----
drivers/char/hw_random/optee-rng.c | 26 ++----
drivers/char/tpm/tpm_ftpm_tee.c | 31 +++++---
drivers/firmware/arm_scmi/transports/optee.c | 32 +++-----
drivers/firmware/broadcom/tee_bnxt_fw.c | 30 ++-----
drivers/firmware/efi/stmm/tee_stmm_efi.c | 25 ++----
drivers/rtc/rtc-optee.c | 27 ++-----
drivers/tee/tee_core.c | 84 ++++++++++++++++++++
include/linux/tee_drv.h | 12 +++
security/keys/trusted-keys/trusted_tee.c | 17 ++--
10 files changed, 164 insertions(+), 138 deletions(-)
base-commit: 7d0a66e4bb9081d75c82ec4957c50034cb0ea449
--
2.47.3
^ permalink raw reply
* Re: [RFC 06/11] crypto: pkcs7: add ability to extract signed attributes by OID
From: Randy Dunlap @ 2025-12-11 16:44 UTC (permalink / raw)
To: Blaise Boscaccy, Jonathan Corbet, Paul Moore, James Morris,
Serge E. Hallyn, Mickaël Salaün, Günther Noack,
Dr. David Alan Gilbert, Andrew Morton, James.Bottomley, dhowells,
linux-security-module, linux-doc, linux-kernel, bpf
In-Reply-To: <20251211021257.1208712-7-bboscaccy@linux.microsoft.com>
On 12/10/25 6:12 PM, Blaise Boscaccy wrote:
> From: James Bottomley <James.Bottomley@HansenPartnership.com>
>
> Signers may add any information they like in signed attributes and
> sometimes this information turns out to be relevant to specific
> signing cases, so add an api pkcs7_get_authattr() to extract the value
> of an authenticated attribute by specific OID. The current
> implementation is designed for the single signer use case and simply
> terminates the search when it finds the relevant OID.
>
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> ---
> crypto/asymmetric_keys/Makefile | 4 +-
> crypto/asymmetric_keys/pkcs7_aa.asn1 | 18 ++++++
> crypto/asymmetric_keys/pkcs7_parser.c | 87 +++++++++++++++++++++++++++
> include/crypto/pkcs7.h | 4 ++
> 4 files changed, 112 insertions(+), 1 deletion(-)
> create mode 100644 crypto/asymmetric_keys/pkcs7_aa.asn1
Hi,
Your patches from James, Paul, etc., are missing your
Signed-off-by: line.
--
~Randy
^ permalink raw reply
* Re: [RFC][PATCH] ima: Add support for staging measurements for deletion
From: Roberto Sassu @ 2025-12-11 15:24 UTC (permalink / raw)
To: Gregory Lumen
Cc: corbet, zohar, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-doc, linux-kernel, linux-integrity,
linux-security-module, chenste, nramas, Roberto Sassu
In-Reply-To: <2f550d4cd860022e990d1de62049df85a6a86df8.camel@huaweicloud.com>
On Thu, 2025-12-11 at 15:50 +0100, Roberto Sassu wrote:
> On Thu, 2025-12-11 at 10:56 +0100, Roberto Sassu wrote:
> > On Wed, 2025-12-10 at 11:12 -0800, Gregory Lumen wrote:
> > > Roberto,
> > >
> > > The proposed approach appears to be workable. However, if our primary goal
> > > here is to enable UM to free kernel memory consumed by the IMA log with an
> > > absolute minimum of kernel functionality/change, then I would argue that
> > > the proposed Stage-then-delete approach still represents unnecessary
> > > complexity when compared to a trim-to-N solution. Specifically:
>
> The benefit of the Stage-then-delete is that you don't need to scan the
> IMA measurements list in advance to determine what to trim, you just
> trim everything by swapping list head (very fast) and then you can read
> and delete the measurements out of the hot path.
I forgot: I will also add in my patch the ability to stage and trim in
one step, to satisfy your use case.
Roberto
> [...]
>
> >
> > > - There exists a potential UM measurement-loss race condition introduced
> > > by the staging functionality that would not exist with a trim-to-N
> > > approach. (Occurs if a kexec call occurs after a UM agent has staged
> > > measurements for deletion, but has not completed copying them to
> > > userspace). This could be avoided by persisting staged measurements across
> > > kexec calls at the cost of making the proposed change larger.
> >
> > The solution is to coordinate the staging with kexec in user space.
>
> To avoid requiring coordination in user space, I will try to see if I
> could improve my patch to prepend the staged entries to the current
> measurement list, before serializing them for kexec().
>
> Roberto
^ permalink raw reply
* Re: [RFC][PATCH] ima: Add support for staging measurements for deletion
From: Roberto Sassu @ 2025-12-11 14:50 UTC (permalink / raw)
To: Gregory Lumen
Cc: corbet, zohar, dmitry.kasatkin, eric.snowberg, paul, jmorris,
serge, linux-doc, linux-kernel, linux-integrity,
linux-security-module, chenste, nramas, Roberto Sassu
In-Reply-To: <d7418d0afa696b8da67e4f25fd0dc1b9d6fd908f.camel@huaweicloud.com>
On Thu, 2025-12-11 at 10:56 +0100, Roberto Sassu wrote:
> On Wed, 2025-12-10 at 11:12 -0800, Gregory Lumen wrote:
> > Roberto,
> >
> > The proposed approach appears to be workable. However, if our primary goal
> > here is to enable UM to free kernel memory consumed by the IMA log with an
> > absolute minimum of kernel functionality/change, then I would argue that
> > the proposed Stage-then-delete approach still represents unnecessary
> > complexity when compared to a trim-to-N solution. Specifically:
The benefit of the Stage-then-delete is that you don't need to scan the
IMA measurements list in advance to determine what to trim, you just
trim everything by swapping list head (very fast) and then you can read
and delete the measurements out of the hot path.
[...]
>
> > - There exists a potential UM measurement-loss race condition introduced
> > by the staging functionality that would not exist with a trim-to-N
> > approach. (Occurs if a kexec call occurs after a UM agent has staged
> > measurements for deletion, but has not completed copying them to
> > userspace). This could be avoided by persisting staged measurements across
> > kexec calls at the cost of making the proposed change larger.
>
> The solution is to coordinate the staging with kexec in user space.
To avoid requiring coordination in user space, I will try to see if I
could improve my patch to prepend the staged entries to the current
measurement list, before serializing them for kexec().
Roberto
^ permalink raw reply
* Re: [PATCH v4 16/35] kref: Add context-analysis annotations
From: Marco Elver @ 2025-12-11 13:54 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251211122636.GI3911114@noisy.programming.kicks-ass.net>
On Thu, 11 Dec 2025 at 13:26, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Nov 20, 2025 at 04:09:41PM +0100, Marco Elver wrote:
> > Mark functions that conditionally acquire the passed lock.
> >
> > Signed-off-by: Marco Elver <elver@google.com>
> > ---
> > include/linux/kref.h | 2 ++
> > 1 file changed, 2 insertions(+)
> >
> > diff --git a/include/linux/kref.h b/include/linux/kref.h
> > index 88e82ab1367c..9bc6abe57572 100644
> > --- a/include/linux/kref.h
> > +++ b/include/linux/kref.h
> > @@ -81,6 +81,7 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)
> > static inline int kref_put_mutex(struct kref *kref,
> > void (*release)(struct kref *kref),
> > struct mutex *mutex)
> > + __cond_acquires(true, mutex)
> > {
> > if (refcount_dec_and_mutex_lock(&kref->refcount, mutex)) {
> > release(kref);
> > @@ -102,6 +103,7 @@ static inline int kref_put_mutex(struct kref *kref,
> > static inline int kref_put_lock(struct kref *kref,
> > void (*release)(struct kref *kref),
> > spinlock_t *lock)
> > + __cond_acquires(true, lock)
> > {
> > if (refcount_dec_and_lock(&kref->refcount, lock)) {
> > release(kref);
> > --
> > 2.52.0.rc1.455.g30608eb744-goog
> >
>
> Note that both use the underlying refcount_dec_and_*lock() functions.
> Its a bit sad that annotation those isn't sufficient. These are inline
> functions after all, the compiler should be able to see through all that.
Wrappers will need their own annotations; for this kind of static
analysis (built-in warning diagnostic), inferring things like
__cond_acquires(true, lock) is far too complex (requires
intra-procedural control-flow analysis), and would likely be
incomplete too.
It might also be reasonable to argue that the explicit annotation is
good for documentation.
Aside: There's other static analysis tooling, like clang-analyzer that
can afford to do more complex flow-sensitive intra-procedural
analysis. But that has its own limitations, requires separate
invocation, and is pretty slow in comparison.
^ permalink raw reply
* Re: [PATCH v4 07/35] lockdep: Annotate lockdep assertions for context analysis
From: Marco Elver @ 2025-12-11 13:24 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251211114302.GC3911114@noisy.programming.kicks-ass.net>
On Thu, 11 Dec 2025 at 12:43, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Nov 20, 2025 at 04:09:32PM +0100, Marco Elver wrote:
>
> > include/linux/lockdep.h | 12 ++++++------
> > 1 file changed, 6 insertions(+), 6 deletions(-)
> >
> > diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
> > index 67964dc4db95..2c99a6823161 100644
> > --- a/include/linux/lockdep.h
> > +++ b/include/linux/lockdep.h
> > @@ -282,16 +282,16 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
> > do { WARN_ON_ONCE(debug_locks && !(cond)); } while (0)
>
> Since I typically read patches without first reading the Changelog --
> because when I read the code later, I also don't see changelogs.
>
> I must admit to getting most terribly confused here -- *again*, as I
> then search back to previous discussions and found I was previously also
> confused.
>
> As such, I think we want a comment here that explains that assume_ctx
> thing.
>
> It is *NOT* (as the clang naming suggests) an assertion of holding the
> lock (which is requires_ctx), but rather an annotation that forces the
> ctx to be considered held.
Noted. I'll add some appropriate wording above the
__assumes_ctx_guard() attribute, so this is not lost in the commit
logs.
^ permalink raw reply
* Re: [PATCH v4 06/35] cleanup: Basic compatibility with context analysis
From: Marco Elver @ 2025-12-11 13:19 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251211121659.GH3911114@noisy.programming.kicks-ass.net>
On Thu, 11 Dec 2025 at 13:17, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Nov 20, 2025 at 04:09:31PM +0100, Marco Elver wrote:
> > Introduce basic compatibility with cleanup.h infrastructure: introduce
> > DECLARE_LOCK_GUARD_*_ATTRS() helpers to add attributes to constructors
> > and destructors respectively.
> >
> > Note: Due to the scoped cleanup helpers used for lock guards wrapping
> > acquire and release around their own constructors/destructors that store
> > pointers to the passed locks in a separate struct, we currently cannot
> > accurately annotate *destructors* which lock was released. While it's
> > possible to annotate the constructor to say which lock was acquired,
> > that alone would result in false positives claiming the lock was not
> > released on function return.
> >
> > Instead, to avoid false positives, we can claim that the constructor
> > "assumes" that the taken lock is held via __assumes_ctx_guard().
> Moo, so the alias analysis didn't help here?
Unfortunately no, because intra-procedural alias analysis for these
kinds of diagnostics is infeasible. The compiler can only safely
perform alias analysis for local variables that do not escape the
function. The layers of wrapping here make this a bit tricky.
The compiler (unlike before) is now able to deal with things like:
{
spinlock_t *lock_scope __attribute__((cleanup(spin_unlock))) = &lock;
spin_lock(&lock); // lock through &lock
... critical section ...
} // unlock through lock_scope (alias -> &lock)
> What is the scope of this __assumes_ctx stuff? The way it is used in the
> lock initializes seems to suggest it escapes scope. But then something
> like:
It escapes scope.
> scoped_guard (mutex, &foo) {
> ...
> }
> // context analysis would still assume foo held
>
> is somewhat sub-optimal, no?
Correct. We're trading false negatives over false positives at this
point, just to get things to compile cleanly.
> > Better support for Linux's scoped guard design could be added in
> > future if deemed critical.
>
> I would think so, per the above I don't think this is 'right'.
It's not sound, but we'll avoid false positives for the time being.
Maybe we can wrangle the jigsaw of macros to let it correctly acquire
and then release (via a 2nd cleanup function), it might be as simple
as marking the 'constructor' with the right __acquires(..), and then
have a 2nd __attribute__((cleanup)) variable that just does a no-op
release via __release(..) so we get the already supported pattern
above.
^ permalink raw reply
* Re: [PATCH v4 02/35] compiler-context-analysis: Add infrastructure for Context Analysis with Clang
From: Marco Elver @ 2025-12-11 13:12 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251211120441.GG3911114@noisy.programming.kicks-ass.net>
On Thu, 11 Dec 2025 at 13:04, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Thu, Nov 20, 2025 at 03:49:04PM +0100, Marco Elver wrote:
>
> > +/**
> > + * context_guard_struct() - declare or define a context guard struct
> > + * @name: struct name
> > + *
> > + * Helper to declare or define a struct type that is also a context guard.
> > + *
> > + * .. code-block:: c
> > + *
> > + * context_guard_struct(my_handle) {
> > + * int foo;
> > + * long bar;
> > + * };
> > + *
> > + * struct some_state {
> > + * ...
> > + * };
> > + * // ... declared elsewhere ...
> > + * context_guard_struct(some_state);
> > + *
> > + * Note: The implementation defines several helper functions that can acquire
> > + * and release the context guard.
> > + */
> > +# define context_guard_struct(name, ...) \
> > + struct __ctx_guard_type(name) __VA_ARGS__ name; \
> > + static __always_inline void __acquire_ctx_guard(const struct name *var) \
> > + __attribute__((overloadable)) __no_context_analysis __acquires_ctx_guard(var) { } \
> > + static __always_inline void __acquire_shared_ctx_guard(const struct name *var) \
> > + __attribute__((overloadable)) __no_context_analysis __acquires_shared_ctx_guard(var) { } \
> > + static __always_inline bool __try_acquire_ctx_guard(const struct name *var, bool ret) \
> > + __attribute__((overloadable)) __no_context_analysis __try_acquires_ctx_guard(1, var) \
> > + { return ret; } \
> > + static __always_inline bool __try_acquire_shared_ctx_guard(const struct name *var, bool ret) \
> > + __attribute__((overloadable)) __no_context_analysis __try_acquires_shared_ctx_guard(1, var) \
> > + { return ret; } \
> > + static __always_inline void __release_ctx_guard(const struct name *var) \
> > + __attribute__((overloadable)) __no_context_analysis __releases_ctx_guard(var) { } \
> > + static __always_inline void __release_shared_ctx_guard(const struct name *var) \
> > + __attribute__((overloadable)) __no_context_analysis __releases_shared_ctx_guard(var) { } \
> > + static __always_inline void __assume_ctx_guard(const struct name *var) \
> > + __attribute__((overloadable)) __assumes_ctx_guard(var) { } \
> > + static __always_inline void __assume_shared_ctx_guard(const struct name *var) \
> > + __attribute__((overloadable)) __assumes_shared_ctx_guard(var) { } \
> > + struct name
>
> -typedef struct {
> +context_guard_struct(rwlock) {
> struct rwbase_rt rwbase;
> atomic_t readers;
> #ifdef CONFIG_DEBUG_LOCK_ALLOC
> struct lockdep_map dep_map;
> #endif
> -} rwlock_t;
> +};
> +typedef struct rwlock rwlock_t;
>
>
> I must say I find the 'guard' naming here somewhat confusing. This is
> not a guard, but an actual lock type.
The switch to "context analysis" required us coming up with a name for
the actual objects (previously: "capability") that "guard" those
contexts.
The reasoning was that these are guards for entering a particular
context. The lock guards the given context, but the context itself !=
lock. Clang's naming of "capability" was a lot clearer in isolation,
but the problem that Linus raised is that "capability" is already
overloaded in the kernel.
The fact it overlaps in naming with the other guard(..) infrastructure
is not entirely coincidental, but I see the confusion.
What's a better name?
context_lock_struct -> and call it "context lock" rather than "context
guard"; it might work also for things like RCU, PREEMPT, BH, etc. that
aren't normal "locks", but could claim they are "context locks".
context_handle_struct -> "context handle" ...
?
^ permalink raw reply
* Re: [PATCH v4 16/35] kref: Add context-analysis annotations
From: Peter Zijlstra @ 2025-12-11 12:26 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120151033.3840508-17-elver@google.com>
On Thu, Nov 20, 2025 at 04:09:41PM +0100, Marco Elver wrote:
> Mark functions that conditionally acquire the passed lock.
>
> Signed-off-by: Marco Elver <elver@google.com>
> ---
> include/linux/kref.h | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/include/linux/kref.h b/include/linux/kref.h
> index 88e82ab1367c..9bc6abe57572 100644
> --- a/include/linux/kref.h
> +++ b/include/linux/kref.h
> @@ -81,6 +81,7 @@ static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)
> static inline int kref_put_mutex(struct kref *kref,
> void (*release)(struct kref *kref),
> struct mutex *mutex)
> + __cond_acquires(true, mutex)
> {
> if (refcount_dec_and_mutex_lock(&kref->refcount, mutex)) {
> release(kref);
> @@ -102,6 +103,7 @@ static inline int kref_put_mutex(struct kref *kref,
> static inline int kref_put_lock(struct kref *kref,
> void (*release)(struct kref *kref),
> spinlock_t *lock)
> + __cond_acquires(true, lock)
> {
> if (refcount_dec_and_lock(&kref->refcount, lock)) {
> release(kref);
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
Note that both use the underlying refcount_dec_and_*lock() functions.
Its a bit sad that annotation those isn't sufficient. These are inline
functions after all, the compiler should be able to see through all that.
^ permalink raw reply
* Re: [PATCH v4 06/35] cleanup: Basic compatibility with context analysis
From: Peter Zijlstra @ 2025-12-11 12:16 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120151033.3840508-7-elver@google.com>
On Thu, Nov 20, 2025 at 04:09:31PM +0100, Marco Elver wrote:
> Introduce basic compatibility with cleanup.h infrastructure: introduce
> DECLARE_LOCK_GUARD_*_ATTRS() helpers to add attributes to constructors
> and destructors respectively.
>
> Note: Due to the scoped cleanup helpers used for lock guards wrapping
> acquire and release around their own constructors/destructors that store
> pointers to the passed locks in a separate struct, we currently cannot
> accurately annotate *destructors* which lock was released. While it's
> possible to annotate the constructor to say which lock was acquired,
> that alone would result in false positives claiming the lock was not
> released on function return.
>
> Instead, to avoid false positives, we can claim that the constructor
> "assumes" that the taken lock is held via __assumes_ctx_guard().
What is the scope of this __assumes_ctx stuff? The way it is used in the
lock initializes seems to suggest it escapes scope. But then something
like:
scoped_guard (mutex, &foo) {
...
}
// context analysis would still assume foo held
is somewhat sub-optimal, no?
> Better support for Linux's scoped guard design could be added in
> future if deemed critical.
I would think so, per the above I don't think this is 'right'.
^ permalink raw reply
* Re: [PATCH v4 02/35] compiler-context-analysis: Add infrastructure for Context Analysis with Clang
From: Peter Zijlstra @ 2025-12-11 12:04 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120145835.3833031-4-elver@google.com>
On Thu, Nov 20, 2025 at 03:49:04PM +0100, Marco Elver wrote:
> +/**
> + * context_guard_struct() - declare or define a context guard struct
> + * @name: struct name
> + *
> + * Helper to declare or define a struct type that is also a context guard.
> + *
> + * .. code-block:: c
> + *
> + * context_guard_struct(my_handle) {
> + * int foo;
> + * long bar;
> + * };
> + *
> + * struct some_state {
> + * ...
> + * };
> + * // ... declared elsewhere ...
> + * context_guard_struct(some_state);
> + *
> + * Note: The implementation defines several helper functions that can acquire
> + * and release the context guard.
> + */
> +# define context_guard_struct(name, ...) \
> + struct __ctx_guard_type(name) __VA_ARGS__ name; \
> + static __always_inline void __acquire_ctx_guard(const struct name *var) \
> + __attribute__((overloadable)) __no_context_analysis __acquires_ctx_guard(var) { } \
> + static __always_inline void __acquire_shared_ctx_guard(const struct name *var) \
> + __attribute__((overloadable)) __no_context_analysis __acquires_shared_ctx_guard(var) { } \
> + static __always_inline bool __try_acquire_ctx_guard(const struct name *var, bool ret) \
> + __attribute__((overloadable)) __no_context_analysis __try_acquires_ctx_guard(1, var) \
> + { return ret; } \
> + static __always_inline bool __try_acquire_shared_ctx_guard(const struct name *var, bool ret) \
> + __attribute__((overloadable)) __no_context_analysis __try_acquires_shared_ctx_guard(1, var) \
> + { return ret; } \
> + static __always_inline void __release_ctx_guard(const struct name *var) \
> + __attribute__((overloadable)) __no_context_analysis __releases_ctx_guard(var) { } \
> + static __always_inline void __release_shared_ctx_guard(const struct name *var) \
> + __attribute__((overloadable)) __no_context_analysis __releases_shared_ctx_guard(var) { } \
> + static __always_inline void __assume_ctx_guard(const struct name *var) \
> + __attribute__((overloadable)) __assumes_ctx_guard(var) { } \
> + static __always_inline void __assume_shared_ctx_guard(const struct name *var) \
> + __attribute__((overloadable)) __assumes_shared_ctx_guard(var) { } \
> + struct name
-typedef struct {
+context_guard_struct(rwlock) {
struct rwbase_rt rwbase;
atomic_t readers;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
struct lockdep_map dep_map;
#endif
-} rwlock_t;
+};
+typedef struct rwlock rwlock_t;
I must say I find the 'guard' naming here somewhat confusing. This is
not a guard, but an actual lock type.
^ permalink raw reply
* Re: [PATCH v4 06/35] cleanup: Basic compatibility with context analysis
From: Peter Zijlstra @ 2025-12-11 11:51 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120151033.3840508-7-elver@google.com>
On Thu, Nov 20, 2025 at 04:09:31PM +0100, Marco Elver wrote:
> +#define DECLARE_LOCK_GUARD_0_ATTRS(_name, _lock, _unlock) \
> +static inline class_##_name##_t class_##_name##_constructor(void) _lock;\
> +static inline void class_##_name##_destructor(class_##_name##_t *_T) _unlock;
> +
> +#define DECLARE_LOCK_GUARD_1_ATTRS(_name, _lock, _unlock) \
> +static inline class_##_name##_t class_##_name##_constructor(lock_##_name##_t *_T) _lock;\
> +static inline void class_##_name##_destructor(class_##_name##_t *_T) _unlock;
When you rebase this series; you'll find cleanup.h moved to
__always_inline (because compilers are weird) and these should probably
also switch.
^ permalink raw reply
* Re: [PATCH v4 08/35] locking/rwlock, spinlock: Support Clang's context analysis
From: Peter Zijlstra @ 2025-12-11 11:49 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120151033.3840508-9-elver@google.com>
On Thu, Nov 20, 2025 at 04:09:33PM +0100, Marco Elver wrote:
> To avoid warnings in constructors, the initialization functions mark a
> lock as acquired when initialized before guarded variables.
> diff --git a/include/linux/rwlock.h b/include/linux/rwlock.h
> index 5b87c6f4a243..a2f85a0356c4 100644
> --- a/include/linux/rwlock.h
> +++ b/include/linux/rwlock.h
> @@ -22,23 +22,24 @@ do { \
> static struct lock_class_key __key; \
> \
> __rwlock_init((lock), #lock, &__key); \
> + __assume_ctx_guard(lock); \
> } while (0)
> #else
> # define rwlock_init(lock) \
> - do { *(lock) = __RW_LOCK_UNLOCKED(lock); } while (0)
> + do { *(lock) = __RW_LOCK_UNLOCKED(lock); __assume_ctx_guard(lock); } while (0)
> #endif
This is again somewhat magical and confused the living daylight out of
me. I know (from having looked back on previous discussions) that I was
confused about this before, and clearly it didn't stick.
So obviously I'll be confused again when I look at this code in a years
time or so :/
Can we get a comment near this __assume_ctx_guard() thing (because
putting it all over the lock initializers would probably be duplicating
things too much)?
^ permalink raw reply
* Re: [PATCH v4 02/35] compiler-context-analysis: Add infrastructure for Context Analysis with Clang
From: Peter Zijlstra @ 2025-12-11 11:44 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120145835.3833031-4-elver@google.com>
On Thu, Nov 20, 2025 at 03:49:04PM +0100, Marco Elver wrote:
> +/**
> + * __guarded_by - struct member and globals attribute, declares variable
> + * only accessible within active context
> + *
> + * Declares that the struct member or global variable is only accessible within
> + * the context entered by the given context guard. Read operations on the data
> + * require shared access, while write operations require exclusive access.
> + *
> + * .. code-block:: c
> + *
> + * struct some_state {
> + * spinlock_t lock;
> + * long counter __guarded_by(&lock);
> + * };
> + */
> +# define __guarded_by(...) __attribute__((guarded_by(__VA_ARGS__)))
I must express pure hatred for this '.. code-block:: c' thing.
^ permalink raw reply
* Re: [PATCH v4 07/35] lockdep: Annotate lockdep assertions for context analysis
From: Peter Zijlstra @ 2025-12-11 11:43 UTC (permalink / raw)
To: Marco Elver
Cc: Boqun Feng, Ingo Molnar, Will Deacon, David S. Miller,
Luc Van Oostenryck, Chris Li, Paul E. McKenney,
Alexander Potapenko, Arnd Bergmann, Bart Van Assche,
Christoph Hellwig, Dmitry Vyukov, Eric Dumazet,
Frederic Weisbecker, Greg Kroah-Hartman, Herbert Xu, Ian Rogers,
Jann Horn, Joel Fernandes, Johannes Berg, Jonathan Corbet,
Josh Triplett, Justin Stitt, Kees Cook, Kentaro Takeda,
Lukas Bulwahn, Mark Rutland, Mathieu Desnoyers, Miguel Ojeda,
Nathan Chancellor, Neeraj Upadhyay, Nick Desaulniers,
Steven Rostedt, Tetsuo Handa, Thomas Gleixner, Thomas Graf,
Uladzislau Rezki, Waiman Long, kasan-dev, linux-crypto, linux-doc,
linux-kbuild, linux-kernel, linux-mm, linux-security-module,
linux-sparse, linux-wireless, llvm, rcu
In-Reply-To: <20251120151033.3840508-8-elver@google.com>
On Thu, Nov 20, 2025 at 04:09:32PM +0100, Marco Elver wrote:
> include/linux/lockdep.h | 12 ++++++------
> 1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
> index 67964dc4db95..2c99a6823161 100644
> --- a/include/linux/lockdep.h
> +++ b/include/linux/lockdep.h
> @@ -282,16 +282,16 @@ extern void lock_unpin_lock(struct lockdep_map *lock, struct pin_cookie);
> do { WARN_ON_ONCE(debug_locks && !(cond)); } while (0)
Since I typically read patches without first reading the Changelog --
because when I read the code later, I also don't see changelogs.
I must admit to getting most terribly confused here -- *again*, as I
then search back to previous discussions and found I was previously also
confused.
As such, I think we want a comment here that explains that assume_ctx
thing.
It is *NOT* (as the clang naming suggests) an assertion of holding the
lock (which is requires_ctx), but rather an annotation that forces the
ctx to be considered held.
>
> #define lockdep_assert_held(l) \
> - lockdep_assert(lockdep_is_held(l) != LOCK_STATE_NOT_HELD)
> + do { lockdep_assert(lockdep_is_held(l) != LOCK_STATE_NOT_HELD); __assume_ctx_guard(l); } while (0)
>
> #define lockdep_assert_not_held(l) \
> lockdep_assert(lockdep_is_held(l) != LOCK_STATE_HELD)
>
> #define lockdep_assert_held_write(l) \
> - lockdep_assert(lockdep_is_held_type(l, 0))
> + do { lockdep_assert(lockdep_is_held_type(l, 0)); __assume_ctx_guard(l); } while (0)
>
> #define lockdep_assert_held_read(l) \
> - lockdep_assert(lockdep_is_held_type(l, 1))
> + do { lockdep_assert(lockdep_is_held_type(l, 1)); __assume_shared_ctx_guard(l); } while (0)
>
> #define lockdep_assert_held_once(l) \
> lockdep_assert_once(lockdep_is_held(l) != LOCK_STATE_NOT_HELD)
> @@ -389,10 +389,10 @@ extern int lockdep_is_held(const void *);
> #define lockdep_assert(c) do { } while (0)
> #define lockdep_assert_once(c) do { } while (0)
>
> -#define lockdep_assert_held(l) do { (void)(l); } while (0)
> +#define lockdep_assert_held(l) __assume_ctx_guard(l)
> #define lockdep_assert_not_held(l) do { (void)(l); } while (0)
> -#define lockdep_assert_held_write(l) do { (void)(l); } while (0)
> -#define lockdep_assert_held_read(l) do { (void)(l); } while (0)
> +#define lockdep_assert_held_write(l) __assume_ctx_guard(l)
> +#define lockdep_assert_held_read(l) __assume_shared_ctx_guard(l)
> #define lockdep_assert_held_once(l) do { (void)(l); } while (0)
> #define lockdep_assert_none_held_once() do { } while (0)
>
> --
> 2.52.0.rc1.455.g30608eb744-goog
>
^ permalink raw reply
* Re: [RFC][PATCH] ima: Add support for staging measurements for deletion
From: Roberto Sassu @ 2025-12-11 10:18 UTC (permalink / raw)
To: steven chen, corbet, zohar, dmitry.kasatkin, eric.snowberg, paul,
jmorris, serge
Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
gregorylumen, nramas, Roberto Sassu
In-Reply-To: <9cb4cbab-bcca-4ac8-a7a5-0cf3de67353e@linux.microsoft.com>
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@huawei.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@huawei.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;
>
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox