Linux filesystem development
 help / color / mirror / Atom feed
* overlayfs: verity validation broken since f77f281b6118
@ 2026-05-01 17:14 Colin Walters
  2026-05-01 18:07 ` Eric Biggers
  2026-05-02  9:23 ` Amir Goldstein
  0 siblings, 2 replies; 8+ messages in thread
From: Colin Walters @ 2026-05-01 17:14 UTC (permalink / raw)
  To: Christoph Hellwig, Eric Biggers; +Cc: linux-fsdevel@vger.kernel.org

Hi Christoph & Eric,

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174

There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.

From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
From: Colin Walters <walters@verbum.org>
Date: Fri, 1 May 2026 09:45:58 -0400
Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay

Reproduces the regression reported at:
https://github.com/bootc-dev/bootc/issues/2174

A recent change in how fsverity state was cached in memory
I think caused inodes not in cache to appear to have
missing verity=require for overlayfs.

This test catches that.

Generated-by: OpenCode (Claude Sonnet 4.5)
Signed-off-by: Colin Walters <walters@verbum.org>
---
 tests/overlay/118     | 62 +++++++++++++++++++++++++++++++++++++++++++
 tests/overlay/118.out |  1 +
 2 files changed, 63 insertions(+)
 create mode 100755 tests/overlay/118
 create mode 100644 tests/overlay/118.out

diff --git a/tests/overlay/118 b/tests/overlay/118
new file mode 100755
index 00000000..ca21e076
--- /dev/null
+++ b/tests/overlay/118
@@ -0,0 +1,62 @@
+#! /bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2026 Red Hat, Inc. All Rights Reserved.
+#
+# FS QA Test No. 118
+#
+# Regression test for the overlayfs lazy fsverity load path.
+#
+# See also overlay/080 which builds a metacopy midlayer over a
+# verity-enabled data lower layer (the composefs architecture).
+#
+. ./common/preamble
+_begin_fstest auto quick metacopy redirect verity
+
+# Import common functions.
+. ./common/filter
+. ./common/verity
+
+# We use non-default scratch underlying overlay dirs, we need to check
+# them explicitly after the test.
+_require_scratch_nocheck
+_require_scratch_overlay_features redirect_dir metacopy
+_require_scratch_overlay_verity
+
+# remove all files from previous tests
+_scratch_mkfs
+
+testfile="verityfile"
+lowerdir=$OVL_BASE_SCRATCH_MNT/lower
+midlayer=$OVL_BASE_SCRATCH_MNT/midlayer
+upperdir=$OVL_BASE_SCRATCH_MNT/upper
+workdir=$OVL_BASE_SCRATCH_MNT/workdir
+workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
+
+mkdir -p $lowerdir $midlayer $upperdir $workdir $workdir2
+
+# Create a verity-enabled file on the lower (data) layer.
+echo -n "overlay verity lazy load test" > $lowerdir/$testfile
+chmod 600 $lowerdir/$testfile
+_fsv_enable $lowerdir/$testfile >> $seqres.full 2>&1 \
+	|| _fail "failed to enable fsverity on $lowerdir/$testfile"
+
+# This is the same structure composefs creates at install time.
+_overlay_scratch_mount_dirs $lowerdir $midlayer $workdir2 \
+	-o redirect_dir=on,index=on,metacopy=on,verity=on
+chmod 400 $SCRATCH_MNT/$testfile
+$UMOUNT_PROG $SCRATCH_MNT
+
+# Drop all caches to reproduce the bug.
+echo 3 > /proc/sys/vm/drop_caches
+
+# Remount and verify we can read.
+_overlay_scratch_mount_dirs "$midlayer:$lowerdir" $upperdir $workdir \
+	-o redirect_dir=on,index=on,metacopy=on,verity=require
+cat $SCRATCH_MNT/$testfile > /dev/null 2>>$seqres.full \
+	|| echo "verity file read failed through overlay (regression)"
+
+$UMOUNT_PROG $SCRATCH_MNT
+
+# success, all done
+status=0
+exit
diff --git a/tests/overlay/118.out b/tests/overlay/118.out
new file mode 100644
index 00000000..881d8dcd
--- /dev/null
+++ b/tests/overlay/118.out
@@ -0,0 +1 @@
+QA output created by 118
-- 
2.52.0



