On Fri, 2026-01-30 at 16:39 -0600, Frederick Lawler wrote:
> IMA performs unnecessary measurements on files in stacked file systems
> that do not set kstat.change_cookie to an inode's i_version.
> 
> For example: TMPFS (upper) is stacked onto XFS (lower).
> Actions files result in re-measurement because commit 1cf7e834a6fb
> ("xfs: switch to multigrain timestamps") introduced multigrain
> timestamps into XFS which changed the kstat.change_cookie semantics
> to no longer supply an i_version to compare against in
> integrity_inode_attributes_changed(). Once the inode is in TMPFS,
> the change detection behavior operates as normal because TMPFS updates
> kstat.change_cookie to the i_version.
> 
> Instead, fall back onto a ctime comparison. This also gives file systems
> that do not support i_version an opportunity avoid the same behavior,
> as they're more likely to have ctime support.
> 
> timespec64_to_ns() is chosen to avoid adding extra storage to
> integrity_inode_attributes by leveraging the existing version field.

Correct me if I'm wrong, but this issue seems to me xfs-specific. In
that case, maybe it is better to remove the stacked filesystem part,
which might be misleading.

Thanks

Roberto

> Link: https://lore.kernel.org/all/aTspr4_h9IU4EyrR@CMGLRV3
> Fixes: 1cf7e834a6fb ("xfs: switch to multigrain timestamps")
> Suggested-by: Jeff Layton <[email protected]>
> Signed-off-by: Frederick Lawler <[email protected]>
> ---
>  include/linux/integrity.h         |  6 +++++-
>  security/integrity/ima/ima_api.c  | 11 ++++++++---
>  security/integrity/ima/ima_main.c |  2 +-
>  3 files changed, 14 insertions(+), 5 deletions(-)
> 
> diff --git a/include/linux/integrity.h b/include/linux/integrity.h
> index 
> 382c783f0fa3ae4a938cdf9559291ba1903a378e..ec2c94907f417c4a71ecce29ac79edac9bc2c6f8
>  100644
> --- a/include/linux/integrity.h
> +++ b/include/linux/integrity.h
> @@ -10,6 +10,7 @@
>  #include <linux/fs.h>
>  #include <linux/iversion.h>
>  #include <linux/kernel.h>
> +#include <linux/time64.h>
>  
>  enum integrity_status {
>       INTEGRITY_PASS = 0,
> @@ -58,6 +59,9 @@ integrity_inode_attrs_stat_changed
>       if (stat->result_mask & STATX_CHANGE_COOKIE)
>               return stat->change_cookie != attrs->version;
>  
> +     if (stat->result_mask & STATX_CTIME)
> +             return timespec64_to_ns(&stat->ctime) != (s64)attrs->version;
> +
>       return true;
>  }
>  
> @@ -84,7 +88,7 @@ integrity_inode_attrs_changed(const struct 
> integrity_inode_attributes *attrs,
>        * only for IMA if vfs_getattr_nosec() fails.
>        */
>       if (!file || vfs_getattr_nosec(&file->f_path, &stat,
> -                                    STATX_CHANGE_COOKIE,
> +                                    STATX_CHANGE_COOKIE | STATX_CTIME,
>                                      AT_STATX_SYNC_AS_STAT))
>               return !IS_I_VERSION(inode) ||
>                      !inode_eq_iversion(inode, attrs->version);
> diff --git a/security/integrity/ima/ima_api.c 
> b/security/integrity/ima/ima_api.c
> index 
> c35ea613c9f8d404ba4886e3b736c3bab29d1668..e47d6281febc15a0ac1bd2ea1d28fea4d0cd5c58
>  100644
> --- a/security/integrity/ima/ima_api.c
> +++ b/security/integrity/ima/ima_api.c
> @@ -272,10 +272,15 @@ int ima_collect_measurement(struct ima_iint_cache 
> *iint, struct file *file,
>        * to an initial measurement/appraisal/audit, but was modified to
>        * assume the file changed.
>        */
> -     result = vfs_getattr_nosec(&file->f_path, &stat, STATX_CHANGE_COOKIE,
> +     result = vfs_getattr_nosec(&file->f_path, &stat,
> +                                STATX_CHANGE_COOKIE | STATX_CTIME,
>                                  AT_STATX_SYNC_AS_STAT);
> -     if (!result && (stat.result_mask & STATX_CHANGE_COOKIE))
> -             i_version = stat.change_cookie;
> +     if (!result) {
> +             if (stat.result_mask & STATX_CHANGE_COOKIE)
> +                     i_version = stat.change_cookie;
> +             else if (stat.result_mask & STATX_CTIME)
> +                     i_version = timespec64_to_ns(&stat.ctime);
> +     }
>       hash.hdr.algo = algo;
>       hash.hdr.length = hash_digest_size[algo];
>  
> diff --git a/security/integrity/ima/ima_main.c 
> b/security/integrity/ima/ima_main.c
> index 
> 8cb17c9d446caaa5a98f5ec8f027c17ba7babca8..776db158b0bd8a0d053729ac0cc15af8b6020a98
>  100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -199,7 +199,7 @@ static void ima_check_last_writer(struct ima_iint_cache 
> *iint,
>                                           &iint->atomic_flags);
>               if ((iint->flags & IMA_NEW_FILE) ||
>                   vfs_getattr_nosec(&file->f_path, &stat,
> -                                   STATX_CHANGE_COOKIE,
> +                                   STATX_CHANGE_COOKIE | STATX_CTIME,
>                                     AT_STATX_SYNC_AS_STAT) ||
>                   integrity_inode_attrs_stat_changed(&iint->real_inode,
>                                                      &stat)) {
> 


Reply via email to