* [PATCH v4] ima: Access decompressed kernel module to verify appended signature
From: Coiby Xu @ 2025-11-19 14:03 UTC (permalink / raw)
To: linux-integrity, Mimi Zohar
Cc: Karel Srot, Paul Moore, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, James Morris, Serge E. Hallyn, Fan Wu,
Stephen Smalley, Ondrej Mosnacek, open list,
open list:MODULE SUPPORT, open list:SECURITY SUBSYSTEM,
open list:SELINUX SECURITY MODULE
In-Reply-To: <20251031074016.1975356-1-coxu@redhat.com>
Currently, when in-kernel module decompression (CONFIG_MODULE_DECOMPRESS)
is enabled, IMA has no way to verify the appended module signature as it
can't decompress the module.
Define a new kernel_read_file_id enumerate READING_MODULE_COMPRESSED so
IMA can calculate the compressed kernel module data hash on
READING_MODULE_COMPRESSED and defer appraising/measuring it until on
READING_MODULE when the module has been decompressed.
Before enabling in-kernel module decompression, a kernel module in
initramfs can still be loaded with ima_policy=secure_boot. So adjust the
kernel module rule in secure_boot policy to allow either an IMA
signature OR an appended signature i.e. to use
"appraise func=MODULE_CHECK appraise_type=imasig|modsig".
Reported-by: Karel Srot <ksrot@redhat.com>
Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
Suggested-by: Paul Moore <paul@paul-moore.com>
Signed-off-by: Coiby Xu <coxu@redhat.com>
---
include/linux/kernel_read_file.h | 1 +
kernel/module/main.c | 17 ++++++++++++++---
security/integrity/ima/ima_main.c | 24 ++++++++++++++++--------
security/integrity/ima/ima_policy.c | 3 ++-
security/ipe/hooks.c | 1 +
security/selinux/hooks.c | 5 +++--
6 files changed, 37 insertions(+), 14 deletions(-)
diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h
index 90451e2e12bd..d613a7b4dd35 100644
--- a/include/linux/kernel_read_file.h
+++ b/include/linux/kernel_read_file.h
@@ -14,6 +14,7 @@
id(KEXEC_INITRAMFS, kexec-initramfs) \
id(POLICY, security-policy) \
id(X509_CERTIFICATE, x509-certificate) \
+ id(MODULE_COMPRESSED, kernel-module-compressed) \
id(MAX_ID, )
#define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
diff --git a/kernel/module/main.c b/kernel/module/main.c
index c66b26184936..7b3ec2fa6e7c 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -3675,24 +3675,35 @@ static int idempotent_wait_for_completion(struct idempotent *u)
static int init_module_from_file(struct file *f, const char __user * uargs, int flags)
{
+ bool compressed = !!(flags & MODULE_INIT_COMPRESSED_FILE);
struct load_info info = { };
void *buf = NULL;
int len;
+ int err;
- len = kernel_read_file(f, 0, &buf, INT_MAX, NULL, READING_MODULE);
+ len = kernel_read_file(f, 0, &buf, INT_MAX, NULL,
+ compressed ? READING_MODULE_COMPRESSED :
+ READING_MODULE);
if (len < 0) {
mod_stat_inc(&failed_kreads);
return len;
}
- if (flags & MODULE_INIT_COMPRESSED_FILE) {
- int err = module_decompress(&info, buf, len);
+ if (compressed) {
+ err = module_decompress(&info, buf, len);
vfree(buf); /* compressed data is no longer needed */
if (err) {
mod_stat_inc(&failed_decompress);
mod_stat_add_long(len, &invalid_decompress_bytes);
return err;
}
+ err = security_kernel_post_read_file(f, (char *)info.hdr, info.len,
+ READING_MODULE);
+ if (err) {
+ mod_stat_inc(&failed_kreads);
+ free_copy(&info, flags);
+ return err;
+ }
} else {
info.hdr = buf;
info.len = len;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index ebaebccfbe9a..edd0fd3d77a0 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -235,7 +235,8 @@ static void ima_file_free(struct file *file)
static int process_measurement(struct file *file, const struct cred *cred,
struct lsm_prop *prop, char *buf, loff_t size,
- int mask, enum ima_hooks func)
+ int mask, enum ima_hooks func,
+ enum kernel_read_file_id read_id)
{
struct inode *real_inode, *inode = file_inode(file);
struct ima_iint_cache *iint = NULL;
@@ -406,6 +407,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (rc != 0 && rc != -EBADF && rc != -EINVAL)
goto out_locked;
+ /* Defer measuring/appraising kernel modules to READING_MODULE */
+ if (read_id == READING_MODULE_COMPRESSED) {
+ must_appraise = 0;
+ goto out_locked;
+ }
+
if (!pathbuf) /* ima_rdwr_violation possibly pre-fetched */
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
@@ -486,14 +493,14 @@ static int ima_file_mmap(struct file *file, unsigned long reqprot,
if (reqprot & PROT_EXEC) {
ret = process_measurement(file, current_cred(), &prop, NULL,
- 0, MAY_EXEC, MMAP_CHECK_REQPROT);
+ 0, MAY_EXEC, MMAP_CHECK_REQPROT, 0);
if (ret)
return ret;
}
if (prot & PROT_EXEC)
return process_measurement(file, current_cred(), &prop, NULL,
- 0, MAY_EXEC, MMAP_CHECK);
+ 0, MAY_EXEC, MMAP_CHECK, 0);
return 0;
}
@@ -577,7 +584,7 @@ static int ima_bprm_check(struct linux_binprm *bprm)
security_current_getlsmprop_subj(&prop);
return process_measurement(bprm->file, current_cred(),
- &prop, NULL, 0, MAY_EXEC, BPRM_CHECK);
+ &prop, NULL, 0, MAY_EXEC, BPRM_CHECK, 0);
}
/**
@@ -607,7 +614,7 @@ static int ima_creds_check(struct linux_binprm *bprm, const struct file *file)
security_current_getlsmprop_subj(&prop);
return process_measurement((struct file *)file, bprm->cred, &prop, NULL,
- 0, MAY_EXEC, CREDS_CHECK);
+ 0, MAY_EXEC, CREDS_CHECK, 0);
}
/**
@@ -655,7 +662,7 @@ static int ima_file_check(struct file *file, int mask)
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
- MAY_APPEND), FILE_CHECK);
+ MAY_APPEND), FILE_CHECK, 0);
}
static int __ima_inode_hash(struct inode *inode, struct file *file, char *buf,
@@ -874,12 +881,13 @@ static int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
func = read_idmap[read_id] ?: FILE_CHECK;
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, NULL, 0,
- MAY_READ, func);
+ MAY_READ, func, 0);
}
const int read_idmap[READING_MAX_ID] = {
[READING_FIRMWARE] = FIRMWARE_CHECK,
[READING_MODULE] = MODULE_CHECK,
+ [READING_MODULE_COMPRESSED] = MODULE_CHECK,
[READING_KEXEC_IMAGE] = KEXEC_KERNEL_CHECK,
[READING_KEXEC_INITRAMFS] = KEXEC_INITRAMFS_CHECK,
[READING_POLICY] = POLICY_CHECK
@@ -917,7 +925,7 @@ static int ima_post_read_file(struct file *file, char *buf, loff_t size,
func = read_idmap[read_id] ?: FILE_CHECK;
security_current_getlsmprop_subj(&prop);
return process_measurement(file, current_cred(), &prop, buf, size,
- MAY_READ, func);
+ MAY_READ, func, read_id);
}
/**
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 164d62832f8e..7468afaab686 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -244,7 +244,8 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = {
static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
{.action = APPRAISE, .func = MODULE_CHECK,
- .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
+ .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED | IMA_MODSIG_ALLOWED |
+ IMA_CHECK_BLACKLIST},
{.action = APPRAISE, .func = FIRMWARE_CHECK,
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
{.action = APPRAISE, .func = KEXEC_KERNEL_CHECK,
diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
index d0323b81cd8f..1053a4acf589 100644
--- a/security/ipe/hooks.c
+++ b/security/ipe/hooks.c
@@ -118,6 +118,7 @@ int ipe_kernel_read_file(struct file *file, enum kernel_read_file_id id,
op = IPE_OP_FIRMWARE;
break;
case READING_MODULE:
+ case READING_MODULE_COMPRESSED:
op = IPE_OP_KERNEL_MODULE;
break;
case READING_KEXEC_INITRAMFS:
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index dfc22da42f30..c1ff69d5d76e 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4275,7 +4275,7 @@ static int selinux_kernel_read_file(struct file *file,
{
int rc = 0;
- BUILD_BUG_ON_MSG(READING_MAX_ID > 7,
+ BUILD_BUG_ON_MSG(READING_MAX_ID > 8,
"New kernel_read_file_id introduced; update SELinux!");
switch (id) {
@@ -4283,6 +4283,7 @@ static int selinux_kernel_read_file(struct file *file,
rc = selinux_kernel_load_from_file(file, SYSTEM__FIRMWARE_LOAD);
break;
case READING_MODULE:
+ case READING_MODULE_COMPRESSED:
rc = selinux_kernel_load_from_file(file, SYSTEM__MODULE_LOAD);
break;
case READING_KEXEC_IMAGE:
@@ -4311,7 +4312,7 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents)
{
int rc = 0;
- BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7,
+ BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8,
"New kernel_load_data_id introduced; update SELinux!");
switch (id) {
base-commit: 43369273518f57b7d56c1cf12d636a809b7bd81b
--
2.51.1
^ permalink raw reply related
* Re: [PATCH v3] ima: Access decompressed kernel module to verify appended signature
From: Coiby Xu @ 2025-11-19 14:05 UTC (permalink / raw)
To: Mimi Zohar
Cc: linux-integrity, Karel Srot, Paul Moore, Luis Chamberlain,
Petr Pavlu, Daniel Gomez, Sami Tolvanen, Roberto Sassu,
Dmitry Kasatkin, Eric Snowberg, James Morris, Serge E. Hallyn,
Fan Wu, Stephen Smalley, Ondrej Mosnacek, open list,
open list:MODULE SUPPORT, open list:SECURITY SUBSYSTEM,
open list:SELINUX SECURITY MODULE
In-Reply-To: <fc1d67e411ef53460517db4c03bdcf1b9d9f8a8f.camel@linux.ibm.com>
On Wed, Nov 19, 2025 at 08:29:22AM -0500, Mimi Zohar wrote:
>Hi Coiby,
Hi Mimi,
>
>On Wed, 2025-11-19 at 11:47 +0800, Coiby Xu wrote:
>> Currently, when in-kernel module decompression (CONFIG_MODULE_DECOMPRESS)
>> is enabled, IMA has no way to verify the appended module signature as it
>> can't decompress the module.
>>
>> Define a new kernel_read_file_id enumerate READING_MODULE_COMPRESSED so
>> IMA can know only to collect original module data hash on
>> READING_MODULE_COMPRESSED and defer appraising/measuring it until on
>> READING_MODULE when the module has been decompressed.
>
>This paragraph is a bit awkward. Perhaps something like:
>
>-> so IMA can calculate the compressed kernel module data hash and defer
>measuring/appraising ...
>
>>
>> Before enabling in-kernel module decompression, a kernel module in
>> initramfs can still be loaded with ima_policy=secure_boot. So adjust the
>> kernel module rule in secure_boot policy to allow either an IMA
>> signature OR an appended signature i.e. to use
>> "appraise func=MODULE_CHECK appraise_type=imasig|modsig".
>>
>> Reported-by: Karel Srot <ksrot@redhat.com>
>> Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
>> Suggested-by: Paul Moore <paul@paul-moore.com>
>> Signed-off-by: Coiby Xu <coxu@redhat.com>
>
>Thanks, Coiby!
>
>The patch applies cleanly to linus' tree, but needs to be applied to next-
>integrity. Please re-base.
I've sent v4 which has been rebased onto next tree with improved
wording as suggested.
>
>--
>
>thanks,
>
>Mimi
>
--
Best regards,
Coiby
^ permalink raw reply
* Re: [PATCH] KEYS: encrypted: Use pr_fmt()
From: Thorsten Blum @ 2025-11-19 14:45 UTC (permalink / raw)
To: Jarkko Sakkinen
Cc: Mimi Zohar, David Howells, Paul Moore, James Morris,
Serge E. Hallyn, linux-integrity, keyrings, linux-security-module,
linux-kernel
In-Reply-To: <aR0v9mLOKJsr_0Zm@kernel.org>
On 19. Nov 2025, at 03:48, Jarkko Sakkinen wrote:
> On Thu, Nov 13, 2025 at 01:35:44PM +0100, Thorsten Blum wrote:
>> Use pr_fmt() to automatically prefix all pr_<level>() log messages with
>
> This fails to describe what "use" means.
I don't understand what you mean. What's wrong with "use ... to ..."?
>> "encrypted_key: " and remove all manually added prefixes.
>>
>> Reformat the code accordingly and avoid line breaks in log messages.
>>
>> Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
>> ---
>> security/keys/encrypted-keys/encrypted.c | 74 +++++++++++-------------
>> security/keys/encrypted-keys/encrypted.h | 2 +-
>> 2 files changed, 35 insertions(+), 41 deletions(-)
>>
>> diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c
>> index 513c09e2b01c..a8e8bf949b4b 100644
>> --- a/security/keys/encrypted-keys/encrypted.c
>> +++ b/security/keys/encrypted-keys/encrypted.c
>> @@ -11,6 +11,8 @@
>> * See Documentation/security/keys/trusted-encrypted.rst
>> */
>>
>
> Should have undef prepending.
Why is this necessary when the #define is at the top of a source file?
The kernel documentation [1] doesn't mention this anywhere. Isn't #undef
only needed when redefining 'pr_fmt' in the middle of a file to avoid a
compiler warning/error?
>> +#define pr_fmt(fmt) "encrypted_key: " fmt
>> +
>> [...]
Thanks,
Thorsten
[1] https://docs.kernel.org/core-api/printk-basics.html
^ permalink raw reply
* Re: [PATCH v4] ima: Access decompressed kernel module to verify appended signature
From: Mimi Zohar @ 2025-11-19 15:29 UTC (permalink / raw)
To: Coiby Xu, linux-integrity
Cc: Karel Srot, Paul Moore, Luis Chamberlain, Petr Pavlu,
Daniel Gomez, Sami Tolvanen, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, James Morris, Serge E. Hallyn, Fan Wu,
Stephen Smalley, Ondrej Mosnacek, open list,
open list:MODULE SUPPORT, open list:SECURITY SUBSYSTEM,
open list:SELINUX SECURITY MODULE
In-Reply-To: <20251119140326.787451-1-coxu@redhat.com>
On Wed, 2025-11-19 at 22:03 +0800, Coiby Xu wrote:
> Currently, when in-kernel module decompression (CONFIG_MODULE_DECOMPRESS)
> is enabled, IMA has no way to verify the appended module signature as it
> can't decompress the module.
>
> Define a new kernel_read_file_id enumerate READING_MODULE_COMPRESSED so
> IMA can calculate the compressed kernel module data hash on
> READING_MODULE_COMPRESSED and defer appraising/measuring it until on
> READING_MODULE when the module has been decompressed.
>
> Before enabling in-kernel module decompression, a kernel module in
> initramfs can still be loaded with ima_policy=secure_boot. So adjust the
> kernel module rule in secure_boot policy to allow either an IMA
> signature OR an appended signature i.e. to use
> "appraise func=MODULE_CHECK appraise_type=imasig|modsig".
>
> Reported-by: Karel Srot <ksrot@redhat.com>
> Suggested-by: Mimi Zohar <zohar@linux.ibm.com>
> Suggested-by: Paul Moore <paul@paul-moore.com>
> Signed-off-by: Coiby Xu <coxu@redhat.com>
Thanks, Coiby! The patch is now queued in next-integrity.
--
Mimi
^ permalink raw reply
* Re: [PATCH] lsm: use unrcu_pointer() for current->cred in security_init()
From: Paul Moore @ 2025-11-19 15:36 UTC (permalink / raw)
To: Xiujianfeng; +Cc: linux-security-module
In-Reply-To: <5508241d-7c8f-4caa-a62e-3d8eb2426b55@huaweicloud.com>
On Tue, Nov 18, 2025 at 10:49 PM Xiujianfeng
<xiujianfeng@huaweicloud.com> wrote:
> On 11/19/2025 8:28 AM, Paul Moore wrote:
> > We need to directly allocate the cred's LSM state for the initial task
> > when we initialize the LSM framework. Unfortunately, this results in a
> > RCU related type mismatch, use the unrcu_pointer() macro to handle this
> > a bit more elegantly.
> >
> > The explicit type casting still remains as we need to work around the
> > constification of current->cred in this particular case.
> >
> > Signed-off-by: Paul Moore <paul@paul-moore.com>
> > ---
> > security/lsm_init.c | 3 ++-
> > 1 file changed, 2 insertions(+), 1 deletion(-)
> >
> > diff --git a/security/lsm_init.c b/security/lsm_init.c
> > index 6bb67d41ce52..4dec9199e4c2 100644
> > --- a/security/lsm_init.c
> > +++ b/security/lsm_init.c
> > @@ -467,7 +467,8 @@ int __init security_init(void)
> > blob_sizes.lbs_inode, 0,
> > SLAB_PANIC, NULL);
> >
> > - if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL))
> > + if (lsm_cred_alloc(unrcu_pointer((struct cred __rcu *)current->cred),
> > + GFP_KERNEL))
>
> Since the casting from const to non-const is inevitable, why not
> directly cast it to (struct cred *)?
I like having the call to the unrcu_pointer() macro as it helps bring
attention to this corner case should things change in the task_struct,
however, I do think you have a point about moving the cast to *after*
the unrcu_pointer() call as we could so a simpler "(struct cred *)"
cast.
v2 coming shortly.
> > panic("early LSM cred alloc failed\n");
> > if (lsm_task_alloc(current))
> > panic("early LSM task alloc failed\n");
--
paul-moore.com
^ permalink raw reply
* [PATCH v2] lsm: use unrcu_pointer() for current->cred in security_init()
From: Paul Moore @ 2025-11-19 15:36 UTC (permalink / raw)
To: linux-security-module
We need to directly allocate the cred's LSM state for the initial task
when we initialize the LSM framework. Unfortunately, this results in a
RCU related type mismatch, use the unrcu_pointer() macro to handle this
a bit more elegantly.
The explicit type casting still remains as we need to work around the
constification of current->cred in this particular case.
Signed-off-by: Paul Moore <paul@paul-moore.com>
---
security/lsm_init.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/security/lsm_init.c b/security/lsm_init.c
index 6bb67d41ce52..05bd52e6b1f2 100644
--- a/security/lsm_init.c
+++ b/security/lsm_init.c
@@ -467,7 +467,8 @@ int __init security_init(void)
blob_sizes.lbs_inode, 0,
SLAB_PANIC, NULL);
- if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL))
+ if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred),
+ GFP_KERNEL))
panic("early LSM cred alloc failed\n");
if (lsm_task_alloc(current))
panic("early LSM task alloc failed\n");
--
2.52.0
^ permalink raw reply related
* Re: [PATCH v3 0/9] module: Introduce hash-based integrity checking
From: Sebastian Andrzej Siewior @ 2025-11-19 15:48 UTC (permalink / raw)
To: James Bottomley
Cc: Thomas Weißschuh, Masahiro Yamada, Nathan Chancellor,
Arnd Bergmann, Luis Chamberlain, Petr Pavlu, Sami Tolvanen,
Daniel Gomez, Paul Moore, James Morris, Serge E. Hallyn,
Jonathan Corbet, Madhavan Srinivasan, Michael Ellerman,
Nicholas Piggin, Christophe Leroy, Naveen N Rao, Mimi Zohar,
Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <f1dca9daa01d0d2432c12ecabede3fa1389b1d29.camel@HansenPartnership.com>
On 2025-04-29 10:05:04 [-0400], James Bottomley wrote:
> On Tue, 2025-04-29 at 15:04 +0200, Thomas Weißschuh wrote:
> > The current signature-based module integrity checking has some
> > drawbacks in combination with reproducible builds:
> > Either the module signing key is generated at build time, which makes
> > the build unreproducible,
>
> I don't believe it does: as long as you know what the key was, which
> you can get from the kernel keyring, you can exactly reproduce the core
> build (it's a public key after all and really equivalent to built in
> configuration). Is the fact that you have to boot the kernel to get
> the key the problem? In which case we could insist it be shipped in
> the kernel packaging.
The kernel itself is signed. This is not a problem because distros have
the "unsigned" package which is used for comparison.
The modules are signed by an ephemeral key which is created at build
time. This is where the problem starts:
- the public key is embedded into the kernel. Extracting it with tooling
is possible (or it is part of the kernel package). Adding this key
into the build process while rebuilding the kernel should work.
This will however alter the build process and is not *the* original
one, which was used to build the image.
- the private key remains unknown which means the modules can not be
signed. The rebuilding would need to get past this limitation and the
logic must not be affected by this "change". Then the modules need to
be stripped of their signature for the comparison.
Doing all this requires additional handling/ tooling on the "validation"
infrastructure. This infrastructure works currently without special
care.
Adding special care will not build the package exactly like it has been
built originally _and_ the results need to be interpreted (as in we
remove this signature and do this and now it is fine).
Adding hashes of each module into the kernel image looks like a
reasonable thing to do. I don't see any downsides to this. Yes, you are
limited to the modules available at build time but this is also the case
today with the ephemeral key. It is meant for distros not for individual
developers testing their code.
With this change it is possible to build a kernel and its modules and
put the result in an archive such as tar/ deb/ rpm. You can build the
package _again_ following exactly the same steps as you did before and
the result will be the identical archive.
Bit by bit.
No need for interpreting the results, stripping signatures or altering
the build process.
I fully agree with this approach. I don't like the big hash array but I
have an idea how to optimize that part. So I don't see a problem in the
long term.
> Regards,
>
> James
Sebastian
^ permalink raw reply
* Re: [PATCH] lockdown: Only log restrictions once
From: Paul Moore @ 2025-11-19 16:05 UTC (permalink / raw)
To: Daniel Tang, Nicolas Bouchinet, Xiu Jianfeng
Cc: linux-security-module, linux-kernel, Nathan Lynch,
Matthew Garrett, Kees Cook, David Howells, James Morris
In-Reply-To: <3641397.L58v44csPz@daniel-desktop3>
On Wed, Nov 19, 2025 at 8:22 AM Daniel Tang
<danielzgtg.opensource@gmail.com> wrote:
>
> KDE's lockscreen causes systemd-logind to spam dmesg about hibernation.
> systemd declined to cache /sys/power/state due to runtime changeability.
>
> Link: https://github.com/systemd/systemd/pull/39802
> Signed-off-by: Daniel Tang <danielzgtg.opensource@gmail.com>
> ---
> security/lockdown/lockdown.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
Adding the Lockdown maintainers to the To/CC line.
> diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
> index cf83afa1d879..4ced8c76dc6b 100644
> --- a/security/lockdown/lockdown.c
> +++ b/security/lockdown/lockdown.c
> @@ -62,9 +62,11 @@ static int lockdown_is_locked_down(enum lockdown_reason what)
> "Invalid lockdown reason"))
> return -EPERM;
>
> + static volatile unsigned long lockdown_reasons_seen;
I'll let the Lockdown folks comment on the rest, but at the very least
this variable should be declared at the top of the function. Yes, you
*can* declare it in the middle, but just because you can, doesn't mean
you should ;)
> + static_assert(ARRAY_SIZE(lockdown_reasons) < sizeof(lockdown_reasons_seen) * 8);
> if (kernel_locked_down >= what) {
> - if (lockdown_reasons[what])
> - pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
> + if (lockdown_reasons[what] && !test_and_set_bit(what, &lockdown_reasons_seen))
> + pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
> current->comm, lockdown_reasons[what]);
> return -EPERM;
> }
> --
> 2.51.0
--
paul-moore.com
^ permalink raw reply
* [PATCH v2] lockdown: Only log restrictions once
From: Daniel Tang @ 2025-11-19 18:07 UTC (permalink / raw)
To: Nicolas Bouchinet, Xiu Jianfeng, Paul Moore
Cc: linux-security-module, linux-kernel, Nathan Lynch,
Matthew Garrett, Kees Cook, David Howells, James Morris
In-Reply-To: <CAHC9VhQNGnm6NBSrUmfwoEwAxqedYbHckEkb+J47W5gWjrKBOA@mail.gmail.com>
KDE's lockscreen causes systemd-logind to spam dmesg about hibernation.
systemd declined to cache /sys/power/state due to runtime changeability.
Link: https://github.com/systemd/systemd/pull/39802
Signed-off-by: Daniel Tang <danielzgtg.opensource@gmail.com>
---
security/lockdown/lockdown.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
index cf83afa1d879..83b50de52f21 100644
--- a/security/lockdown/lockdown.c
+++ b/security/lockdown/lockdown.c
@@ -58,13 +58,16 @@ early_param("lockdown", lockdown_param);
*/
static int lockdown_is_locked_down(enum lockdown_reason what)
{
+ static volatile unsigned long lockdown_reasons_seen;
+ static_assert(ARRAY_SIZE(lockdown_reasons) < sizeof(lockdown_reasons_seen) * 8);
+
if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
"Invalid lockdown reason"))
return -EPERM;
if (kernel_locked_down >= what) {
- if (lockdown_reasons[what])
- pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
+ if (lockdown_reasons[what] && !test_and_set_bit(what, &lockdown_reasons_seen))
+ pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
current->comm, lockdown_reasons[what]);
return -EPERM;
}
--
2.51.0
^ permalink raw reply related
* Re: [PATCH v3 7/9] module: Move lockdown check into generic module loader
From: Paul Moore @ 2025-11-19 19:55 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Thomas Weißschuh, Masahiro Yamada, Nathan Chancellor,
Arnd Bergmann, Luis Chamberlain, Petr Pavlu, Sami Tolvanen,
Daniel Gomez, James Morris, Serge E. Hallyn, Jonathan Corbet,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy, Naveen N Rao, Mimi Zohar, Roberto Sassu,
Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
Christian Heusel, Câju Mihai-Drosi, linux-kbuild,
linux-kernel, linux-arch, linux-modules, linux-security-module,
linux-doc, linuxppc-dev, linux-integrity
In-Reply-To: <20251119112055.W1l5FOxc@linutronix.de>
On Wed, Nov 19, 2025 at 6:20 AM Sebastian Andrzej Siewior
<bigeasy@linutronix.de> wrote:
> On 2025-04-29 15:04:34 [+0200], Thomas Weißschuh wrote:
> > The lockdown check buried in module_sig_check() will not compose well
> > with the introduction of hash-based module validation.
>
> An explanation of why would be nice.
/me shrugs
I thought the explanation was sufficient.
> > Move it into module_integrity_check() which will work better.
> >
> > Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
--
paul-moore.com
^ permalink raw reply
* [RFC v1 0/1] Implement IMA Event Log Trimming
From: Anirudh Venkataramanan @ 2025-11-19 21:33 UTC (permalink / raw)
To: linux-integrity
Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
Anirudh Venkataramanan, Steven Chen, Gregory Lumen,
Lakshmi Ramasubramanian, Sush Shringarputale
==========================================================================
| A. Introduction |
==========================================================================
IMA events are kept in kernel memory and preserved across kexec soft
reboots. This can lead to increased kernel memory usage over time,
especially with aggressive IMA policies that measure everything. To reduce
memory pressure, it becomes necessary to discard IMA events but given that
IMA events are extended into PCRs in the TPM, just discarding events will
break the PCR extension chain, making future verification of the IMA event
log impossible.
This patch series proposes a method to discard IMA events while keeping the
log verifiable. While reducing memory pressure is the primary objective,
the second order benefit of trimming the IMA log is that IMA log verifiers
(local userspace daemon or a remote cloud service) can process smaller IMA
logs on a rolling basis, thus avoiding re-verification of previously
verified events.
The method has other advantages too:
1. It provides a userspace interface that can be used to precisely control
trim point, allowing for trim points to be optionally aligned with
userspace IMA event log validation.
2. It ensures that all necessary information required for continued IMA
log validation is made available via the userspace interface at all
times.
3. It provides a simple mechanism for userspace applications to determine
if the event log has been unexpectedly trimmed.
4. The duration for which the IMA Measurement list mutex must be held (for
trimming) is minimal.
==========================================================================
| B. Solution |
==========================================================================
--------------------------------------------------------------------------
| B.1 Overview |
--------------------------------------------------------------------------
The kernel trims the IMA event log based on PCR values supplied by userspace.
The core principles leveraged are as follows:
- Given an IMA event log, PCR values for each IMA event can be obtained by
recalulating the PCR extension for each event. Thus processing N events
from the start will yield PCR values as of event N. This is referred to
as "IMA event log replay".
- To get the PCR value for event N + 1, only the PCR value as of event N
is needed. If this can be known, events till and including N can be
safely purged.
Putting it all together, we get the following userspace + kernel flow:
1. A userspace application replays the IMA event log to generate PCR
values and then triggers a trim by providing these values to the kernel
(by writing to a pseudo file).
Optionally, the userspace application may verify these PCR values
against the corresponding TPM quote, and trigger trimming only if
the calculated PCR values match up to the expectations in the quote's
PCR digest.
2. The kernel uses the userspace supplied PCR values to trim the IMA
measurements list at a specific point, and so these are referred to as
"trim-to PCR values" in this context.
Note that the kernel doesn't really understand what these userspace
provided PCR values mean or what IMA event they correspond to, and so
it does its own IMA event replay till either the replayed PCR values
match with the userspace provided ones, or it runs out of events.
If a match is found, the kernel can proceed with trimming the IMA
measurements list. This is done in two steps, to keep locking context
minimal.
step 1: Find and return the list entry (as a count from head) of exact
match. This does not lock the measurements list mutex, ensuring
new events can be appended to the log.
step 2: Lock the measurements list mutex and trim the measurements list
at the previously identified list entry.
If the trim is successful, the trim-to PCR values are saved as "starting
PCR values". The next time userspace wants to replay the IMA event log,
it will use the starting PCR values as the base for the IMA event log
replay.
--------------------------------------------------------------------------
| B.2 Kernel Interfaces |
--------------------------------------------------------------------------
A new configfs pseudo file /sys/kernel/config/ima/pcrs that supports the
following operations is exposed.
read: returns starting PCR values stored in the kernel (within IMA
specifically).
write: writes trim-to PCR values to trigger trimming. If trimming is
successful, trim-to PCR values are stored as starting PCR values.
requires root privileges.
--------------------------------------------------------------------------
| B.3 Walk-through with a real example |
--------------------------------------------------------------------------
This is a real example from a test run.
Suppose this IMA policy is deployed:
measure func=FILE_CHECK mask=MAY_READ pcr=10
measure func=FILE_CHECK mask=MAY_WRITE pcr=11
When the policy is deployed, a zero digest starting PCR value will be set
for each PCR used. If the TPM supports multiple hashbanks, there will be
one starting PCR value per PCR, per TPM hashbank. This can be seen in the
following hexdump:
$ sudo hexdump -vC /sys/kernel/config/ima/pcrs
00000000 70 63 72 31 30 3a 73 68 61 31 3a 00 00 00 00 00 |pcr10:sha1:.....|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 |...............p|
00000020 63 72 31 31 3a 73 68 61 31 3a 00 00 00 00 00 00 |cr11:sha1:......|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 63 |..............pc|
00000040 72 31 30 3a 73 68 61 32 35 36 3a 00 00 00 00 00 |r10:sha256:.....|
00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000060 00 00 00 00 00 00 00 00 00 00 00 70 63 72 31 31 |...........pcr11|
00000070 3a 73 68 61 32 35 36 3a 00 00 00 00 00 00 00 00 |:sha256:........|
00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000090 00 00 00 00 00 00 00 00 70 63 72 31 30 3a 73 68 |........pcr10:sh|
000000a0 61 33 38 34 3a 00 00 00 00 00 00 00 00 00 00 00 |a384:...........|
000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
000000d0 00 00 00 00 00 70 63 72 31 31 3a 73 68 61 33 38 |.....pcr11:sha38|
000000e0 34 3a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |4:..............|
000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000110 00 00 |..|
00000112
Let's say that a userspace utility replays the IMA event log, and triggers
trimming by writing the following PCR values (i.e. trim-to PCR values) to the
pseudo file:
pcr10:sha256:8268782906555cf3aefc179f815c878527dd4e67eaa836572ebabab31977922c
pcr11:sha256:4c7f31927183eacb53d51d95b0162916fd3fca51a8d1efc6dde3805eb891fe41
The trim is successful,
1. Some number of entries from the measurements log will disappear. This
can be verified by reading out the ASCII or binary IMA measurements
file.
2. The trim-to PCR values are saved as starting PCR values. This can be
verified by reading out the pseudo file again as shown below. Note that
even through only sha256 PCR values were written, the kernel populated
sha1 and sha384 starting values as well.
$ sudo hexdump -vC /sys/kernel/config/ima/pcrs
00000000 70 63 72 31 30 3a 73 68 61 31 3a c4 7f 9d 00 68 |pcr10:sha1:....h|
00000010 e4 86 71 bf bc ae f0 10 12 ff 68 e2 9e 74 e4 70 |..q.......h..t.p|
00000020 63 72 31 31 3a 73 68 61 31 3a 90 d7 17 ac 60 4d |cr11:sha1:....`M|
00000030 c8 25 ce 77 7d 9d 94 cf 44 7b b2 2e 2e e2 70 63 |.%.w}...D{....pc|
00000040 72 31 30 3a 73 68 61 32 35 36 3a 82 68 78 29 06 |r10:sha256:.hx).|
00000050 55 5c f3 ae fc 17 9f 81 5c 87 85 27 dd 4e 67 ea |U\......\..'.Ng.|
00000060 a8 36 57 2e ba ba b3 19 77 92 2c 70 63 72 31 31 |.6W.....w.,pcr11|
00000070 3a 73 68 61 32 35 36 3a 4c 7f 31 92 71 83 ea cb |:sha256:L.1.q...|
00000080 53 d5 1d 95 b0 16 29 16 fd 3f ca 51 a8 d1 ef c6 |S.....)..?.Q....|
00000090 dd e3 80 5e b8 91 fe 41 70 63 72 31 30 3a 73 68 |...^...Apcr10:sh|
000000a0 61 33 38 34 3a 8e d6 12 18 b1 d6 cd 95 16 98 33 |a384:..........3|
000000b0 2b 7d a2 d6 d9 05 c7 e8 5b 15 b0 91 c5 fc 23 d1 |+}......[.....#.|
000000c0 f9 a8 8d 60 50 5c e9 64 5f d7 b3 b2 f1 9c 90 0a |...`P\.d_.......|
000000d0 45 53 5d b2 57 70 63 72 31 31 3a 73 68 61 33 38 |ES].Wpcr11:sha38|
000000e0 34 3a 25 fc 21 28 31 5a f7 c6 fb 0f 40 c9 06 e6 |4:%.!(1Z....@...|
000000f0 c5 da ed 20 61 a1 03 54 4f 67 18 88 82 0f 48 d1 |... a..TOg....H.|
00000100 2f e0 3d 36 46 5e 94 a4 88 51 f8 91 39 7e e5 97 |/.=6F^...Q..9~..|
00000110 2c c5 |,.|
00000112
--------------------------------------------------------------------------
| C. Footnotes |
--------------------------------------------------------------------------
1. The 'pcrs' pseudo file is currently part of configfs. This was due to
some early internal feedback in a different context. This can as well be
in securityfs with the rest of the IMA pseudo files.
2. PCR values are never read out of the TPM at any point. All PCR values
used are derived from IMA event log replay.
3. Code is "RFC quality". Refinements can be made if the method is accepted.
4. For functional validation, base kernel version was 6.17 stable, with the
most recent tested version being 6.17.8.
5. Code has been validated to some degree using a python-based internal test
tool. This can be published if there is community interest.
Steven Chen (1):
ima: Implement IMA event log trimming
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ima/Kconfig | 13 +
drivers/ima/Makefile | 2 +
drivers/ima/ima_config_pcrs.c | 291 ++++++++++++++++++
include/linux/ima.h | 27 ++
security/integrity/ima/Makefile | 4 +
security/integrity/ima/ima.h | 8 +
security/integrity/ima/ima_init.c | 44 +++
security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
security/integrity/ima/ima_policy.c | 7 +-
security/integrity/ima/ima_queue.c | 5 +-
12 files changed, 821 insertions(+), 4 deletions(-)
create mode 100644 drivers/ima/Kconfig
create mode 100644 drivers/ima/Makefile
create mode 100644 drivers/ima/ima_config_pcrs.c
create mode 100644 security/integrity/ima/ima_log_trim.c
--
2.43.0
^ permalink raw reply
* [RFC v1 1/1] ima: Implement IMA event log trimming
From: Anirudh Venkataramanan @ 2025-11-19 21:33 UTC (permalink / raw)
To: linux-integrity
Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
Anirudh Venkataramanan, Steven Chen, Gregory Lumen,
Lakshmi Ramasubramanian, Sush Shringarputale
In-Reply-To: <20251119213359.39397-1-anirudhve@linux.microsoft.com>
From: Steven Chen <chenste@linux.microsoft.com>
Implement a PCR value based method for IMA event log trimming by
exposing a new pseudo file /sys/kernel/config/ima/pcrs. This
pseudo file allows userspace to:
- read IMA starting PCR values.
- write trim-to PCR values to trigger IMA log trimming.
If a trimming operation is successful, one or more entries will be
purged from the IMA measurements list in the kernel.
Signed-off-by: Steven Chen <chenste@linux.microsoft.com>
Signed-off-by: Anirudh Venkataramanan <anirudhve@linux.microsoft.com>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/ima/Kconfig | 13 +
drivers/ima/Makefile | 2 +
drivers/ima/ima_config_pcrs.c | 291 ++++++++++++++++++
include/linux/ima.h | 27 ++
security/integrity/ima/Makefile | 4 +
security/integrity/ima/ima.h | 8 +
security/integrity/ima/ima_init.c | 44 +++
security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
security/integrity/ima/ima_policy.c | 7 +-
security/integrity/ima/ima_queue.c | 5 +-
12 files changed, 821 insertions(+), 4 deletions(-)
create mode 100644 drivers/ima/Kconfig
create mode 100644 drivers/ima/Makefile
create mode 100644 drivers/ima/ima_config_pcrs.c
create mode 100644 security/integrity/ima/ima_log_trim.c
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 4915a63866b0..35b83be86c42 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -251,4 +251,6 @@ source "drivers/hte/Kconfig"
source "drivers/cdx/Kconfig"
+source "drivers/ima/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 8e1ffa4358d5..3aad6096d416 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -197,3 +197,4 @@ obj-$(CONFIG_DPLL) += dpll/
obj-$(CONFIG_DIBS) += dibs/
obj-$(CONFIG_S390) += s390/
+obj-$(CONFIG_IMA_PCRS) += ima/
diff --git a/drivers/ima/Kconfig b/drivers/ima/Kconfig
new file mode 100644
index 000000000000..9e465fbe3adb
--- /dev/null
+++ b/drivers/ima/Kconfig
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config IMA_PCRS
+ bool "IMA Event Log Trimming"
+ default n
+ help
+ Say Y here if you want support for IMA Event Log Trimming.
+
+ This creates the configfs file /sys/kernel/config/ima/pcrs.
+ Userspace
+ - writes to this file to trigger IMA event log trimming
+ - reads this file to get IMA starting PCR values
+
+ If unsure, say N.
diff --git a/drivers/ima/Makefile b/drivers/ima/Makefile
new file mode 100644
index 000000000000..ac9b9b96b5cb
--- /dev/null
+++ b/drivers/ima/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_IMA_PCRS) += ima_config_pcrs.o
diff --git a/drivers/ima/ima_config_pcrs.c b/drivers/ima/ima_config_pcrs.c
new file mode 100644
index 000000000000..f329665f2b90
--- /dev/null
+++ b/drivers/ima/ima_config_pcrs.c
@@ -0,0 +1,291 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Microsoft Corporation
+ *
+ * Authors:
+ * Steven Chen <chenste@linux.microsoft.com>
+ *
+ * File: ima_config_pcrs.c
+ *
+ * Implements IMA interface to allow userspace to read IMA starting PCR
+ * values and trigger IMA event log trimming.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/configfs.h>
+#include <linux/printk.h>
+#include <linux/tpm.h>
+#include <linux/ima.h>
+/*
+ * The IMA PCR configfs is a childless subsystem. It cannot create
+ * any config_items. It just has attribute pcrs for read and write.
+ */
+
+#define MAX_HASH_ALGO_NAME_LENGTH 16 /*sha1, sha256, ...*/
+#define PCR_NAME_LEN 6 /*pcrxx:*/
+#define MIN_PCR_RECORD_LENGTH 20
+#define OFFSET_IDX_1 3
+#define OFFSET_IDX_2 4
+#define OFFSET_COLON 5
+
+/*
+ * mutex protects atomicity of trimming measurement list
+ * and updating ima_start_point_pcr_values
+ * protects concurrent writes to the IMA PCR values
+ * This also not allow memory allocation while waiting the mutex
+ */
+static DEFINE_MUTEX(ima_pcr_write_mutex);
+
+/* show current IMA Start Point PCR values in ascii format */
+static ssize_t ima_config_pcrs_ascii_show(struct config_item *item, char *page)
+{
+ int len = 0;
+
+ for (int i = 0; i < num_tpm_banks; i++) {
+ for (int j = 0; j < num_pcr_configured; j++) {
+ int algorithm_id = ima_start_point_pcr_values[i].alg_id;
+
+ /*
+ * Write "pcrXX:AAA:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ * where XX is the PCR index (00-23),
+ * where AAA is the algorithm name, such as sha1, sha256, sha384..
+ * and x...x is the digest data in hex format
+ */
+ len += sprintf(page + len, "pcr%02d:", digests_pcr_map[j]);
+ len += sprintf(page + len, "%s:", hash_algo_name[algorithm_id]);
+ for (int k = 0; k < hash_digest_size[algorithm_id]; k++)
+ len += sprintf(page + len, "%02x",
+ ima_start_point_pcr_values[i].digests[j][k]);
+ len += sprintf(page + len, "\n");
+ }
+ }
+ return len;
+}
+
+/* show current IMA Start Point PCR values */
+static ssize_t ima_config_pcrs_show(struct config_item *item, char *page)
+{
+ int len = 0;
+
+ for (int i = 0; i < num_tpm_banks; i++) {
+ int algorithm_id = ima_start_point_pcr_values[i].alg_id;
+
+ for (int j = 0; j < TPM2_PLATFORM_PCR; ++j) {
+ int digest_index = pcr_digests_map[j];
+
+ if (digest_index == -1)
+ continue;
+ /*
+ * Write "pcrXX:AAA:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
+ * where XX is the PCR index (00-23),
+ * where AAA is the algorithm name, such as sha1, sha256, sha384..
+ * and x...x is the digest data in raw format
+ */
+ len += sprintf(page + len, "pcr%02d:", j);
+ len += sprintf(page + len, "%s:", hash_algo_name[algorithm_id]);
+
+ memcpy(page + len, ima_start_point_pcr_values[i].digests[digest_index],
+ hash_digest_size[algorithm_id]);
+
+ len += hash_digest_size[algorithm_id];
+ }
+ }
+
+ return len;
+}
+
+/* get PCR index from input string */
+static int get_pcr_idx(char *p, int offset)
+{
+ /* validate digits first */
+ if (!isdigit((unsigned char)p[offset + OFFSET_IDX_1]) ||
+ !isdigit((unsigned char)p[offset + OFFSET_IDX_2]))
+ return -EINVAL;
+
+ return (p[offset + OFFSET_IDX_1] - '0') * 10 + (p[offset + OFFSET_IDX_2] - '0');
+}
+
+/*
+ * Parse the input data
+ * Get new PCR values and trim IMA event log
+ */
+static ssize_t ima_config_pcrs_store(struct config_item *item, const char *page, size_t count)
+{
+ struct ima_pcr_value *ima_pcr_values;
+ unsigned long pcr_found = 0;
+ char *p = (char *)page;
+ int pcr_count;
+ int offset = 0;
+ int ret = -EINVAL;
+
+ if (count <= 0)
+ return ret;
+
+ if (num_pcr_configured <= 0)
+ return -ENOMEM;
+
+ /*
+ * Only one thread can write to the PCRs at a time
+ * Not allocate memory while waiting the mutex
+ */
+
+ mutex_lock(&ima_pcr_write_mutex);
+
+ pcr_count = num_pcr_configured;
+
+ ima_pcr_values = kcalloc(num_pcr_configured, sizeof(*ima_pcr_values), GFP_KERNEL);
+
+ if (!ima_pcr_values) {
+ ret = -ENOMEM;
+ goto out_notrim_unlock;
+ }
+
+ /*
+ * parse the input data
+ * each entry is like: pcrX:AAA:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ * where XX is the PCR index (00-23),
+ * where AAA is the algorithm name, e.g. sha1, sha256, sha384..
+ * and x...x is the digest data in binary format
+ * multiple entries are concatenated together
+ */
+ while (offset < count) {
+ int index, mapped_index, algo_id, algo_name_len = 0;
+ char algo_name[MAX_HASH_ALGO_NAME_LENGTH];
+ u8 digest_len;
+
+ /* Need at least "pcrXX:AAA:" */
+ if (offset + MIN_PCR_RECORD_LENGTH > count)
+ goto out_nottrim;
+
+ if (memcmp(p + offset, "pcr", 3) != 0 || p[offset + OFFSET_COLON] != ':') {
+ pr_alert("invalid input format\n");
+ goto out_nottrim;
+ }
+
+ /* Get the PCR index*/
+ index = get_pcr_idx(p, offset);
+ if (index < 0 || index >= TPM2_PLATFORM_PCR) {
+ pr_alert("invalid PCR index: %d\n", index);
+ goto out_nottrim;
+ }
+
+ offset += PCR_NAME_LEN;
+
+ while (offset + algo_name_len < count && p[offset + algo_name_len] != ':')
+ ++algo_name_len;
+
+ /* Check actual algo name length */
+ if (algo_name_len == 0 || algo_name_len >= MAX_HASH_ALGO_NAME_LENGTH) {
+ pr_err("ima pcr configuration: invalid algorithm name length\n");
+ goto out_nottrim;
+ }
+
+ memcpy(algo_name, p + offset, algo_name_len);
+ algo_name[algo_name_len] = '\0';
+
+ algo_id = match_string(hash_algo_name, HASH_ALGO__LAST, algo_name);
+ /* validate algo_id */
+ if (algo_id < HASH_ALGO_SHA1 || algo_id >= HASH_ALGO__LAST) {
+ pr_err("ima pcr configuration: invalid algorithm ID\n");
+ goto out_nottrim;
+ }
+ digest_len = hash_digest_size[algo_id];
+ offset += algo_name_len + 1;
+ /* validate we have enough data for the digest */
+ if (offset + digest_len > count)
+ goto out_nottrim;
+
+ mapped_index = pcr_digests_map[index];
+
+ if (pcr_digests_map[index] != -1 && !test_bit(index, &pcr_found)) {
+ ima_pcr_values[mapped_index].algo_id = algo_id;
+ set_bit(index, &pcr_found);
+ memcpy(ima_pcr_values[mapped_index].digest, p + offset, digest_len);
+ --pcr_count;
+ } else {
+ pr_alert("invalid PCR index or duplicate PCR set index: %d\n", index);
+ goto out_nottrim;
+ }
+ offset += digest_len;
+ }
+
+ if (pcr_count != 0) {
+ /* not all PCRs are provided */
+ pr_alert("not all PCRs are provided\n");
+ goto out_nottrim;
+ }
+
+ /*
+ * all configured PCRs values are provided, now recalculate the IMA PCRs
+ * and check if they can match the these PCR values
+ * Trim the IMA event logs if the given PCR values match
+ */
+ ret = ima_trim_event_log((const void *)ima_pcr_values);
+ if (ret >= 0)
+ ret = count;
+
+out_nottrim:
+ kfree(ima_pcr_values);
+out_notrim_unlock:
+ mutex_unlock(&ima_pcr_write_mutex);
+ return ret;
+}
+
+CONFIGFS_ATTR(ima_config_, pcrs);
+CONFIGFS_ATTR_RO(ima_config_, pcrs_ascii);
+
+static struct configfs_attribute *ima_config_pcr_attrs[] = {
+ &ima_config_attr_pcrs,
+ &ima_config_attr_pcrs_ascii,
+ NULL,
+};
+
+static const struct config_item_type ima_config_pcr_type = {
+ .ct_attrs = ima_config_pcr_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem config_ima_pcrs = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "ima",
+ .ci_type = &ima_config_pcr_type,
+ },
+ },
+};
+
+static int __init configfs_ima_pcrs_init(void)
+{
+ struct tpm_chip *tpm_chip;
+ int ret;
+
+ tpm_chip = tpm_default_chip();
+ if (!tpm_chip) {
+ pr_err("No TPM chip found, IMA PCR configfs disabled\n");
+ return -ENODEV;
+ }
+
+ config_group_init(&config_ima_pcrs.su_group);
+ mutex_init(&config_ima_pcrs.su_mutex);
+ ret = configfs_register_subsystem(&config_ima_pcrs);
+ if (ret) {
+ pr_err("Error %d while registering subsystem %s\n",
+ ret, config_ima_pcrs.su_group.cg_item.ci_namebuf);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit configfs_ima_pcrs_exit(void)
+{
+ configfs_unregister_subsystem(&config_ima_pcrs);
+}
+
+module_init(configfs_ima_pcrs_init);
+module_exit(configfs_ima_pcrs_exit);
+MODULE_DESCRIPTION("IMA PCRS configfs module");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 8e29cb4e6a01..f8a6209b9423 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -12,6 +12,8 @@
#include <linux/security.h>
#include <linux/kexec.h>
#include <crypto/hash_info.h>
+#include <linux/tpm.h>
+
struct linux_binprm;
#ifdef CONFIG_IMA
@@ -24,6 +26,31 @@ extern int ima_measure_critical_data(const char *event_label,
const void *buf, size_t buf_len,
bool hash, u8 *digest, size_t digest_len);
+#ifdef CONFIG_IMA_PCRS
+struct ima_pcr_value {
+ u8 digest[HASH_MAX_DIGESTSIZE]; /* PCR value */
+ u8 algo_id; /* Hash algorithm ID */
+};
+
+struct tpm_bank_pcr_values {
+ u16 alg_id;
+ u8 **digests; /* Array of pointers, one per configured PCR */
+};
+
+extern u8 num_tpm_banks;
+extern u8 num_pcr_configured;
+
+extern signed char algo_pcr_bank_map[HASH_ALGO__LAST];
+extern signed char pcr_digests_map[TPM2_PLATFORM_PCR];
+extern signed char digests_pcr_map[TPM2_PLATFORM_PCR];
+
+extern struct tpm_bank_pcr_values *ima_start_point_pcr_values;
+
+extern int ima_trim_event_log(const void *bin);
+#else
+static inline int ima_trim_event_log(const void *bin) { return 0; }
+#endif /* CONFIG_IMA_PCRS */
+
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
extern void ima_appraise_parse_cmdline(void);
#else
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index b376d38b4ee6..ae9ce210638d 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -18,3 +18,7 @@ ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
ifeq ($(CONFIG_EFI),y)
ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
endif
+
+ifeq ($(CONFIG_IMA_PCRS),y)
+ima-y += ima_log_trim.o
+endif
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e3d71d8d56e3..d9accf504e42 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -55,6 +55,8 @@ struct ima_algo_desc {
enum hash_algo algo;
};
+extern struct mutex ima_extend_list_mutex; /* protects extending measurement list */
+
/* set during initialization */
extern int ima_hash_algo __ro_after_init;
extern int ima_sha1_idx __ro_after_init;
@@ -250,6 +252,12 @@ void ima_measure_kexec_event(const char *event_name);
static inline void ima_measure_kexec_event(const char *event_name) {}
#endif
+#ifdef CONFIG_IMA_PCRS
+extern int ima_add_configured_pcr(int new_pcr_index);
+#else
+static inline int ima_add_configured_pcr(int new_pcr_index) { return 0; }
+#endif /* CONFIG_IMA_PCRS */
+
/*
* The default binary_runtime_measurements list format is defined as the
* platform native format. The canonical format is defined as little-endian.
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index a2f34f2d8ad7..1102d2073336 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -24,6 +24,16 @@
const char boot_aggregate_name[] = "boot_aggregate";
struct tpm_chip *ima_tpm_chip;
+#ifdef CONFIG_IMA_PCRS
+u8 num_tpm_banks;
+u8 num_pcr_configured;
+signed char algo_pcr_bank_map[HASH_ALGO__LAST];
+signed char digests_pcr_map[TPM2_PLATFORM_PCR];
+signed char pcr_digests_map[TPM2_PLATFORM_PCR];
+
+struct tpm_bank_pcr_values *ima_start_point_pcr_values;
+#endif
+
/* Add the boot aggregate to the IMA measurement list and extend
* the PCR register.
*
@@ -134,6 +144,40 @@ int __init ima_init(void)
if (rc != 0)
return rc;
+#ifdef CONFIG_IMA_PCRS
+
+ num_tpm_banks = 0;
+ num_pcr_configured = 0;
+
+ for (int i = 0; i < TPM2_PLATFORM_PCR; i++) {
+ pcr_digests_map[i] = -1;
+ digests_pcr_map[i] = -1;
+ }
+
+ for (int i = 0; i < HASH_ALGO__LAST; i++)
+ algo_pcr_bank_map[i] = -1;
+
+ if (ima_tpm_chip) {
+ num_tpm_banks = ima_tpm_chip->nr_allocated_banks;
+ /* Allocate memory for the ima start point PCR values */
+ ima_start_point_pcr_values = kcalloc(num_tpm_banks,
+ sizeof(*ima_start_point_pcr_values),
+ GFP_KERNEL);
+
+ if (!ima_start_point_pcr_values)
+ return -ENOMEM;
+
+ for (int i = 0; i < num_tpm_banks; i++) {
+ int algo_id = ima_tpm_chip->allocated_banks[i].crypto_id;
+
+ ima_start_point_pcr_values[i].alg_id = algo_id;
+ algo_pcr_bank_map[algo_id] = i;
+ }
+
+ ima_add_configured_pcr(CONFIG_IMA_MEASURE_PCR_IDX);
+ }
+#endif
+
/* It can be called before ima_init_digests(), it does not use TPM. */
ima_load_kexec_buffer();
diff --git a/security/integrity/ima/ima_log_trim.c b/security/integrity/ima/ima_log_trim.c
new file mode 100644
index 000000000000..85025d502db2
--- /dev/null
+++ b/security/integrity/ima/ima_log_trim.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2025 Microsoft Corporation
+ *
+ * Authors:
+ * Steven Chen <chenste@linux.microsoft.com>
+ *
+ * File: ima_log_trim.c
+ * Implements PCR value based IMA Event Log Trimming
+ */
+
+#include <linux/fcntl.h>
+#include <linux/kernel_read_file.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/vmalloc.h>
+#include <linux/ima.h>
+#include <crypto/hash_info.h>
+
+#include "ima.h"
+
+struct digest_value {
+ u8 digest[HASH_MAX_DIGESTSIZE]; /* digest value */
+};
+
+/* Average IMA event log data length */
+#define EXTEND_BUF_LEN 300
+
+static struct digest_value *ima_extended_pcr;
+static struct ima_pcr_value *target_pcr_values;
+static unsigned long pcr_matched;
+static int pcr_match_needed;
+
+/* Calculate the hash digest for the given data using the specified algorithm */
+static int ima_calculate_pcr(const void *data, size_t len, u8 *md, int algo)
+{
+ struct crypto_shash *tfm;
+ struct shash_desc *desc;
+ int ret = 0, size;
+
+ tfm = crypto_alloc_shash(hash_algo_name[algo], 0, 0);
+
+ if (IS_ERR(tfm)) {
+ ret = -ENOMEM;
+ goto out_nocleanup;
+ }
+
+ /* Allocate memory for the shash_desc structure and initialize it */
+ size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm);
+
+ desc = kzalloc(size, GFP_KERNEL);
+ if (!desc) {
+ crypto_free_shash(tfm);
+ ret = -ENOMEM;
+ goto out_nocleanup;
+ }
+
+ desc->tfm = tfm;
+
+ if (crypto_shash_init(desc)) {
+ ret = -EINVAL;
+ goto out_cleanup;
+ }
+
+ /* Update the hash with the input data */
+ if (crypto_shash_update(desc, data, len)) {
+ ret = -EINVAL;
+ goto out_cleanup;
+ }
+
+ /* Finalize the hash and retrieve the digest */
+ ret = crypto_shash_final(desc, md);
+ if (ret)
+ goto out_cleanup;
+
+ ret = crypto_shash_digestsize(tfm);
+
+out_cleanup:
+ kfree(desc);
+ crypto_free_shash(tfm);
+out_nocleanup:
+ return ret;
+}
+
+/* Add a new configured PCR */
+int ima_add_configured_pcr(int new_pcr_index)
+{
+ if (!ima_tpm_chip)
+ return -1;
+
+ if (new_pcr_index < 0 || new_pcr_index >= TPM2_PLATFORM_PCR)
+ return -1;
+
+ if (pcr_digests_map[new_pcr_index] != -1)
+ return 0;
+
+ /*
+ * For each TPM bank, expand one more entry for the new PCR
+ * Allocate new PCR digest buffers for the new PCR
+ */
+ for (int i = 0; i < num_tpm_banks; ++i) {
+ int length = hash_digest_size[ima_start_point_pcr_values[i].alg_id];
+ u8 *new_pcr = kmalloc(sizeof(u8) * length, GFP_KERNEL);
+ u8 **new_buf;
+
+ if (!new_pcr)
+ return -ENOMEM;
+ memset(new_pcr, 0, sizeof(u8) * length);
+ new_buf = (u8 **)krealloc(ima_start_point_pcr_values[i].digests,
+ (num_pcr_configured + 1) * sizeof(u8 *), GFP_KERNEL);
+ if (!new_buf) {
+ kfree(new_pcr);
+ return -ENOMEM;
+ }
+ new_buf[num_pcr_configured] = new_pcr;
+ ima_start_point_pcr_values[i].digests = new_buf;
+ }
+
+ pcr_digests_map[new_pcr_index] = num_pcr_configured;
+ digests_pcr_map[num_pcr_configured] = new_pcr_index;
+
+ ++num_pcr_configured;
+ return 0;
+}
+
+/* Delete the IMA event logs */
+static int ima_purge_event_log(int number_logs)
+{
+ struct ima_queue_entry *qe;
+ int cur = 0;
+
+ mutex_lock(&ima_extend_list_mutex);
+ rcu_read_lock();
+
+ /*
+ * Remove this entry from both hash table and the measurement list
+ * When removing from hash table, decrease the length counter
+ * so that the hash table re-sizing logic works correctly
+ */
+ list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ /* if CONFIG_IMA_DISABLE_HTABLE is set, the hash table is not used */
+ if (!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE))
+ hlist_del_rcu(&qe->hnext);
+
+ atomic_long_dec(&ima_htable.len);
+ list_del_rcu(&qe->later);
+ ++cur;
+ if (cur >= number_logs)
+ break;
+ }
+
+ rcu_read_unlock();
+ mutex_unlock(&ima_extend_list_mutex);
+ return cur;
+}
+
+/* compare the target PCR values with IMA Start Point PCR Values */
+static int ima_compare_pcr_values(struct ima_pcr_value *target_pcr_val)
+{
+ int count_not_matched = 0;
+
+ for (int i = 0; i < num_pcr_configured; ++i) {
+ int algo_id_tmp;
+ int bank_id_tmp;
+ int hash_size;
+
+ algo_id_tmp = target_pcr_val[i].algo_id;
+ bank_id_tmp = algo_pcr_bank_map[algo_id_tmp];
+ hash_size = hash_digest_size[algo_id_tmp];
+
+ if (bank_id_tmp < 0 || bank_id_tmp >= num_tpm_banks)
+ return -EINVAL;
+
+ /* already matched this PCR, skip it */
+ if (memcmp(ima_start_point_pcr_values[bank_id_tmp].digests[i],
+ target_pcr_val[i].digest, hash_size) == 0) {
+ set_bit(i, &pcr_matched);
+ continue;
+ }
+ count_not_matched++;
+ }
+ return count_not_matched;
+}
+
+static int ima_recalculate_check_pcrs(int pcr_map_index, char *org_pointer, char *digest_pointer,
+ int total_length, bool digest_zero)
+{
+ int ret = 0;
+
+ /* Recalculate the PCR for each bank */
+ for (int i = 0; i < num_tpm_banks; i++) {
+ int digest_len, crypto_id, hash_size, idx, len;
+ u8 digest[HASH_MAX_DIGESTSIZE];
+
+ crypto_id = ima_start_point_pcr_values[i].alg_id;
+ hash_size = hash_digest_size[crypto_id];
+ digest_len = 0;
+ /* Prepare digest buffer */
+ if (digest_zero) {
+ /* all zero digest, use 0xFF to extend */
+ memset(digest, 0xFF, HASH_MAX_DIGESTSIZE);
+ digest_len = hash_size;
+ } else {
+ memcpy(digest_pointer, org_pointer, total_length);
+ digest_len = ima_calculate_pcr(digest_pointer, total_length, digest,
+ crypto_id);
+ if (digest_len < 0) {
+ ret = digest_len;
+ break;
+ }
+ }
+
+ len = digest_len + hash_size;
+
+ idx = i * num_pcr_configured + pcr_map_index;
+ memcpy(digest_pointer, ima_extended_pcr[idx].digest, hash_size);
+ memcpy(digest_pointer + hash_size, digest, digest_len);
+
+ /* Recalculate the PCR starting with the IMA Start Point PCR value */
+ digest_len = ima_calculate_pcr(digest_pointer, len, ima_extended_pcr[idx].digest,
+ crypto_id);
+
+ if (digest_len < 0) {
+ ret = digest_len;
+ break;
+ }
+
+ /*
+ * Check if the extended PCR value matches the target PCR value
+ * if matched, mark this PCR as matched
+ * if all PCRs matched, set the entry_found flag
+ */
+ if (crypto_id == target_pcr_values[pcr_map_index].algo_id) {
+ if (memcmp(ima_extended_pcr[idx].digest,
+ target_pcr_values[pcr_map_index].digest, hash_size) == 0) {
+ set_bit(pcr_map_index, &pcr_matched);
+ --pcr_match_needed;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int ima_get_log_count(void)
+{
+ u8 algo_digest_buffer[EXTEND_BUF_LEN];
+ u8 digest_buffer[EXTEND_BUF_LEN];
+ struct ima_queue_entry *qe;
+ int count = 0;
+ int ret = 0;
+ unsigned int hash_digest_length;
+
+ /* Event log digests algorithm is SHA1 */
+ hash_digest_length = hash_digest_size[HASH_ALGO_SHA1];
+ list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ char *org_digest_pointer, *digest_pointer;
+ int pcr_idx, pcr_map_index, total_length;
+ struct ima_template_entry *e = qe->entry;
+ bool digest_zero;
+
+ if (!qe->entry) {
+ ret = -EINVAL;
+ break;
+ }
+ pcr_idx = e->pcr;
+ pcr_map_index = pcr_digests_map[pcr_idx];
+
+ if (test_bit(pcr_map_index, &pcr_matched) || pcr_digests_map[pcr_idx] == -1) {
+ /* already matched this PCR, something wrong */
+ ret = -EINVAL;
+ break;
+ }
+ /* The original digest buffer is used to save data for multiple banks/algorithms */
+ org_digest_pointer = digest_buffer;
+ digest_pointer = algo_digest_buffer;
+
+ total_length = e->template_data_len;
+
+ /* Allocate large memory for the original and digest buffers if needed */
+ if (total_length > EXTEND_BUF_LEN) {
+ org_digest_pointer = kzalloc(total_length, GFP_KERNEL);
+ if (!org_digest_pointer) {
+ ret = -ENOMEM;
+ break;
+ }
+ digest_pointer = kzalloc(total_length, GFP_KERNEL);
+ if (!digest_pointer) {
+ kfree(org_digest_pointer);
+ ret = -ENOMEM;
+ break;
+ }
+ }
+
+ digest_zero = true;
+ /*
+ * Check if the original digest is all zeros or not
+ * if not all zero, use template data to recalculate PCR
+ */
+ if (memchr_inv(e->digests->digest, 0, hash_digest_length) != NULL) {
+ int offset = 0;
+
+ for (int i = 0; i < e->template_desc->num_fields; i++) {
+ memcpy(org_digest_pointer + offset, &e->template_data[i].len,
+ sizeof(e->template_data[i].len));
+ offset += sizeof(e->template_data[i].len);
+ memcpy(org_digest_pointer + offset, e->template_data[i].data,
+ e->template_data[i].len);
+ offset += e->template_data[i].len;
+ }
+ digest_zero = false;
+ }
+
+ count++;
+
+ /* Check if this log entry can match the target PCRs */
+ ret = ima_recalculate_check_pcrs(pcr_map_index, org_digest_pointer,
+ digest_pointer, total_length, digest_zero);
+
+ if (total_length > EXTEND_BUF_LEN) {
+ kfree(org_digest_pointer);
+ kfree(digest_pointer);
+ }
+
+ /* If entry found or error occurred, break the loop */
+ if (ret < 0 || pcr_match_needed <= 0)
+ break;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ if (pcr_match_needed <= 0)
+ return count;
+ else
+ return 0;
+}
+
+/*
+ * Trim the IMA event log to match the given PCR values
+ * Return:
+ * >0: number of log entries removed
+ * 0: no log entries removed
+ * -1: error
+ * -ENOENT: no matching log entry found
+ * -EIO: I/O error when removing log entries
+ * -EINVAL: invalid parameter
+ * -ENOMEM: memory allocation failure
+ */
+int ima_trim_event_log(const void *bin)
+{
+ int count, ret = -1;
+
+ count = 0;
+ target_pcr_values = (struct ima_pcr_value *)bin;
+ pcr_matched = 0;
+
+ pcr_match_needed = ima_compare_pcr_values(target_pcr_values);
+
+ /* No need to trim */
+ if (pcr_match_needed <= 0) {
+ ret = 0;
+ goto out_nofree;
+ }
+
+ ima_extended_pcr = kcalloc(num_tpm_banks * num_pcr_configured,
+ sizeof(*ima_extended_pcr), GFP_KERNEL);
+
+ if (!ima_extended_pcr) {
+ ret = -ENOMEM;
+ goto out_nofree;
+ }
+
+ /* Initialize ima_extended_pcr with ima_start_point_pcr_values */
+ for (int i = 0; i < num_tpm_banks; i++) {
+ int length = hash_digest_size[ima_start_point_pcr_values[i].alg_id];
+
+ for (int j = 0; j < num_pcr_configured; ++j) {
+ int record_index = i * num_pcr_configured + j;
+
+ memcpy(ima_extended_pcr[record_index].digest,
+ ima_start_point_pcr_values[i].digests[j], length);
+ }
+ }
+
+ ret = ima_get_log_count();
+
+ if (ret <= 0)
+ goto out_notrim;
+
+ count = ret;
+
+ /* Remove logs from the IMA log list */
+ ret = ima_purge_event_log(count);
+
+ if (ret == count) {
+ /* Update the IMA Start Point PCR values */
+ for (int i = 0; i < num_tpm_banks; i++) {
+ int algorithm_id = ima_start_point_pcr_values[i].alg_id;
+ int hash_size = hash_digest_size[algorithm_id];
+
+ for (int j = 0; j < num_pcr_configured; j++) {
+ int ext_idx = i * num_pcr_configured + j;
+
+ memcpy(ima_start_point_pcr_values[i].digests[j],
+ ima_extended_pcr[ext_idx].digest, hash_size);
+ }
+ }
+ } else {
+ /* something wrong, should not happen */
+ ret = -EIO;
+ }
+
+out_notrim:
+ kfree(ima_extended_pcr);
+
+out_nofree:
+ return ret;
+}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 7468afaab686..fe537827ac1f 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1895,10 +1895,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
ima_log_string(ab, "pcr", args[0].from);
result = kstrtoint(args[0].from, 10, &entry->pcr);
- if (result || INVALID_PCR(entry->pcr))
+ if (result || INVALID_PCR(entry->pcr)) {
result = -EINVAL;
- else
+ } else {
entry->flags |= IMA_PCR;
+ if (ima_add_configured_pcr(entry->pcr) < 0)
+ result = -EINVAL;
+ }
break;
case Opt_template:
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 590637e81ad1..7bbfdd2ce3b0 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -39,11 +39,12 @@ struct ima_h_table ima_htable = {
.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
};
-/* mutex protects atomicity of extending measurement list
+/*
+ * This mutex protects atomicity of extending measurement list
* and extending the TPM PCR aggregate. Since tpm_extend can take
* long (and the tpm driver uses a mutex), we can't use the spinlock.
*/
-static DEFINE_MUTEX(ima_extend_list_mutex);
+DEFINE_MUTEX(ima_extend_list_mutex);
/*
* Used internally by the kernel to suspend measurements.
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] security: sctp: Format type and permission checks tables
From: Paul Moore @ 2025-11-20 0:10 UTC (permalink / raw)
To: Bagas Sanjaya, Linux Kernel Mailing List, Linux Documentation,
Linux Security Module
Cc: Jonathan Corbet, Jarkko Sakkinen, Christian Brauner,
Bagas Sanjaya, Jeff Layton, Kees Cook, Mickaël Salaün,
Stuart Yoder
In-Reply-To: <20251103113922.61232-2-bagasdotme@gmail.com>
On Nov 3, 2025 Bagas Sanjaya <bagasdotme@gmail.com> wrote:
>
> Use reST grid tables for both type and permission checks tables.
>
> Signed-off-by: Bagas Sanjaya <bagasdotme@gmail.com>
> ---
> This patch is based on lsm tree.
>
> Documentation/security/SCTP.rst | 48 +++++++++++++++++++++------------
> 1 file changed, 31 insertions(+), 17 deletions(-)
I rendered the patched file to HTML, and given that large portions of
the file are still rendered as monospaced preformatted text, the new
table rendering looks a bit out of place.
Let's stick with the existing table format until the entire document
is converted to something that is at least as constitent and
aesthetically pleasing as the current revision.
--
paul-moore.com
^ permalink raw reply
* Re: [PATCH] security: sctp: Format type and permission checks tables
From: Bagas Sanjaya @ 2025-11-20 1:39 UTC (permalink / raw)
To: Paul Moore, Linux Kernel Mailing List, Linux Documentation,
Linux Security Module
Cc: Jonathan Corbet, Jarkko Sakkinen, Christian Brauner, Jeff Layton,
Kees Cook, Mickaël Salaün, Stuart Yoder
In-Reply-To: <d5efca7df0d2e12ad9d6f3d35afa2828@paul-moore.com>
[-- Attachment #1: Type: text/plain, Size: 519 bytes --]
On Wed, Nov 19, 2025 at 07:10:31PM -0500, Paul Moore wrote:
> I rendered the patched file to HTML, and given that large portions of
> the file are still rendered as monospaced preformatted text, the new
> table rendering looks a bit out of place.
>
> Let's stick with the existing table format until the entire document
> is converted to something that is at least as constitent and
> aesthetically pleasing as the current revision.
OK, thanks!
--
An old man doll... just what I always wanted! - Clara
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH -next] ima: Handle error code returned by ima_filter_rule_match()
From: Zhao Yipeng @ 2025-11-20 7:18 UTC (permalink / raw)
To: zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg
Cc: lujialin4, linux-integrity, linux-security-module, linux-kernel
In ima_match_rules(), if ima_filter_rule_match() returns -ENOENT due to
the rule being NULL, the function incorrectly skips the 'if (!rc)' check
and sets 'result = true'. The LSM rule is considered a match, causing
extra files to be measured by IMA.
This issue can be reproduced in the following scenario:
After unloading the SELinux policy module via 'semodule -d', if an IMA
measurement is triggered before ima_lsm_rules is updated,
in ima_match_rules(), the first call to ima_filter_rule_match() returns
-ESTALE. This causes the code to enter the 'if (rc == -ESTALE &&
!rule_reinitialized)' block, perform ima_lsm_copy_rule() and retry. In
ima_lsm_copy_rule(), since the SELinux module has been removed, the rule
becomes NULL, and the second call to ima_filter_rule_match() returns
-ENOENT. This bypasses the 'if (!rc)' check and results in a false match.
Call trace:
selinux_audit_rule_match+0x310/0x3b8
security_audit_rule_match+0x60/0xa0
ima_match_rules+0x2e4/0x4a0
ima_match_policy+0x9c/0x1e8
ima_get_action+0x48/0x60
process_measurement+0xf8/0xa98
ima_bprm_check+0x98/0xd8
security_bprm_check+0x5c/0x78
search_binary_handler+0x6c/0x318
exec_binprm+0x58/0x1b8
bprm_execve+0xb8/0x130
do_execveat_common.isra.0+0x1a8/0x258
__arm64_sys_execve+0x48/0x68
invoke_syscall+0x50/0x128
el0_svc_common.constprop.0+0xc8/0xf0
do_el0_svc+0x24/0x38
el0_svc+0x44/0x200
el0t_64_sync_handler+0x100/0x130
el0t_64_sync+0x3c8/0x3d0
Fix this by changing 'if (!rc)' to 'if (rc <= 0)' to ensure that error
codes like -ENOENT do not bypass the check and accidentally result in a
successful match.
Fixes: 4af4662fa4a9d ("integrity: IMA policy")
Signed-off-by: Zhao Yipeng <zhaoyipeng5@huawei.com>
---
security/integrity/ima/ima_policy.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 128fab897930..db6d55af5a80 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -674,7 +674,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
goto retry;
}
}
- if (!rc) {
+ if (rc <= 0) {
result = false;
goto out;
}
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2] lockdown: Only log restrictions once
From: Xiujianfeng @ 2025-11-20 7:37 UTC (permalink / raw)
To: Daniel Tang, Nicolas Bouchinet, Xiu Jianfeng, Paul Moore
Cc: linux-security-module, linux-kernel, Nathan Lynch,
Matthew Garrett, Kees Cook, David Howells, James Morris
In-Reply-To: <1961790.USuA9gRusQ@daniel-desktop3>
Hi Daniel,
On 11/20/2025 2:07 AM, Daniel Tang wrote:
> KDE's lockscreen causes systemd-logind to spam dmesg about hibernation.
> systemd declined to cache /sys/power/state due to runtime changeability.
>
> Link: https://github.com/systemd/systemd/pull/39802
> Signed-off-by: Daniel Tang <danielzgtg.opensource@gmail.com>
> ---
> security/lockdown/lockdown.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
> index cf83afa1d879..83b50de52f21 100644
> --- a/security/lockdown/lockdown.c
> +++ b/security/lockdown/lockdown.c
> @@ -58,13 +58,16 @@ early_param("lockdown", lockdown_param);
> */
> static int lockdown_is_locked_down(enum lockdown_reason what)
> {
> + static volatile unsigned long lockdown_reasons_seen;
> + static_assert(ARRAY_SIZE(lockdown_reasons) < sizeof(lockdown_reasons_seen) * 8);
> +
> if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
> "Invalid lockdown reason"))
> return -EPERM;
>
> if (kernel_locked_down >= what) {
> - if (lockdown_reasons[what])
> - pr_notice_ratelimited("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
> + if (lockdown_reasons[what] && !test_and_set_bit(what, &lockdown_reasons_seen))
> + pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
> current->comm, lockdown_reasons[what]);
Currently lockdown does not support the audit function, so I believe the
logs here serve a purpose similar to auditing. Based on this, I think
this change will meaningfully degrade the quality of the logs, making it
hard for users to find out what happens when lockdown is active,
especially after a long time running.
Is it possible to adjust the printk_ratelimit & printk_ratelimit_burst
in /proc/sys/kernel/ to reduce the logs in your scenario?
Anyway, I will wait for Nicolas's comment.
> return -EPERM;
> }
^ permalink raw reply
* Re: [PATCH v2] lsm: use unrcu_pointer() for current->cred in security_init()
From: Xiu Jianfeng @ 2025-11-20 7:48 UTC (permalink / raw)
To: Paul Moore, linux-security-module
In-Reply-To: <20251119153636.15243-2-paul@paul-moore.com>
On 11/19/2025 11:36 PM, Paul Moore wrote:
> We need to directly allocate the cred's LSM state for the initial task
> when we initialize the LSM framework. Unfortunately, this results in a
> RCU related type mismatch, use the unrcu_pointer() macro to handle this
> a bit more elegantly.
>
> The explicit type casting still remains as we need to work around the
> constification of current->cred in this particular case.
>
> Signed-off-by: Paul Moore <paul@paul-moore.com>
Reviewed-by: Xiu Jianfeng <xiujianfeng@huawei.com>
> ---
> security/lsm_init.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/security/lsm_init.c b/security/lsm_init.c
> index 6bb67d41ce52..05bd52e6b1f2 100644
> --- a/security/lsm_init.c
> +++ b/security/lsm_init.c
> @@ -467,7 +467,8 @@ int __init security_init(void)
> blob_sizes.lbs_inode, 0,
> SLAB_PANIC, NULL);
>
> - if (lsm_cred_alloc((struct cred __rcu *)current->cred, GFP_KERNEL))
> + if (lsm_cred_alloc((struct cred *)unrcu_pointer(current->cred),
> + GFP_KERNEL))
> panic("early LSM cred alloc failed\n");
> if (lsm_task_alloc(current))
> panic("early LSM task alloc failed\n");
^ permalink raw reply
* Re: [BUG] landlock: sleeping function called from invalid context in hook_sb_delete()
From: Günther Noack @ 2025-11-20 8:48 UTC (permalink / raw)
To: 许佳凯
Cc: linux-kernel, linux-security-module, Günther Noack,
Serge E. Hallyn, Mickaël Salaün
In-Reply-To: <20dd8187.9d18.19a75eadc43.Coremail.xujiakai24@mails.ucas.ac.cn>
Hello!
Thanks for the report!
CC-ing Mickaël, who authored that code
On Wed, Nov 12, 2025 at 10:35:17AM +0800, 许佳凯 wrote:
> The call trace indicates that hook_sb_delete() holds s_inode_list_lock (a spinlock) while invoking operations that may eventually call iput(), which can sleep.
> This violates the locking context expectations and triggers __might_sleep() warnings.
> The issue seems to be related to how Landlock handles superblock cleanup during security_sb_delete().
>
>
> I’m currently only reporting this issue to the community; the exact fix will likely need to be confirmed and implemented by the Landlock and filesystem maintainers.
This looks like a false positive to me.
There are three places where iput() is being called in hook_sb_delete,
two of them are in places where it is *not* holding the
s_inode_list_lock. The one that *is* holding the s_inode_list_lock
has the following comment:
/*
* At this point, we own the ihold() reference that was
* originally set up by get_inode_object() and the
* __iget() reference that we just set in this loop
* walk. Therefore the following call to iput() will
* not sleep nor drop the inode because there is now at
* least two references to it.
*/
That seems to indicate that the sleepability concern was taken into
consideration. iput() only sleeps if the refcount reaches zero, and
if you can exclude that, it won't sleep.
—Günther
--
^ permalink raw reply
* Re: [PATCH v2] lockdown: Only log restrictions once
From: Daniel Tang @ 2025-11-20 9:26 UTC (permalink / raw)
To: Nicolas Bouchinet, Xiu Jianfeng, Paul Moore, Xiujianfeng
Cc: linux-security-module, linux-kernel, Nathan Lynch,
Matthew Garrett, Kees Cook, David Howells, James Morris
In-Reply-To: <2f4a1af8-adc6-4cbc-813f-4cc8e9bc75ae@huaweicloud.com>
On Thursday, 20 November 2025, 02:37:56 EST Xiujianfeng <xiujianfeng@huaweicloud.com> wrote:
> Is it possible to adjust the printk_ratelimit & printk_ratelimit_burst
> in /proc/sys/kernel/ to reduce the logs in your scenario?
It's not working. Watching the console after setting the sysctl and
repeatedly clicking org.freedesktop.login1.Manager.CanSuspend in
qdbusviewer (simulating what the lockscreen does), I see:
```console
root@daniel-desktop3:~# uname -a
Linux daniel-desktop3 6.17.0-6-generic #6-Ubuntu SMP PREEMPT_DYNAMIC Tue Oct 7 13:34:17 UTC 2025 x86_64 GNU/Linux
root@daniel-desktop3:~# sysctl kernel.printk_ratelimit_burst=1
kernel.printk_ratelimit_burst = 1
root@daniel-desktop3:~# sysctl kernel.printk_ratelimit=999999
kernel.printk_ratelimit = 999999
root@daniel-desktop3:~# dmesg -W
[14385.334698] lockdown_is_locked_down: 3 callbacks suppressed
[14385.334701] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14385.614738] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14385.878857] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14386.166744] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14386.454771] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14386.750900] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14387.038795] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14387.334770] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14387.622696] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14387.926763] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14390.366582] lockdown_is_locked_down: 7 callbacks suppressed
[14390.366585] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14390.798744] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14391.118802] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14391.422728] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14391.742754] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14392.046735] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14392.350745] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14392.654992] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14392.974797] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
[14393.270741] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
```
At my desk, I lock my screen every 5 hours. In public, I might lock my
screen every 1 minute, 5 minute, or 15 *minutes*. printk_ratelimit
seems to be targeted towards things that happen every N *seconds*.
> logs here serve a purpose similar to auditing. Based on this, I think
> this change will meaningfully degrade the quality of the logs, making it
> hard for users to find out what happens when lockdown is active,
> especially after a long time running.
For v3 in December, I'm thinking of adding a code path to special-case
*reads* from /sys/power/state. What do you think?
^ permalink raw reply
* Re: [PATCH -next] ima: Handle error code returned by ima_filter_rule_match()
From: Roberto Sassu @ 2025-11-20 9:17 UTC (permalink / raw)
To: Zhao Yipeng, zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg
Cc: lujialin4, linux-integrity, linux-security-module, linux-kernel
In-Reply-To: <20251120071805.1604864-1-zhaoyipeng5@huawei.com>
On Thu, 2025-11-20 at 15:18 +0800, Zhao Yipeng wrote:
> In ima_match_rules(), if ima_filter_rule_match() returns -ENOENT due to
> the rule being NULL, the function incorrectly skips the 'if (!rc)' check
> and sets 'result = true'. The LSM rule is considered a match, causing
> extra files to be measured by IMA.
>
> This issue can be reproduced in the following scenario:
> After unloading the SELinux policy module via 'semodule -d', if an IMA
> measurement is triggered before ima_lsm_rules is updated,
> in ima_match_rules(), the first call to ima_filter_rule_match() returns
> -ESTALE. This causes the code to enter the 'if (rc == -ESTALE &&
> !rule_reinitialized)' block, perform ima_lsm_copy_rule() and retry. In
> ima_lsm_copy_rule(), since the SELinux module has been removed, the rule
> becomes NULL, and the second call to ima_filter_rule_match() returns
> -ENOENT. This bypasses the 'if (!rc)' check and results in a false match.
>
> Call trace:
> selinux_audit_rule_match+0x310/0x3b8
> security_audit_rule_match+0x60/0xa0
> ima_match_rules+0x2e4/0x4a0
> ima_match_policy+0x9c/0x1e8
> ima_get_action+0x48/0x60
> process_measurement+0xf8/0xa98
> ima_bprm_check+0x98/0xd8
> security_bprm_check+0x5c/0x78
> search_binary_handler+0x6c/0x318
> exec_binprm+0x58/0x1b8
> bprm_execve+0xb8/0x130
> do_execveat_common.isra.0+0x1a8/0x258
> __arm64_sys_execve+0x48/0x68
> invoke_syscall+0x50/0x128
> el0_svc_common.constprop.0+0xc8/0xf0
> do_el0_svc+0x24/0x38
> el0_svc+0x44/0x200
> el0t_64_sync_handler+0x100/0x130
> el0t_64_sync+0x3c8/0x3d0
>
> Fix this by changing 'if (!rc)' to 'if (rc <= 0)' to ensure that error
> codes like -ENOENT do not bypass the check and accidentally result in a
> successful match.
Thanks, it makes sense.
Reviewed-by: Roberto Sassu <roberto.sassu@huawei.com>
Roberto
> Fixes: 4af4662fa4a9d ("integrity: IMA policy")
> Signed-off-by: Zhao Yipeng <zhaoyipeng5@huawei.com>
> ---
> security/integrity/ima/ima_policy.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index 128fab897930..db6d55af5a80 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -674,7 +674,7 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
> goto retry;
> }
> }
> - if (!rc) {
> + if (rc <= 0) {
> result = false;
> goto out;
> }
^ permalink raw reply
* Re: Re: [BUG] landlock: sleeping function called from invalid context in hook_sb_delete()
From: 许佳凯 @ 2025-11-20 10:52 UTC (permalink / raw)
To: Günther Noack
Cc: linux-kernel, linux-security-module, Günther Noack,
Serge E. Hallyn, Mickaël Salaün
In-Reply-To: <20251120.c5c17c664315@gnoack.org>
Hello Günther,
Thanks a lot for your detailed reply.
Your explanation makes perfect sense. I agree this is indeed a false positive.
Thanks for pointing that out and for the clarification.
I appreciate your time and the helpful analysis.
Best regards,
-Jiakai
^ permalink raw reply
* Re: [RFC v1 0/1] Implement IMA Event Log Trimming
From: Roberto Sassu @ 2025-11-20 11:02 UTC (permalink / raw)
To: Anirudh Venkataramanan, linux-integrity
Cc: Mimi Zohar, Roberto Sassu, Dmitry Kasatkin, Eric Snowberg,
Paul Moore, James Morris, Serge E . Hallyn, linux-security-module,
Steven Chen, Gregory Lumen, Lakshmi Ramasubramanian,
Sush Shringarputale
In-Reply-To: <20251119213359.39397-1-anirudhve@linux.microsoft.com>
On Wed, 2025-11-19 at 13:33 -0800, Anirudh Venkataramanan wrote:
> ==========================================================================
> > A. Introduction |
> ==========================================================================
>
> IMA events are kept in kernel memory and preserved across kexec soft
> reboots. This can lead to increased kernel memory usage over time,
> especially with aggressive IMA policies that measure everything. To reduce
> memory pressure, it becomes necessary to discard IMA events but given that
> IMA events are extended into PCRs in the TPM, just discarding events will
> break the PCR extension chain, making future verification of the IMA event
> log impossible.
>
> This patch series proposes a method to discard IMA events while keeping the
> log verifiable. While reducing memory pressure is the primary objective,
> the second order benefit of trimming the IMA log is that IMA log verifiers
> (local userspace daemon or a remote cloud service) can process smaller IMA
> logs on a rolling basis, thus avoiding re-verification of previously
> verified events.
Hi Anirudh
I will rephrase this paragraph, to be sure that I understood it
correctly.
You are proposing a method to trim the measurement list and, at the
same time, to keep the measurement list verifiable after trimming. The
way you would like to achieve that is to keep the verification state in
the kernel in the form of PCR values.
Those values mean what verifiers have already verified. Thus, for the
next verification attempt, verifiers take the current PCR values as
starting values, replay the truncated IMA measurement list, and again
you match with the current PCR values and you trim until that point.
So the benefit of this proposal is that you keep the verification of
the IMA measurement list self-contained by using the last verification
state (PCR starting value) and the truncated IMA measurement list as
the inputs of your verification.
Let me reiterate on the trusted computing principles IMA relies on for
providing the evidence about a system's integrity.
Unless you are at the beginning of the measurement chain, where the
Root of Trust for Measurement (RTM) is trusted by assumption, the
measurements done by a component can be trusted because that component
was already measured by the previous component in the boot chain,
before it had any chance to corrupt the system.
In the context of IMA, IMA can be trusted to make new measurements
because it measures every file before those files could cause any harm
to the system. So, potentially IMA and the kernel can be corrupted by
any file.
What you are proposing would not work, because you are placing trust in
an input (the PCR starting value) that can be manipulated at any time
by a corrupted kernel, before you had the chance to detect such
corruption.
Let me describe a scenario where I could take advantage of such
weakness. After the first measurement list trim, I perform an attack on
the system such that it corrupts the kernel. IMA added a new entry in
the measurement list, which would reveal the attack.
But, since I have control of the kernel, I conveniently update the PCR
starting value to replay the new measurement entry, and remove the
measurement entry from the measurement list.
Now, from the perspective of the user space verifiers everything is
fine, the truncated IMA measurement list is clean, no attack, and the
current PCR values match by replaying the new PCR starting value with
the remaining of the IMA measurement list.
So, in my opinion the kernel should just offer the ability to trim the
measurement list, and a remote verifier should be responsible to verify
the measurement list, without relying on anything from the system being
evaluated.
Sure, the remote verifier can verify just the trimmed IMA measurement
list, but the remote verifier must solely rely on state information
maintained internally.
Roberto
> The method has other advantages too:
>
> 1. It provides a userspace interface that can be used to precisely control
> trim point, allowing for trim points to be optionally aligned with
> userspace IMA event log validation.
>
> 2. It ensures that all necessary information required for continued IMA
> log validation is made available via the userspace interface at all
> times.
>
> 3. It provides a simple mechanism for userspace applications to determine
> if the event log has been unexpectedly trimmed.
>
> 4. The duration for which the IMA Measurement list mutex must be held (for
> trimming) is minimal.
>
> ==========================================================================
> > B. Solution |
> ==========================================================================
>
> --------------------------------------------------------------------------
> > B.1 Overview |
> --------------------------------------------------------------------------
>
> The kernel trims the IMA event log based on PCR values supplied by userspace.
> The core principles leveraged are as follows:
>
> - Given an IMA event log, PCR values for each IMA event can be obtained by
> recalulating the PCR extension for each event. Thus processing N events
> from the start will yield PCR values as of event N. This is referred to
> as "IMA event log replay".
>
> - To get the PCR value for event N + 1, only the PCR value as of event N
> is needed. If this can be known, events till and including N can be
> safely purged.
>
> Putting it all together, we get the following userspace + kernel flow:
>
> 1. A userspace application replays the IMA event log to generate PCR
> values and then triggers a trim by providing these values to the kernel
> (by writing to a pseudo file).
>
> Optionally, the userspace application may verify these PCR values
> against the corresponding TPM quote, and trigger trimming only if
> the calculated PCR values match up to the expectations in the quote's
> PCR digest.
>
> 2. The kernel uses the userspace supplied PCR values to trim the IMA
> measurements list at a specific point, and so these are referred to as
> "trim-to PCR values" in this context.
>
> Note that the kernel doesn't really understand what these userspace
> provided PCR values mean or what IMA event they correspond to, and so
> it does its own IMA event replay till either the replayed PCR values
> match with the userspace provided ones, or it runs out of events.
>
> If a match is found, the kernel can proceed with trimming the IMA
> measurements list. This is done in two steps, to keep locking context
> minimal.
>
> step 1: Find and return the list entry (as a count from head) of exact
> match. This does not lock the measurements list mutex, ensuring
> new events can be appended to the log.
>
> step 2: Lock the measurements list mutex and trim the measurements list
> at the previously identified list entry.
>
> If the trim is successful, the trim-to PCR values are saved as "starting
> PCR values". The next time userspace wants to replay the IMA event log,
> it will use the starting PCR values as the base for the IMA event log
> replay.
>
> --------------------------------------------------------------------------
> > B.2 Kernel Interfaces |
> --------------------------------------------------------------------------
>
> A new configfs pseudo file /sys/kernel/config/ima/pcrs that supports the
> following operations is exposed.
>
> read: returns starting PCR values stored in the kernel (within IMA
> specifically).
>
> write: writes trim-to PCR values to trigger trimming. If trimming is
> successful, trim-to PCR values are stored as starting PCR values.
> requires root privileges.
>
> --------------------------------------------------------------------------
> > B.3 Walk-through with a real example |
> --------------------------------------------------------------------------
>
> This is a real example from a test run.
>
> Suppose this IMA policy is deployed:
>
> measure func=FILE_CHECK mask=MAY_READ pcr=10
> measure func=FILE_CHECK mask=MAY_WRITE pcr=11
>
> When the policy is deployed, a zero digest starting PCR value will be set
> for each PCR used. If the TPM supports multiple hashbanks, there will be
> one starting PCR value per PCR, per TPM hashbank. This can be seen in the
> following hexdump:
>
> $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
> 00000000 70 63 72 31 30 3a 73 68 61 31 3a 00 00 00 00 00 |pcr10:sha1:.....|
> 00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 |...............p|
> 00000020 63 72 31 31 3a 73 68 61 31 3a 00 00 00 00 00 00 |cr11:sha1:......|
> 00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 70 63 |..............pc|
> 00000040 72 31 30 3a 73 68 61 32 35 36 3a 00 00 00 00 00 |r10:sha256:.....|
> 00000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
> 00000060 00 00 00 00 00 00 00 00 00 00 00 70 63 72 31 31 |...........pcr11|
> 00000070 3a 73 68 61 32 35 36 3a 00 00 00 00 00 00 00 00 |:sha256:........|
> 00000080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
> 00000090 00 00 00 00 00 00 00 00 70 63 72 31 30 3a 73 68 |........pcr10:sh|
> 000000a0 61 33 38 34 3a 00 00 00 00 00 00 00 00 00 00 00 |a384:...........|
> 000000b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
> 000000c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
> 000000d0 00 00 00 00 00 70 63 72 31 31 3a 73 68 61 33 38 |.....pcr11:sha38|
> 000000e0 34 3a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |4:..............|
> 000000f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
> 00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
> 00000110 00 00 |..|
> 00000112
>
> Let's say that a userspace utility replays the IMA event log, and triggers
> trimming by writing the following PCR values (i.e. trim-to PCR values) to the
> pseudo file:
>
> pcr10:sha256:8268782906555cf3aefc179f815c878527dd4e67eaa836572ebabab31977922c
> pcr11:sha256:4c7f31927183eacb53d51d95b0162916fd3fca51a8d1efc6dde3805eb891fe41
>
> The trim is successful,
>
> 1. Some number of entries from the measurements log will disappear. This
> can be verified by reading out the ASCII or binary IMA measurements
> file.
>
> 2. The trim-to PCR values are saved as starting PCR values. This can be
> verified by reading out the pseudo file again as shown below. Note that
> even through only sha256 PCR values were written, the kernel populated
> sha1 and sha384 starting values as well.
>
> $ sudo hexdump -vC /sys/kernel/config/ima/pcrs
>
> 00000000 70 63 72 31 30 3a 73 68 61 31 3a c4 7f 9d 00 68 |pcr10:sha1:....h|
> 00000010 e4 86 71 bf bc ae f0 10 12 ff 68 e2 9e 74 e4 70 |..q.......h..t.p|
> 00000020 63 72 31 31 3a 73 68 61 31 3a 90 d7 17 ac 60 4d |cr11:sha1:....`M|
> 00000030 c8 25 ce 77 7d 9d 94 cf 44 7b b2 2e 2e e2 70 63 |.%.w}...D{....pc|
> 00000040 72 31 30 3a 73 68 61 32 35 36 3a 82 68 78 29 06 |r10:sha256:.hx).|
> 00000050 55 5c f3 ae fc 17 9f 81 5c 87 85 27 dd 4e 67 ea |U\......\..'.Ng.|
> 00000060 a8 36 57 2e ba ba b3 19 77 92 2c 70 63 72 31 31 |.6W.....w.,pcr11|
> 00000070 3a 73 68 61 32 35 36 3a 4c 7f 31 92 71 83 ea cb |:sha256:L.1.q...|
> 00000080 53 d5 1d 95 b0 16 29 16 fd 3f ca 51 a8 d1 ef c6 |S.....)..?.Q....|
> 00000090 dd e3 80 5e b8 91 fe 41 70 63 72 31 30 3a 73 68 |...^...Apcr10:sh|
> 000000a0 61 33 38 34 3a 8e d6 12 18 b1 d6 cd 95 16 98 33 |a384:..........3|
> 000000b0 2b 7d a2 d6 d9 05 c7 e8 5b 15 b0 91 c5 fc 23 d1 |+}......[.....#.|
> 000000c0 f9 a8 8d 60 50 5c e9 64 5f d7 b3 b2 f1 9c 90 0a |...`P\.d_.......|
> 000000d0 45 53 5d b2 57 70 63 72 31 31 3a 73 68 61 33 38 |ES].Wpcr11:sha38|
> 000000e0 34 3a 25 fc 21 28 31 5a f7 c6 fb 0f 40 c9 06 e6 |4:%.!(1Z....@...|
> 000000f0 c5 da ed 20 61 a1 03 54 4f 67 18 88 82 0f 48 d1 |... a..TOg....H.|
> 00000100 2f e0 3d 36 46 5e 94 a4 88 51 f8 91 39 7e e5 97 |/.=6F^...Q..9~..|
> 00000110 2c c5 |,.|
> 00000112
>
> --------------------------------------------------------------------------
> > C. Footnotes |
> --------------------------------------------------------------------------
>
> 1. The 'pcrs' pseudo file is currently part of configfs. This was due to
> some early internal feedback in a different context. This can as well be
> in securityfs with the rest of the IMA pseudo files.
>
> 2. PCR values are never read out of the TPM at any point. All PCR values
> used are derived from IMA event log replay.
>
> 3. Code is "RFC quality". Refinements can be made if the method is accepted.
>
> 4. For functional validation, base kernel version was 6.17 stable, with the
> most recent tested version being 6.17.8.
>
> 5. Code has been validated to some degree using a python-based internal test
> tool. This can be published if there is community interest.
>
> Steven Chen (1):
> ima: Implement IMA event log trimming
>
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/ima/Kconfig | 13 +
> drivers/ima/Makefile | 2 +
> drivers/ima/ima_config_pcrs.c | 291 ++++++++++++++++++
> include/linux/ima.h | 27 ++
> security/integrity/ima/Makefile | 4 +
> security/integrity/ima/ima.h | 8 +
> security/integrity/ima/ima_init.c | 44 +++
> security/integrity/ima/ima_log_trim.c | 421 ++++++++++++++++++++++++++
> security/integrity/ima/ima_policy.c | 7 +-
> security/integrity/ima/ima_queue.c | 5 +-
> 12 files changed, 821 insertions(+), 4 deletions(-)
> create mode 100644 drivers/ima/Kconfig
> create mode 100644 drivers/ima/Makefile
> create mode 100644 drivers/ima/ima_config_pcrs.c
> create mode 100644 security/integrity/ima/ima_log_trim.c
>
^ permalink raw reply
* Re: [PATCH v2] lockdown: Only log restrictions once
From: Xiujianfeng @ 2025-11-20 13:35 UTC (permalink / raw)
To: Daniel Tang, Nicolas Bouchinet, Xiu Jianfeng, Paul Moore
Cc: linux-security-module, linux-kernel, Nathan Lynch,
Matthew Garrett, Kees Cook, David Howells, James Morris
In-Reply-To: <7645139.4DdEvYhyI6@daniel-desktop3>
Hi Daniel,
On 11/20/2025 5:26 PM, Daniel Tang wrote:
> On Thursday, 20 November 2025, 02:37:56 EST Xiujianfeng <xiujianfeng@huaweicloud.com> wrote:
>> Is it possible to adjust the printk_ratelimit & printk_ratelimit_burst
>> in /proc/sys/kernel/ to reduce the logs in your scenario?
>
> It's not working. Watching the console after setting the sysctl and
> repeatedly clicking org.freedesktop.login1.Manager.CanSuspend in
> qdbusviewer (simulating what the lockscreen does), I see:
>
> ```console
> root@daniel-desktop3:~# uname -a
> Linux daniel-desktop3 6.17.0-6-generic #6-Ubuntu SMP PREEMPT_DYNAMIC Tue Oct 7 13:34:17 UTC 2025 x86_64 GNU/Linux
> root@daniel-desktop3:~# sysctl kernel.printk_ratelimit_burst=1
> kernel.printk_ratelimit_burst = 1
> root@daniel-desktop3:~# sysctl kernel.printk_ratelimit=999999
> kernel.printk_ratelimit = 999999
> root@daniel-desktop3:~# dmesg -W
> [14385.334698] lockdown_is_locked_down: 3 callbacks suppressed
> [14385.334701] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14385.614738] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14385.878857] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14386.166744] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14386.454771] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14386.750900] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14387.038795] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14387.334770] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14387.622696] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14387.926763] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14390.366582] lockdown_is_locked_down: 7 callbacks suppressed
> [14390.366585] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14390.798744] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14391.118802] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14391.422728] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14391.742754] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14392.046735] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14392.350745] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14392.654992] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14392.974797] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> [14393.270741] Lockdown: systemd-logind: hibernation is restricted; see man kernel_lockdown.7
> ```
>
> At my desk, I lock my screen every 5 hours. In public, I might lock my
> screen every 1 minute, 5 minute, or 15 *minutes*. printk_ratelimit
> seems to be targeted towards things that happen every N *seconds*.
Sorry for misleading.
After reading the code, I found that the macro of printk_ratelimited is
#define printk_ratelimited(fmt, ...) \
({ \
static DEFINE_RATELIMIT_STATE(_rs, \
DEFAULT_RATELIMIT_INTERVAL, \
DEFAULT_RATELIMIT_BURST); \
\
if (__ratelimit(&_rs)) \
printk(fmt, ##__VA_ARGS__); \
})
It seems that the rate is fixed and can not be modified via sysctl.
While another interface with a modifiable rate, which is
printk_ratelimit(), is marked as "don't use".
>
>> logs here serve a purpose similar to auditing. Based on this, I think
>> this change will meaningfully degrade the quality of the logs, making it
>> hard for users to find out what happens when lockdown is active,
>> especially after a long time running.
>
> For v3 in December, I'm thinking of adding a code path to special-case
> *reads* from /sys/power/state. What do you think?
Sorry, I am not familiar with hibernation, maybe you can CC hibernation
maintainers.
>
^ permalink raw reply
* [PATCH v4 00/35] Compiler-Based Context- and Locking-Analysis
From: Marco Elver @ 2025-11-20 14:49 UTC (permalink / raw)
To: elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon,
Linus Torvalds
Cc: 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
Context Analysis is a language extension, which enables statically
checking that required contexts are active (or inactive) by acquiring
and releasing user-definable "context guards". An obvious application is
lock-safety checking for the kernel's various synchronization primitives
(each of which represents a "context guard"), and checking that locking
rules are not violated.
The feature requires Clang 22 (unreleased) or later. Clang originally
called the feature "Thread Safety Analysis" [1]. This was later changed
and the feature became more flexible, gaining the ability to define
custom "capabilities". Its foundations can be found in "Capability
Systems" [2], used to specify the permissibility of operations to depend
on some "capability" being held (or not held).
Because the feature is not just able to express "capabilities" related
to synchronization primitives, and "capability" is already overloaded in
the kernel, the naming chosen for the kernel departs from Clang's
"Thread Safety" and "capability" nomenclature; we refer to the feature
as "Context Analysis" to avoid confusion. The internal implementation
still makes references to Clang's terminology in a few places, such as
`-Wthread-safety` being the warning option that also still appears in
diagnostic messages.
Additional details can be found in the added kernel-doc documentation.
An LWN article covered v2 of the series: https://lwn.net/Articles/1012990/
[1] https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
[2] https://www.cs.cornell.edu/talc/papers/capabilities.pdf
=== Development Approach ===
Prior art exists in the form of Sparse's Context Tracking. Locking
annotations on functions already exist sparsely, so the concept of
analyzing locking rules is not foreign to the kernel's codebase.
However, Clang's analysis is more complete vs. Sparse's, with the
typical trade-offs in static analysis: improved completeness is
sacrificed for more possible false positives or additional annotations
required by the programmer. Numerous options exist to disable or opt out
certain code from analysis.
This series initially aimed to retain compatibility with Sparse, which
can provide tree-wide analysis of a subset of the context analysis
introduced, but it was later decided to drop Sparse compatibility. For
the most part, the new (and old) keywords used for annotations remain
the same, and many of the pre-existing annotations remain valid.
One big question is how to enable this feature, given we end up with a
new dialect of C; two approaches have been considered:
A. Tree-wide all-or-nothing approach. This approach requires tree-wide
changes, adding annotations or selective opt-outs. Making more
primitives context-analysis aware increases churn where maintainers
are unfamiliar with the feature and the analysis is unable to deal
with complex code patterns as-is.
Because we can't change the programming language (even if from one C
dialect to another) of the kernel overnight, a different approach might
cause less friction.
B. A selective, incremental, and much less intrusive approach.
Maintainers of subsystems opt in their modules or directories into
context analysis (via Makefile):
CONTEXT_ANALYSIS_foo.o := y # foo.o only
CONTEXT_ANALYSIS := y # all TUs
Most (eventually all) synchronization primitives, and more
context guards including ones that track "irq disabled",
"preemption" disabled, etc. could be supported.
The approach taken by this series is B. This ensures that only
subsystems where maintainers are willing to deal with any warnings are
opted-in. Introducing the feature can be done incrementally, without
large tree-wide changes and adding numerous opt-outs and annotations to
the majority of code.
Note: Bart Van Assche concurrently worked on enabling -Wthread-safety:
https://lore.kernel.org/all/20250206175114.1974171-1-bvanassche@acm.org/
Bart's work has shown what it might take to go with approach A
(tree-wide, restricted to 'mutex' usage). This has shown that the
analysis finds real issues when applied to enough subsystems! We hope
this serves as motivation to eventually enable the analysis in as many
subsystems as possible, particularly subsystems that are not as easily
tested by CI systems and test robots.
=== Initial Uses ===
With this initial series, the following synchronization primitives are
supported: `raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`,
`seqlock_t`, `bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`,
`local_lock_t`, `ww_mutex`.
To demonstrate use of the feature on real kernel code, the series also
enables context analysis for the following subsystems:
* kernel/kcov
* kernel/kcsan
* kernel/sched/
* lib/rhashtable
* lib/stackdepot
* mm/kfence
* security/tomoyo
* crypto/
The initial benefits are static detection of violations of locking
rules. As more context guards are supported, we would see more static
checking beyond what regular C can provide, all while remaining easy
(and quick) to use via the Clang compiler.
Note: The kernel already provides dynamic analysis tools Lockdep and
KCSAN for lock-safety checking and data-race detection respectively.
Unlike those, Clang's context analysis is a compile-time static
analysis with no runtime impact. The static analysis complements
existing dynamic analysis tools, as it may catch some issues before
even getting into a running kernel, but is *not* a replacement for
whole-kernel testing with the dynamic analysis tools enabled!
=== Appendix ===
A Clang version that supports `-Wthread-safety-pointer` and the new
alias-analysis of context-guard pointers is required (from this version
onwards):
https://github.com/llvm/llvm-project/commit/7ccb5c08f0685d4787f12c3224a72f0650c5865e
The minimum required release version will be Clang 22.
This series is also available at this Git tree:
https://git.kernel.org/pub/scm/linux/kernel/git/melver/linux.git/log/?h=ctx-analysis/dev
=== Changelog ===
v4:
- Rename capability -> context analysis, per Linus's suggestion:
https://lore.kernel.org/all/CAHk-=wgd-Wcp0GpYaQnU7S9ci+FvFmaNw1gm75mzf0ZWdNLxvw@mail.gmail.com/
- Minor fixes.
v3: https://lore.kernel.org/all/20250918140451.1289454-1-elver@google.com/
- Bump min. Clang version to 22+ (unreleased), which now supports:
* re-entrancy via __attribute__((reentrant_capability));
* basic form of capability alias analysis - which is the
biggest improvement since v2.
This was the result of conclusions from this discussion:
https://lore.kernel.org/all/CANpmjNPquO=W1JAh1FNQb8pMQjgeZAKCPQUAd7qUg=5pjJ6x=Q@mail.gmail.com/
- Rename __asserts_cap/__assert_cap to __assumes_cap/__assume_cap.
- Switch to DECLARE_LOCK_GUARD_1_ATTRS().
- Add __acquire_ret and __acquire_shared_ret helper macros - can be
used to define function-like macros that return objects which
contains a held capabilities. Works now because of capability alias
analysis.
- Add capability_unsafe_alias() helper, where the analysis rightfully
points out we're doing strange things with aliases but we don't
care.
- Support multi-argument attributes.
- Enable for kernel/sched/{core,fair}.c, kernel/kcsan.
- Drop drivers/tty changes (revisit later).
v2: https://lore.kernel.org/all/20250304092417.2873893-1-elver@google.com/
- Remove Sparse context tracking support - after the introduction of
Clang support, so that backports can skip removal of Sparse support.
- Remove __cond_lock() function-like helper.
- ww_mutex support.
- -Wthread-safety-addressof was reworked and committed in upstream
Clang as -Wthread-safety-pointer.
- Make __cond_acquires() and __cond_acquires_shared() take abstract
value, since compiler only cares about zero and non-zero.
- Rename __var_guarded_by to simply __guarded_by. Initially the idea
was to be explicit about if the variable itself or the pointed-to
data is guarded, but in the long-term, making this shorter might be
better.
- Likewise rename __ref_guarded_by to __pt_guarded_by.
- Introduce common header warning suppressions - this is a better
solution than guarding header inclusions with disable_ +
enable_capability_analysis(). Header suppressions are disabled when
selecting CONFIG_WARN_CAPABILITY_ANALYSIS_ALL=y. This bumps the
minimum Clang version required to 20+.
- Make the data_race() macro imply disabled capability analysis.
Writing capability_unsafe(data_race(..)) is unnecessarily verbose
and data_race() on its own already indicates something subtly unsafe
is happening. This change was made after analysis of a finding in
security/tomoyo.
- Enable analysis in the following subsystems as additional examples
of larger subsystem. Where it was obvious, the __guarded_by
attribute was added to lock-guarded variables to improve coverage.
* drivers/tty
* security/tomoyo
* crypto/
RFC v1: https://lore.kernel.org/lkml/20250206181711.1902989-1-elver@google.com
Marco Elver (35):
compiler_types: Move lock checking attributes to
compiler-context-analysis.h
compiler-context-analysis: Add infrastructure for Context Analysis
with Clang
compiler-context-analysis: Add test stub
Documentation: Add documentation for Compiler-Based Context Analysis
checkpatch: Warn about context_unsafe() without comment
cleanup: Basic compatibility with context analysis
lockdep: Annotate lockdep assertions for context analysis
locking/rwlock, spinlock: Support Clang's context analysis
compiler-context-analysis: Change __cond_acquires to take return value
locking/mutex: Support Clang's context analysis
locking/seqlock: Support Clang's context analysis
bit_spinlock: Include missing <asm/processor.h>
bit_spinlock: Support Clang's context analysis
rcu: Support Clang's context analysis
srcu: Support Clang's context analysis
kref: Add context-analysis annotations
locking/rwsem: Support Clang's context analysis
locking/local_lock: Include missing headers
locking/local_lock: Support Clang's context analysis
locking/ww_mutex: Support Clang's context analysis
debugfs: Make debugfs_cancellation a context guard struct
compiler-context-analysis: Remove Sparse support
compiler-context-analysis: Remove __cond_lock() function-like helper
compiler-context-analysis: Introduce header suppressions
compiler: Let data_race() imply disabled context analysis
MAINTAINERS: Add entry for Context Analysis
kfence: Enable context analysis
kcov: Enable context analysis
kcsan: Enable context analysis
stackdepot: Enable context analysis
rhashtable: Enable context analysis
printk: Move locking annotation to printk.c
security/tomoyo: Enable context analysis
crypto: Enable context analysis
sched: Enable context analysis for core.c and fair.c
Documentation/dev-tools/context-analysis.rst | 146 +++++
Documentation/dev-tools/index.rst | 1 +
Documentation/dev-tools/sparse.rst | 19 -
Documentation/mm/process_addrs.rst | 6 +-
MAINTAINERS | 11 +
Makefile | 1 +
crypto/Makefile | 2 +
crypto/acompress.c | 6 +-
crypto/algapi.c | 2 +
crypto/api.c | 1 +
crypto/crypto_engine.c | 2 +-
crypto/drbg.c | 5 +
crypto/internal.h | 2 +-
crypto/proc.c | 3 +
crypto/scompress.c | 24 +-
.../net/wireless/intel/iwlwifi/iwl-trans.c | 4 +-
.../net/wireless/intel/iwlwifi/iwl-trans.h | 6 +-
.../intel/iwlwifi/pcie/gen1_2/internal.h | 5 +-
.../intel/iwlwifi/pcie/gen1_2/trans.c | 4 +-
fs/dlm/lock.c | 2 +-
include/crypto/internal/acompress.h | 7 +-
include/crypto/internal/engine.h | 2 +-
include/linux/bit_spinlock.h | 24 +-
include/linux/cleanup.h | 17 +
include/linux/compiler-context-analysis.h | 429 +++++++++++++
include/linux/compiler.h | 2 +
include/linux/compiler_types.h | 18 +-
include/linux/console.h | 4 +-
include/linux/debugfs.h | 12 +-
include/linux/kref.h | 2 +
include/linux/list_bl.h | 2 +
include/linux/local_lock.h | 45 +-
include/linux/local_lock_internal.h | 73 ++-
include/linux/lockdep.h | 12 +-
include/linux/mm.h | 33 +-
include/linux/mutex.h | 35 +-
include/linux/mutex_types.h | 4 +-
include/linux/rcupdate.h | 90 +--
include/linux/refcount.h | 6 +-
include/linux/rhashtable.h | 14 +-
include/linux/rwlock.h | 22 +-
include/linux/rwlock_api_smp.h | 43 +-
include/linux/rwlock_rt.h | 44 +-
include/linux/rwlock_types.h | 10 +-
include/linux/rwsem.h | 66 +-
include/linux/sched.h | 6 +-
include/linux/sched/signal.h | 16 +-
include/linux/sched/task.h | 5 +-
include/linux/sched/wake_q.h | 3 +
include/linux/seqlock.h | 24 +
include/linux/seqlock_types.h | 5 +-
include/linux/spinlock.h | 89 ++-
include/linux/spinlock_api_smp.h | 34 +-
include/linux/spinlock_api_up.h | 112 +++-
include/linux/spinlock_rt.h | 37 +-
include/linux/spinlock_types.h | 10 +-
include/linux/spinlock_types_raw.h | 5 +-
include/linux/srcu.h | 64 +-
include/linux/srcutiny.h | 4 +
include/linux/srcutree.h | 6 +-
include/linux/ww_mutex.h | 22 +-
kernel/Makefile | 2 +
kernel/kcov.c | 36 +-
kernel/kcsan/Makefile | 2 +
kernel/kcsan/report.c | 11 +-
kernel/printk/printk.c | 2 +
kernel/sched/Makefile | 3 +
kernel/sched/core.c | 89 ++-
kernel/sched/fair.c | 9 +-
kernel/sched/sched.h | 110 +++-
kernel/signal.c | 4 +-
kernel/time/posix-timers.c | 13 +-
lib/Kconfig.debug | 44 ++
lib/Makefile | 6 +
lib/dec_and_lock.c | 8 +-
lib/rhashtable.c | 5 +-
lib/stackdepot.c | 20 +-
lib/test_context-analysis.c | 596 ++++++++++++++++++
mm/kfence/Makefile | 2 +
mm/kfence/core.c | 20 +-
mm/kfence/kfence.h | 14 +-
mm/kfence/report.c | 4 +-
mm/memory.c | 4 +-
mm/pgtable-generic.c | 19 +-
net/ipv4/tcp_sigpool.c | 2 +-
scripts/Makefile.context-analysis | 11 +
scripts/Makefile.lib | 10 +
scripts/checkpatch.pl | 7 +
scripts/context-analysis-suppression.txt | 33 +
security/tomoyo/Makefile | 2 +
security/tomoyo/common.c | 52 +-
security/tomoyo/common.h | 77 +--
security/tomoyo/domain.c | 1 +
security/tomoyo/environ.c | 1 +
security/tomoyo/file.c | 5 +
security/tomoyo/gc.c | 28 +-
security/tomoyo/mount.c | 2 +
security/tomoyo/network.c | 3 +
tools/include/linux/compiler_types.h | 2 -
99 files changed, 2377 insertions(+), 592 deletions(-)
create mode 100644 Documentation/dev-tools/context-analysis.rst
create mode 100644 include/linux/compiler-context-analysis.h
create mode 100644 lib/test_context-analysis.c
create mode 100644 scripts/Makefile.context-analysis
create mode 100644 scripts/context-analysis-suppression.txt
--
2.52.0.rc1.455.g30608eb744-goog
^ permalink raw reply
* [PATCH v4 01/35] compiler_types: Move lock checking attributes to compiler-context-analysis.h
From: Marco Elver @ 2025-11-20 14:49 UTC (permalink / raw)
To: elver, Peter Zijlstra, Boqun Feng, Ingo Molnar, Will Deacon
Cc: 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-2-elver@google.com>
The conditional definition of lock checking macros and attributes is
about to become more complex. Factor them out into their own header for
better readability, and to make it obvious which features are supported
by which mode (currently only Sparse). This is the first step towards
generalizing towards "context analysis".
No functional change intended.
Signed-off-by: Marco Elver <elver@google.com>
Reviewed-by: Bart Van Assche <bvanassche@acm.org>
---
v4:
* Rename capability -> context analysis.
---
include/linux/compiler-context-analysis.h | 32 +++++++++++++++++++++++
include/linux/compiler_types.h | 18 ++-----------
2 files changed, 34 insertions(+), 16 deletions(-)
create mode 100644 include/linux/compiler-context-analysis.h
diff --git a/include/linux/compiler-context-analysis.h b/include/linux/compiler-context-analysis.h
new file mode 100644
index 000000000000..f8af63045281
--- /dev/null
+++ b/include/linux/compiler-context-analysis.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Macros and attributes for compiler-based static context analysis.
+ */
+
+#ifndef _LINUX_COMPILER_CONTEXT_ANALYSIS_H
+#define _LINUX_COMPILER_CONTEXT_ANALYSIS_H
+
+#ifdef __CHECKER__
+
+/* Sparse context/lock checking support. */
+# define __must_hold(x) __attribute__((context(x,1,1)))
+# define __acquires(x) __attribute__((context(x,0,1)))
+# define __cond_acquires(x) __attribute__((context(x,0,-1)))
+# define __releases(x) __attribute__((context(x,1,0)))
+# define __acquire(x) __context__(x,1)
+# define __release(x) __context__(x,-1)
+# define __cond_lock(x, c) ((c) ? ({ __acquire(x); 1; }) : 0)
+
+#else /* !__CHECKER__ */
+
+# define __must_hold(x)
+# define __acquires(x)
+# define __cond_acquires(x)
+# define __releases(x)
+# define __acquire(x) (void)0
+# define __release(x) (void)0
+# define __cond_lock(x, c) (c)
+
+#endif /* __CHECKER__ */
+
+#endif /* _LINUX_COMPILER_CONTEXT_ANALYSIS_H */
diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h
index 0a1b9598940d..7eb8d8db6c28 100644
--- a/include/linux/compiler_types.h
+++ b/include/linux/compiler_types.h
@@ -24,6 +24,8 @@
# define BTF_TYPE_TAG(value) /* nothing */
#endif
+#include <linux/compiler-context-analysis.h>
+
/* sparse defines __CHECKER__; see Documentation/dev-tools/sparse.rst */
#ifdef __CHECKER__
/* address spaces */
@@ -34,14 +36,6 @@
# define __rcu __attribute__((noderef, address_space(__rcu)))
static inline void __chk_user_ptr(const volatile void __user *ptr) { }
static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
-/* context/locking */
-# define __must_hold(x) __attribute__((context(x,1,1)))
-# define __acquires(x) __attribute__((context(x,0,1)))
-# define __cond_acquires(x) __attribute__((context(x,0,-1)))
-# define __releases(x) __attribute__((context(x,1,0)))
-# define __acquire(x) __context__(x,1)
-# define __release(x) __context__(x,-1)
-# define __cond_lock(x,c) ((c) ? ({ __acquire(x); 1; }) : 0)
/* other */
# define __force __attribute__((force))
# define __nocast __attribute__((nocast))
@@ -62,14 +56,6 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { }
# define __chk_user_ptr(x) (void)0
# define __chk_io_ptr(x) (void)0
-/* context/locking */
-# define __must_hold(x)
-# define __acquires(x)
-# define __cond_acquires(x)
-# define __releases(x)
-# define __acquire(x) (void)0
-# define __release(x) (void)0
-# define __cond_lock(x,c) (c)
/* other */
# define __force
# define __nocast
--
2.52.0.rc1.455.g30608eb744-goog
^ permalink raw reply related
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