^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-01 17:14 overlayfs: verity validation broken since f77f281b6118 Colin Walters
@ 2026-05-01 18:07 ` Eric Biggers
  2026-05-05 18:07   ` Andrey Albershteyn
  2026-05-02  9:23 ` Amir Goldstein
  1 sibling, 1 reply; 8+ messages in thread
From: Eric Biggers @ 2026-05-01 18:07 UTC (permalink / raw)
  To: Colin Walters; +Cc: Christoph Hellwig, linux-fsdevel@vger.kernel.org, fsverity

[+Cc fsverity@lists.linux.dev]

On Fri, May 01, 2026 at 01:14:54PM -0400, Colin Walters wrote:
> Hi Christoph & Eric,
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174
> 
> There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.
> 
> From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
> From: Colin Walters <walters@verbum.org>
> Date: Fri, 1 May 2026 09:45:58 -0400
> Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay
> 
> Reproduces the regression reported at:
> https://github.com/bootc-dev/bootc/issues/2174
> 
> A recent change in how fsverity state was cached in memory
> I think caused inodes not in cache to appear to have
> missing verity=require for overlayfs.
> 
> This test catches that.
> 
> Generated-by: OpenCode (Claude Sonnet 4.5)
> Signed-off-by: Colin Walters <walters@verbum.org>

Sorry about that.  I guess it's because the semantics of
fsverity_active() changed to be basically the same as IS_VERITY(), and
that broke ovl_ensure_verity_loaded() which does
'!fsverity_active(inode) && IS_VERITY(inode)'.  I guess now it should
do: IS_VERITY(inode) && fsverity_get_info(inode) == NULL.

- Eric

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-01 17:14 overlayfs: verity validation broken since f77f281b6118 Colin Walters
  2026-05-01 18:07 ` Eric Biggers
@ 2026-05-02  9:23 ` Amir Goldstein
  2026-05-05 16:51   ` Colin Walters
  1 sibling, 1 reply; 8+ messages in thread
From: Amir Goldstein @ 2026-05-02  9:23 UTC (permalink / raw)
  To: Colin Walters
  Cc: Christoph Hellwig, Eric Biggers, linux-fsdevel@vger.kernel.org

On Fri, May 01, 2026 at 01:14:54PM -0400, Colin Walters wrote:
> Hi Christoph & Eric,
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174
> 
> There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.
> 
> From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
> From: Colin Walters <walters@verbum.org>
> Date: Fri, 1 May 2026 09:45:58 -0400
> Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay
> 
> Reproduces the regression reported at:
> https://github.com/bootc-dev/bootc/issues/2174
> 
> A recent change in how fsverity state was cached in memory
> I think caused inodes not in cache to appear to have
> missing verity=require for overlayfs.
> 
> This test catches that.
> 
> Generated-by: OpenCode (Claude Sonnet 4.5)
> Signed-off-by: Colin Walters <walters@verbum.org>
> ---
>  tests/overlay/118     | 62 +++++++++++++++++++++++++++++++++++++++++++
>  tests/overlay/118.out |  1 +


Please use free test numbers below 100

Is there a kernel fix for this? please mention it.

Thanks,
Amir.

>  2 files changed, 63 insertions(+)
>  create mode 100755 tests/overlay/118
>  create mode 100644 tests/overlay/118.out
> 
> diff --git a/tests/overlay/118 b/tests/overlay/118
> new file mode 100755
> index 00000000..ca21e076
> --- /dev/null
> +++ b/tests/overlay/118
> @@ -0,0 +1,62 @@
> +#! /bin/bash
> +# SPDX-License-Identifier: GPL-2.0
> +# Copyright (C) 2026 Red Hat, Inc. All Rights Reserved.
> +#
> +# FS QA Test No. 118
> +#
> +# Regression test for the overlayfs lazy fsverity load path.
> +#
> +# See also overlay/080 which builds a metacopy midlayer over a
> +# verity-enabled data lower layer (the composefs architecture).
> +#
> +. ./common/preamble
> +_begin_fstest auto quick metacopy redirect verity
> +
> +# Import common functions.
> +. ./common/filter
> +. ./common/verity
> +
> +# We use non-default scratch underlying overlay dirs, we need to check
> +# them explicitly after the test.
> +_require_scratch_nocheck
> +_require_scratch_overlay_features redirect_dir metacopy
> +_require_scratch_overlay_verity
> +
> +# remove all files from previous tests
> +_scratch_mkfs
> +
> +testfile="verityfile"
> +lowerdir=$OVL_BASE_SCRATCH_MNT/lower
> +midlayer=$OVL_BASE_SCRATCH_MNT/midlayer
> +upperdir=$OVL_BASE_SCRATCH_MNT/upper
> +workdir=$OVL_BASE_SCRATCH_MNT/workdir
> +workdir2=$OVL_BASE_SCRATCH_MNT/workdir2
> +
> +mkdir -p $lowerdir $midlayer $upperdir $workdir $workdir2
> +
> +# Create a verity-enabled file on the lower (data) layer.
> +echo -n "overlay verity lazy load test" > $lowerdir/$testfile
> +chmod 600 $lowerdir/$testfile
> +_fsv_enable $lowerdir/$testfile >> $seqres.full 2>&1 \
> +	|| _fail "failed to enable fsverity on $lowerdir/$testfile"
> +
> +# This is the same structure composefs creates at install time.
> +_overlay_scratch_mount_dirs $lowerdir $midlayer $workdir2 \
> +	-o redirect_dir=on,index=on,metacopy=on,verity=on
> +chmod 400 $SCRATCH_MNT/$testfile
> +$UMOUNT_PROG $SCRATCH_MNT
> +
> +# Drop all caches to reproduce the bug.
> +echo 3 > /proc/sys/vm/drop_caches
> +
> +# Remount and verify we can read.
> +_overlay_scratch_mount_dirs "$midlayer:$lowerdir" $upperdir $workdir \
> +	-o redirect_dir=on,index=on,metacopy=on,verity=require
> +cat $SCRATCH_MNT/$testfile > /dev/null 2>>$seqres.full \
> +	|| echo "verity file read failed through overlay (regression)"
> +
> +$UMOUNT_PROG $SCRATCH_MNT
> +
> +# success, all done
> +status=0
> +exit
> diff --git a/tests/overlay/118.out b/tests/overlay/118.out
> new file mode 100644
> index 00000000..881d8dcd
> --- /dev/null
> +++ b/tests/overlay/118.out
> @@ -0,0 +1 @@
> +QA output created by 118
> -- 
> 2.52.0
> 
> 

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-02  9:23 ` Amir Goldstein
@ 2026-05-05 16:51   ` Colin Walters
  2026-05-05 17:18     ` Eric Biggers
  0 siblings, 1 reply; 8+ messages in thread
From: Colin Walters @ 2026-05-05 16:51 UTC (permalink / raw)
  To: Amir Goldstein
  Cc: Christoph Hellwig, Eric Biggers, linux-fsdevel@vger.kernel.org,
	fsverity

On Sat, May 2, 2026, at 5:23 AM, Amir Goldstein wrote:
> On Fri, May 01, 2026 at 01:14:54PM -0400, Colin Walters wrote:
>> Hi Christoph & Eric,
>> 
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174
>> 
>> There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.
>> 
>> From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
>> From: Colin Walters <walters@verbum.org>
>> Date: Fri, 1 May 2026 09:45:58 -0400
>> Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay
>> 
>> Reproduces the regression reported at:
>> https://github.com/bootc-dev/bootc/issues/2174
>> 
>> A recent change in how fsverity state was cached in memory
>> I think caused inodes not in cache to appear to have
>> missing verity=require for overlayfs.
>> 
>> This test catches that.
>> 
>> Generated-by: OpenCode (Claude Sonnet 4.5)
>> Signed-off-by: Colin Walters <walters@verbum.org>
>> ---
>>  tests/overlay/118     | 62 +++++++++++++++++++++++++++++++++++++++++++
>>  tests/overlay/118.out |  1 +
>
>
> Please use free test numbers below 100

OK, I can resend with that change if that's the only thing.


>
> Is there a kernel fix for this? please mention it.

Not that I know of. I did have my agent framework (opencode + combo of Gemini+Claude models) generate one initially, but I intentionally didn't post it because the generating is ~easy, verifying it's "good" is another thing and my C has bitrotted a bit (in favor of Rust mostly but I have to deal with a lot of Go too).

Anyways, this trivial change works:

diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 13cb60b52bd6..af8f6c2989ce 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -1039,7 +1039,7 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
 
                if (WARN_ON_ONCE(lowerdata.dentry == NULL) ||
                    ovl_ensure_verity_loaded(&lowerdata) ||
-                   !fsverity_active(d_inode(lowerdata.dentry))) {
+                   !fsverity_get_info(d_inode(lowerdata.dentry))) {
                        return false;
                }
        }
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index 7b86a6bac644..bfdf9310ee78 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -1354,7 +1354,7 @@ int ovl_ensure_verity_loaded(const struct path *datapath)
        struct inode *inode = d_inode(datapath->dentry);
        struct file *filp;
 
-       if (!fsverity_active(inode) && IS_VERITY(inode)) {
+       if (IS_VERITY(inode) && !fsverity_get_info(inode)) {
                /*
                 * If this inode was not yet opened, the verity info hasn't been
                 * loaded yet, so we need to do that here to force it into memory.


However, when I looked at this more closely I felt the APIs are too confusing, and my Rust-ified mindset especially didn't like the side-effectful nature of ovl_ensure_verity_loaded().

So here's a much bigger patch that looks a lot nicer to me upon a quick local review:

diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index af8f6c2989ce..9d3908666b7e 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -1037,9 +1037,11 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
 
 		ovl_path_lowerdata(dentry, &lowerdata);
 
+		struct inode *inode = d_inode(lowerdata.dentry);
+
 		if (WARN_ON_ONCE(lowerdata.dentry == NULL) ||
-		    ovl_ensure_verity_loaded(&lowerdata) ||
-		    !fsverity_get_info(d_inode(lowerdata.dentry))) {
+		    !IS_VERITY(inode) ||
+		    IS_ERR(fsverity_get_or_load_info(inode))) {
 			return false;
 		}
 	}
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index b75df37f70ac..4d23ff29a4c3 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -581,7 +581,6 @@ int ovl_set_metacopy_xattr(struct ovl_fs *ofs, struct dentry *d,
 			   struct ovl_metacopy *metacopy);
 bool ovl_is_metacopy_dentry(struct dentry *dentry);
 char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int padding);
-int ovl_ensure_verity_loaded(const struct path *path);
 int ovl_validate_verity(struct ovl_fs *ofs,
 			const struct path *metapath,
 			const struct path *datapath);
diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c
index bfdf9310ee78..99aa2312de39 100644
--- a/fs/overlayfs/util.c
+++ b/fs/overlayfs/util.c
@@ -1348,25 +1348,6 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, const struct path *path, int pa
 	return ERR_PTR(res);
 }
 
-/* Call with mounter creds as it may open the file */
-int ovl_ensure_verity_loaded(const struct path *datapath)
-{
-	struct inode *inode = d_inode(datapath->dentry);
-	struct file *filp;
-
-	if (IS_VERITY(inode) && !fsverity_get_info(inode)) {
-		/*
-		 * If this inode was not yet opened, the verity info hasn't been
-		 * loaded yet, so we need to do that here to force it into memory.
-		 */
-		filp = kernel_file_open(datapath, O_RDONLY, current_cred());
-		if (IS_ERR(filp))
-			return PTR_ERR(filp);
-		fput(filp);
-	}
-
-	return 0;
-}
 
 int ovl_validate_verity(struct ovl_fs *ofs,
 			const struct path *metapath,
@@ -1375,7 +1356,7 @@ int ovl_validate_verity(struct ovl_fs *ofs,
 	struct ovl_metacopy metacopy_data;
 	u8 actual_digest[FS_VERITY_MAX_DIGEST_SIZE];
 	int xattr_digest_size, digest_size;
-	int xattr_size, err;
+	int xattr_size;
 	u8 verity_algo;
 
 	if (!ofs->config.verity_mode ||
@@ -1398,16 +1379,9 @@ int ovl_validate_verity(struct ovl_fs *ofs,
 
 	xattr_digest_size = ovl_metadata_digest_size(&metacopy_data);
 
-	err = ovl_ensure_verity_loaded(datapath);
-	if (err < 0) {
-		pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n",
-				    datapath->dentry);
-		return -EIO;
-	}
-
 	digest_size = fsverity_get_digest(d_inode(datapath->dentry), actual_digest,
 					  &verity_algo, NULL);
-	if (digest_size == 0) {
+	if (digest_size <= 0) {
 		pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n", datapath->dentry);
 		return -EIO;
 	}
@@ -1426,21 +1400,14 @@ int ovl_validate_verity(struct ovl_fs *ofs,
 int ovl_get_verity_digest(struct ovl_fs *ofs, const struct path *src,
 			  struct ovl_metacopy *metacopy)
 {
-	int err, digest_size;
+	int digest_size;
 
 	if (!ofs->config.verity_mode || !S_ISREG(d_inode(src->dentry)->i_mode))
 		return 0;
 
-	err = ovl_ensure_verity_loaded(src);
-	if (err < 0) {
-		pr_warn_ratelimited("lower file '%pd' failed to load fs-verity info\n",
-				    src->dentry);
-		return -EIO;
-	}
-
 	digest_size = fsverity_get_digest(d_inode(src->dentry),
 					  metacopy->digest, &metacopy->digest_algo, NULL);
-	if (digest_size == 0 ||
+	if (digest_size <= 0 ||
 	    WARN_ON_ONCE(digest_size > FS_VERITY_MAX_DIGEST_SIZE)) {
 		if (ofs->config.verity_mode == OVL_VERITY_REQUIRE) {
 			pr_warn_ratelimited("lower file '%pd' has no fs-verity digest\n",
diff --git a/fs/verity/measure.c b/fs/verity/measure.c
index 6a35623ebdf0..4ccf2ab8a70a 100644
--- a/fs/verity/measure.c
+++ b/fs/verity/measure.c
@@ -68,9 +68,9 @@ EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
  * @alg: (out) the digest's algorithm, as a FS_VERITY_HASH_ALG_* value
  * @halg: (out) the digest's algorithm, as a HASH_ALGO_* value
  *
- * Retrieves the fsverity digest of the given file.  The file must have been
- * opened at least once since the inode was last loaded into the inode cache;
- * otherwise this function will not recognize when fsverity is enabled.
+ * Retrieves the fsverity digest of the given file.  If the inode has the
+ * S_VERITY flag set but the fsverity_info has not yet been loaded into the
+ * in-memory hash table, this function will load it from disk automatically.
  *
  * The file's fsverity digest consists of @raw_digest in combination with either
  * @alg or @halg.  (The caller can choose which one of @alg or @halg to use.)
@@ -80,8 +80,8 @@ EXPORT_SYMBOL_GPL(fsverity_ioctl_measure);
  * provides no security guarantee for users who ignore the algorithm ID, even if
  * they use the digest size (since algorithms can share the same digest size).
  *
- * Return: The size of the raw digest in bytes, or 0 if the file doesn't have
- *	   fsverity enabled.
+ * Return: The size of the raw digest in bytes, 0 if the file doesn't have
+ *	   fsverity enabled, or -errno on error.
  */
 int fsverity_get_digest(struct inode *inode,
 			u8 raw_digest[FS_VERITY_MAX_DIGEST_SIZE],
@@ -90,10 +90,13 @@ int fsverity_get_digest(struct inode *inode,
 	const struct fsverity_info *vi;
 	const struct fsverity_hash_alg *hash_alg;
 
-	vi = fsverity_get_info(inode);
-	if (!vi)
+	if (!IS_VERITY(inode))
 		return 0; /* not a verity file */
 
+	vi = fsverity_get_or_load_info(inode);
+	if (IS_ERR(vi))
+		return PTR_ERR(vi);
+
 	hash_alg = vi->tree_params.hash_alg;
 	memcpy(raw_digest, vi->file_digest, hash_alg->digest_size);
 	if (alg)
@@ -116,6 +119,13 @@ __bpf_kfunc_start_defs();
  *
  * Read fsverity_digest of *file* into *digest_ptr*.
  *
+ * If the file's fsverity_info has not yet been loaded into the in-memory hash
+ * table (i.e. the inode is "cold"), this function will read the verity
+ * descriptor from disk before returning.  This may block on I/O.  The
+ * function is restricted to BPF_PROG_TYPE_LSM hooks (sleepable context), so
+ * blocking is safe, but callers should be aware of the potential latency on
+ * the first call for a given inode.
+ *
  * Return: 0 on success, a negative value on error.
  */
 __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *digest_p)
@@ -138,10 +148,13 @@ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *di
 	if (!IS_ALIGNED((uintptr_t)arg, __alignof__(*arg)))
 		return -EINVAL;
 
-	vi = fsverity_get_info(inode);
-	if (!vi)
+	if (!IS_VERITY(inode))
 		return -ENODATA; /* not a verity file */
 
+	vi = fsverity_get_or_load_info(inode);
+	if (IS_ERR(vi))
+		return PTR_ERR(vi);
+
 	hash_alg = vi->tree_params.hash_alg;
 
 	arg->digest_algorithm = hash_alg - fsverity_hash_algs;
diff --git a/fs/verity/open.c b/fs/verity/open.c
index dfa0d1afe0fe..4db71b396cdb 100644
--- a/fs/verity/open.c
+++ b/fs/verity/open.c
@@ -344,24 +344,48 @@ int fsverity_get_descriptor(struct inode *inode,
 	return 0;
 }
 
-static int ensure_verity_info(struct inode *inode)
+/**
+ * fsverity_get_or_load_info() - get the fsverity_info for a verity inode,
+ *				 loading it from disk if necessary
+ * @inode: inode with the S_VERITY flag set
+ *
+ * If the fsverity_info has already been loaded into the in-memory hash table
+ * (i.e. the file has been opened), return it immediately.  Otherwise read the
+ * verity descriptor from disk and populate the hash table entry.
+ *
+ * Unlike fsverity_get_info(), this function never returns NULL for a verity
+ * inode — it either returns the fsverity_info or an error.
+ *
+ * On encrypted verity files, the fscrypt key must already be set up on the
+ * inode (e.g. via a prior fscrypt_file_open()) before calling this function,
+ * as the descriptor read uses the existing crypto context rather than
+ * establishing one.  Callers such as overlayfs that reach this path during
+ * copy-up already satisfy this precondition.
+ *
+ * The caller must ensure @inode has the S_VERITY flag set.
+ *
+ * Return: the fsverity_info on success, ERR_PTR(-errno) on failure
+ */
+struct fsverity_info *fsverity_get_or_load_info(struct inode *inode)
 {
-	struct fsverity_info *vi = fsverity_get_info(inode), *found;
+	struct fsverity_info *vi, *found;
 	struct fsverity_descriptor *desc;
 	int err;
 
+	if (WARN_ON_ONCE(!IS_VERITY(inode)))
+		return ERR_PTR(-EINVAL);
+
+	vi = fsverity_get_info(inode);
 	if (vi)
-		return 0;
+		return vi;
 
 	err = fsverity_get_descriptor(inode, &desc);
 	if (err)
-		return err;
+		return ERR_PTR(err);
 
 	vi = fsverity_create_info(inode, desc);
-	if (IS_ERR(vi)) {
-		err = PTR_ERR(vi);
+	if (IS_ERR(vi))
 		goto out_free_desc;
-	}
 
 	/*
 	 * Multiple tasks may race to set the inode's verity info, in which case
@@ -372,20 +396,20 @@ static int ensure_verity_info(struct inode *inode)
 						  fsverity_info_hash_params);
 	if (found) {
 		fsverity_free_info(vi);
-		if (IS_ERR(found))
-			err = PTR_ERR(found);
+		vi = found; /* either the winner's entry or ERR_PTR */
 	}
 
 out_free_desc:
 	kfree(desc);
-	return err;
+	return vi;
 }
+EXPORT_SYMBOL_GPL(fsverity_get_or_load_info);
 
 int __fsverity_file_open(struct inode *inode, struct file *filp)
 {
 	if (filp->f_mode & FMODE_WRITE)
 		return -EPERM;
-	return ensure_verity_info(inode);
+	return PTR_ERR_OR_ZERO(fsverity_get_or_load_info(inode));
 }
 EXPORT_SYMBOL_GPL(__fsverity_file_open);
 
diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h
index a8f9aa75b792..0ea8ef2348d2 100644
--- a/include/linux/fsverity.h
+++ b/include/linux/fsverity.h
@@ -136,13 +136,25 @@ struct fsverity_operations {
 
 #ifdef CONFIG_FS_VERITY
 /**
- * fsverity_active() - do reads from the inode need to go through fs-verity?
+ * fsverity_active() - does the inode have the S_VERITY flag set?
  * @inode: inode to check
  *
- * This checks whether the inode's verity info has been set, and reads need
- * to verify the file data.
+ * Check whether the inode has the S_VERITY flag set.  This flag is set
+ * when the filesystem marks the inode as a verity file, and it persists
+ * for the lifetime of the inode in the inode cache.
  *
- * Return: true if reads need to go through fs-verity, otherwise false
+ * Note: S_VERITY being set does NOT mean the fsverity_info has been loaded
+ * into the in-memory hash table yet.  The info is loaded lazily on first
+ * open (via __fsverity_file_open()).  Use fsverity_get_info() to check
+ * whether the info is loaded, or fsverity_get_or_load_info() to load it
+ * on demand.
+ *
+ * The smp_mb() pairs with the try_cmpxchg in set_mask_bits() that SETS the
+ * S_VERITY bit in i_flags.  This ensures that stores made before the flag
+ * was set (in particular, the hash table insertion of the fsverity_info) are
+ * visible to any CPU that subsequently observes S_VERITY set.
+ *
+ * Return: true if the inode has S_VERITY set, otherwise false
  */
 static inline bool fsverity_active(const struct inode *inode)
 {
@@ -159,13 +171,26 @@ static inline bool fsverity_active(const struct inode *inode)
 }
 
 struct fsverity_info *__fsverity_get_info(const struct inode *inode);
+
 /**
- * fsverity_get_info - get fsverity information for an inode
- * @inode: inode to operate on.
+ * fsverity_get_info() - get the in-memory fsverity_info for an inode
+ * @inode: inode to look up
  *
- * This gets the fsverity_info for @inode if it exists.  Safe to call without
- * knowin that a fsverity_info exist for @inode, including on file systems that
- * do not support fsverity.
+ * Return the fsverity_info for @inode if it has been loaded into the
+ * in-memory hash table, otherwise return NULL.
+ *
+ * A NULL return has two distinct meanings:
+ *  1. The inode is not a verity file at all (S_VERITY not set).
+ *  2. The inode IS a verity file, but its info has not been loaded yet
+ *     because the file has not been opened since the inode was last evicted.
+ *
+ * Callers that need to distinguish these cases should check IS_VERITY(inode)
+ * alongside the return value.  Callers that need the info to always be
+ * available should use fsverity_get_or_load_info() instead.
+ *
+ * Safe to call on any inode, even on filesystems that do not support fsverity.
+ *
+ * Return: the fsverity_info if loaded, NULL otherwise
  */
 static inline struct fsverity_info *fsverity_get_info(const struct inode *inode)
 {
@@ -187,6 +212,7 @@ int fsverity_get_digest(struct inode *inode,
 
 /* open.c */
 
+struct fsverity_info *fsverity_get_or_load_info(struct inode *inode);
 int __fsverity_file_open(struct inode *inode, struct file *filp);
 
 /* read_metadata.c */
@@ -242,6 +268,11 @@ static inline int fsverity_get_digest(struct inode *inode,
 
 /* open.c */
 
+static inline struct fsverity_info *fsverity_get_or_load_info(struct inode *inode)
+{
+	return ERR_PTR(-EOPNOTSUPP);
+}
+
 static inline int __fsverity_file_open(struct inode *inode, struct file *filp)
 {
 	return -EOPNOTSUPP;
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 0916f24f005f..1eabb2d76961 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -213,9 +213,14 @@ static bool ima_get_verity_digest(struct ima_iint_cache *iint,
 	/*
 	 * On failure, 'measure' policy rules will result in a file data
 	 * hash containing 0's.
+	 *
+	 * fsverity_get_digest() returns 0 if fsverity is not enabled on the
+	 * file, or a negative errno if the verity info could not be loaded
+	 * (e.g. -EIO reading the descriptor from disk).  Treat both cases the
+	 * same: fall back to the file data hash.
 	 */
 	digest_len = fsverity_get_digest(inode, hash->digest, NULL, &alg);
-	if (digest_len == 0)
+	if (digest_len <= 0)
 		return false;
 
 	/*
diff --git a/security/ipe/eval.c b/security/ipe/eval.c
index 21439c5be336..1910f4b3641b 100644
--- a/security/ipe/eval.c
+++ b/security/ipe/eval.c
@@ -204,10 +204,10 @@ static bool evaluate_fsv_digest(const struct ipe_eval_ctx *const ctx,
 
 	if (!ctx->ino)
 		return false;
-	if (!fsverity_get_digest((struct inode *)ctx->ino,
-				 digest,
-				 NULL,
-				 &alg))
+	if (fsverity_get_digest((struct inode *)ctx->ino,
+				digest,
+				NULL,
+				&alg) <= 0)
 		return false;
 
 	info.alg = hash_algo_name[alg];
-- 
2.52.0

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-05 16:51   ` Colin Walters
@ 2026-05-05 17:18     ` Eric Biggers
  2026-05-05 18:44       ` Colin Walters
  0 siblings, 1 reply; 8+ messages in thread
From: Eric Biggers @ 2026-05-05 17:18 UTC (permalink / raw)
  To: Colin Walters
  Cc: Amir Goldstein, Christoph Hellwig, Miklos Szeredi,
	linux-fsdevel@vger.kernel.org, fsverity

[+Cc Miklos Szeredi <miklos@szeredi.hu>]

On Tue, May 05, 2026 at 12:51:47PM -0400, Colin Walters wrote:
> On Sat, May 2, 2026, at 5:23 AM, Amir Goldstein wrote:
> > On Fri, May 01, 2026 at 01:14:54PM -0400, Colin Walters wrote:
> >> Hi Christoph & Eric,
> >> 
> >> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174
> >> 
> >> There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.
> >> 
> >> From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
> >> From: Colin Walters <walters@verbum.org>
> >> Date: Fri, 1 May 2026 09:45:58 -0400
> >> Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay
> >> 
> >> Reproduces the regression reported at:
> >> https://github.com/bootc-dev/bootc/issues/2174
> >> 
> >> A recent change in how fsverity state was cached in memory
> >> I think caused inodes not in cache to appear to have
> >> missing verity=require for overlayfs.
> >> 
> >> This test catches that.
> >> 
> >> Generated-by: OpenCode (Claude Sonnet 4.5)
> >> Signed-off-by: Colin Walters <walters@verbum.org>
> >> ---
> >>  tests/overlay/118     | 62 +++++++++++++++++++++++++++++++++++++++++++
> >>  tests/overlay/118.out |  1 +
> >
> >
> > Please use free test numbers below 100
> 
> OK, I can resend with that change if that's the only thing.
> 
> 
> >
> > Is there a kernel fix for this? please mention it.
> 
> Not that I know of. I did have my agent framework (opencode + combo of Gemini+Claude models) generate one initially, but I intentionally didn't post it because the generating is ~easy, verifying it's "good" is another thing and my C has bitrotted a bit (in favor of Rust mostly but I have to deal with a lot of Go too).
> 
> Anyways, this trivial change works:

Please do a fix patch first, and then any cleanup second as separate
patch(es).

You'll also need to submit your patches correctly, otherwise they cannot
be accepted into the kernel:
https://docs.kernel.org/process/submitting-patches.html

Thanks,

- Eric

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-01 18:07 ` Eric Biggers
@ 2026-05-05 18:07   ` Andrey Albershteyn
  2026-05-05 20:19     ` Colin Walters
  0 siblings, 1 reply; 8+ messages in thread
From: Andrey Albershteyn @ 2026-05-05 18:07 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Colin Walters, Christoph Hellwig, linux-fsdevel@vger.kernel.org,
	fsverity

On 2026-05-01 11:07:25, Eric Biggers wrote:
> [+Cc fsverity@lists.linux.dev]
> 
> On Fri, May 01, 2026 at 01:14:54PM -0400, Colin Walters wrote:
> > Hi Christoph & Eric,
> > 
> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174
> > 
> > There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.
> > 
> > From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
> > From: Colin Walters <walters@verbum.org>
> > Date: Fri, 1 May 2026 09:45:58 -0400
> > Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay
> > 
> > Reproduces the regression reported at:
> > https://github.com/bootc-dev/bootc/issues/2174
> > 
> > A recent change in how fsverity state was cached in memory
> > I think caused inodes not in cache to appear to have
> > missing verity=require for overlayfs.
> > 
> > This test catches that.
> > 
> > Generated-by: OpenCode (Claude Sonnet 4.5)
> > Signed-off-by: Colin Walters <walters@verbum.org>
> 
> Sorry about that.  I guess it's because the semantics of
> fsverity_active() changed to be basically the same as IS_VERITY(), and
> that broke ovl_ensure_verity_loaded() which does
> '!fsverity_active(inode) && IS_VERITY(inode)'.  I guess now it should
> do: IS_VERITY(inode) && fsverity_get_info(inode) == NULL.
> 
> - Eric
> 

I guess this could be also fixed by patch 2 and 3 from my XFS
fsverity support [1]

1: https://lore.kernel.org/fsverity/20260428083332.768693-1-aalbersh@kernel.org/T/#t

-- 
- Andrey

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-05 17:18     ` Eric Biggers
@ 2026-05-05 18:44       ` Colin Walters
  0 siblings, 0 replies; 8+ messages in thread
From: Colin Walters @ 2026-05-05 18:44 UTC (permalink / raw)
  To: Eric Biggers
  Cc: Amir Goldstein, Christoph Hellwig, Miklos Szeredi,
	linux-fsdevel@vger.kernel.org, fsverity



On Tue, May 5, 2026, at 1:18 PM, Eric Biggers wrote:

> Please do a fix patch first, and then any cleanup second as separate
> patch(es).
>
> You'll also need to submit your patches correctly, otherwise they cannot
> be accepted into the kernel:
> https://docs.kernel.org/process/submitting-patches.html

Yeah, I kind of intentionally was not formally submitting the patches, just drafting the shape of fixes because again they were more "generated" and I was hoping a maintainer here could pick up things since this is a straight up regression, not a feature I'm arguing for.

But anyways to get things moving I posted the first patch narrowed to a 1-liner after I noticed we didn't actually need the copy_up change because we could rely on the side effects from `ovl_ensure_verity_loaded`.

Posted to https://lore.kernel.org/linux-fsdevel/6630d44f-967d-41f0-81ce-6958b371465a@app.fastmail.com/T/#u

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: overlayfs: verity validation broken since f77f281b6118
  2026-05-05 18:07   ` Andrey Albershteyn
@ 2026-05-05 20:19     ` Colin Walters
  0 siblings, 0 replies; 8+ messages in thread
From: Colin Walters @ 2026-05-05 20:19 UTC (permalink / raw)
  To: Andrey Albershteyn, Eric Biggers
  Cc: Christoph Hellwig, linux-fsdevel@vger.kernel.org, fsverity



On Tue, May 5, 2026, at 2:07 PM, Andrey Albershteyn wrote:
> On 2026-05-01 11:07:25, Eric Biggers wrote:
>> [+Cc fsverity@lists.linux.dev]
>> 
>> On Fri, May 01, 2026 at 01:14:54PM -0400, Colin Walters wrote:
>> > Hi Christoph & Eric,
>> > 
>> > https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f77f281b6118 broke composefs's usage of overlayfs verity=require, this was reported originally in https://github.com/bootc-dev/bootc/issues/2174
>> > 
>> > There's some output from an agent run I had in the <details> there, but here's an xfstests patch that passes on without that commit and fails with it.
>> > 
>> > From 14231122bfd1e41337e4fb847acbbe038457c32a Mon Sep 17 00:00:00 2001
>> > From: Colin Walters <walters@verbum.org>
>> > Date: Fri, 1 May 2026 09:45:58 -0400
>> > Subject: [PATCH] overlay/118: test fsverity lazy load through metacopy overlay
>> > 
>> > Reproduces the regression reported at:
>> > https://github.com/bootc-dev/bootc/issues/2174
>> > 
>> > A recent change in how fsverity state was cached in memory
>> > I think caused inodes not in cache to appear to have
>> > missing verity=require for overlayfs.
>> > 
>> > This test catches that.
>> > 
>> > Generated-by: OpenCode (Claude Sonnet 4.5)
>> > Signed-off-by: Colin Walters <walters@verbum.org>
>> 
>> Sorry about that.  I guess it's because the semantics of
>> fsverity_active() changed to be basically the same as IS_VERITY(), and
>> that broke ovl_ensure_verity_loaded() which does
>> '!fsverity_active(inode) && IS_VERITY(inode)'.  I guess now it should
>> do: IS_VERITY(inode) && fsverity_get_info(inode) == NULL.
>> 
>> - Eric
>> 
>
> I guess this could be also fixed by patch 2 and 3 from my XFS
> fsverity support [1]
>
> 1: 
> https://lore.kernel.org/fsverity/20260428083332.768693-1-aalbersh@kernel.org/T/#t

Yes, definitely similar. My draft went even farther - personally I like not having ovl_ensure_verity_loaded at all. But in the end I don't have a really strong opinion on that, as long as we get the xfstest merged so there's less chance of future regressions I'm happy!

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-05-05 20:19 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-01 17:14 overlayfs: verity validation broken since f77f281b6118 Colin Walters
2026-05-01 18:07 ` Eric Biggers
2026-05-05 18:07   ` Andrey Albershteyn
2026-05-05 20:19     ` Colin Walters
2026-05-02  9:23 ` Amir Goldstein
2026-05-05 16:51   ` Colin Walters
2026-05-05 17:18     ` Eric Biggers
2026-05-05 18:44       ` Colin Walters

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox