<feed xmlns='http://www.w3.org/2005/Atom'>
<title>linux-toradex.git/fs/overlayfs, branch master</title>
<subtitle>Linux kernel for Apalis and Colibri modules</subtitle>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/'/>
<entry>
<title>vfs: fix take_dentry_name_snapshot() semantics</title>
<updated>2026-11-03T16:28:20+00:00</updated>
<author>
<name>Linus Torvalds</name>
<email>torvalds@linux-foundation.org</email>
</author>
<published>2026-11-03T00:28:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=51628610f9187c1c1cfe499d92e066bc22ad9693'/>
<id>51628610f9187c1c1cfe499d92e066bc22ad9693</id>
<content type='text'>
The take_dentry_name_snapshot() function would keep trying to take a
reference on a dentry name forever, even if that dentry had been
released and the dentry name would never become valid.

All normal users held a reference to the dentry, but this "dentry name
is dead" could hit the overlayfs RCU walk case, and Amir fixed it by
adding a new try_take_dentry_name_snapshot() that returns on failure.
See commit 15c6bd5cf109 ("vfs: add try_take_dentry_name_snapshot()").

However, that fix was uncomfortably ugly, and the new "try" version was
subtly buggy in that it would also fail spuriously during a rename. The
only current user of that doesn't really care: a spurious failure would
result in the lookup just exiting RCU mode and being retried with the
dentry ref held (using the non-try version), but this all just rubbed me
the wrong way.

Instead of introducing a new and differently buggy helper, this just
makes take_dentry_name_snapshot() deal with all these situations
correctly.

It does that by instead of retrying when the external name reference
cannot be taken, it now retries purely based on the dentry sequence
counter. That means that if the external name reference failed due to a
rename, we will correctly retry it, but if it fails because the dentry
is dead and the name really is gone, it will now return that failure
rather than looping forever.

In this model, take_dentry_name_snapshot() will never return a failure
when a dentry ref is held, so the existing non-overlayfs users don't
need to care and they can safely ignore the return value.

And users that don't hold a reference (ie currently only that overlayfs
case) can use the improved take_dentry_name_snapshot() function safely
and it won't loop forever when racing with the dentry being free'd.

To make this all really robust and easy to use for any possible new use
cases, the failure case will not just return false, but also make sure
that the returned name will be empty, and that you can still call
release_dentry_name_snapshot() on the name.

But since there won't actually be anything to release on failure, you
can also do what overlayfs does: return an error and *not* call the
release function.

IOW, instead of two different functions that each had their own problem
cases, we're now back to just one single function that is robust and
easy to use in all situations. Knock wood.

Link: https://lore.kernel.org/all/CAHk-=wiPhTLn1p0ZHx-7SMdk4_zazqDK1k7MMEn9scRfqeDgtw@mail.gmail.com/
Fixes: 15c6bd5cf109 ("vfs: add try_take_dentry_name_snapshot()")
Cc: Amir Goldstein &lt;amir73il@gmail.com&gt;
Cc: Miklos Szeredi &lt;miklos@szeredi.hu&gt;
Cc: Christian Brauner &lt;brauner@kernel.org&gt;
Cc: Al Viro &lt;viro@zeniv.linux.org.uk&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
The take_dentry_name_snapshot() function would keep trying to take a
reference on a dentry name forever, even if that dentry had been
released and the dentry name would never become valid.

All normal users held a reference to the dentry, but this "dentry name
is dead" could hit the overlayfs RCU walk case, and Amir fixed it by
adding a new try_take_dentry_name_snapshot() that returns on failure.
See commit 15c6bd5cf109 ("vfs: add try_take_dentry_name_snapshot()").

However, that fix was uncomfortably ugly, and the new "try" version was
subtly buggy in that it would also fail spuriously during a rename. The
only current user of that doesn't really care: a spurious failure would
result in the lookup just exiting RCU mode and being retried with the
dentry ref held (using the non-try version), but this all just rubbed me
the wrong way.

Instead of introducing a new and differently buggy helper, this just
makes take_dentry_name_snapshot() deal with all these situations
correctly.

It does that by instead of retrying when the external name reference
cannot be taken, it now retries purely based on the dentry sequence
counter. That means that if the external name reference failed due to a
rename, we will correctly retry it, but if it fails because the dentry
is dead and the name really is gone, it will now return that failure
rather than looping forever.

In this model, take_dentry_name_snapshot() will never return a failure
when a dentry ref is held, so the existing non-overlayfs users don't
need to care and they can safely ignore the return value.

And users that don't hold a reference (ie currently only that overlayfs
case) can use the improved take_dentry_name_snapshot() function safely
and it won't loop forever when racing with the dentry being free'd.

To make this all really robust and easy to use for any possible new use
cases, the failure case will not just return false, but also make sure
that the returned name will be empty, and that you can still call
release_dentry_name_snapshot() on the name.

But since there won't actually be anything to release on failure, you
can also do what overlayfs does: return an error and *not* call the
release function.

IOW, instead of two different functions that each had their own problem
cases, we're now back to just one single function that is robust and
easy to use in all situations. Knock wood.

Link: https://lore.kernel.org/all/CAHk-=wiPhTLn1p0ZHx-7SMdk4_zazqDK1k7MMEn9scRfqeDgtw@mail.gmail.com/
Fixes: 15c6bd5cf109 ("vfs: add try_take_dentry_name_snapshot()")
Cc: Amir Goldstein &lt;amir73il@gmail.com&gt;
Cc: Miklos Szeredi &lt;miklos@szeredi.hu&gt;
Cc: Christian Brauner &lt;brauner@kernel.org&gt;
Cc: Al Viro &lt;viro@zeniv.linux.org.uk&gt;
Signed-off-by: Linus Torvalds &lt;torvalds@linux-foundation.org&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>Merge tag 'ovl-fixes-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs</title>
<updated>2026-10-31T18:35:53+00:00</updated>
<author>
<name>Linus Torvalds</name>
<email>torvalds@linux-foundation.org</email>
</author>
<published>2026-10-31T18:35:53+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=ca62fc8c6b1841039aee6de1ef03c9863751ec04'/>
<id>ca62fc8c6b1841039aee6de1ef03c9863751ec04</id>
<content type='text'>
Pull overlayfs fixes from Amir Goldstein:

 - Fixes for bugs found by Chris Mason's AI review, several of them
   reachable by unprivileged users: an info leak, use-after-free and
   NULL deref bugs, an RCU walk stall with remote layers and data loss
   with nfs_export

 - Fix copy up of files with an empty security.capability xattr, which
   any user could set to block copy up

 - Fix a regression from v6.16 with the override_creds mount option

 - Two casefold fixes

* tag 'ovl-fixes-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: check the casefold flag of directories only
  ovl: reserve space for the casefolded name terminator
  ovl: fix mount setup failing when override_creds drops CAP_SYS_ADMIN
  selftests/ovl: fix detached layer mountinfo expectations
  ovl: set the revalidate flags after d_instantiate()
  ovl: always encode the origin fh when index is enabled
  ovl: read the nlink xattr of every indexed non-dir
  ovl: skip copy up of empty security.capability
  ovl: fix a torn path in ovl_get_redirect()
  ovl: do not copy up a dentry that was removed
  ovl: hold a reference on the parent in readdir
  ovl: use the pinned upper dir in ovl_fix_origin()
  ovl: do not take a name snapshot in RCU walk
  vfs: add try_take_dentry_name_snapshot()
  ovl: take a snapshot of the name for copy up
  ovl: check that the parents of volatile/dirty are dirs
  ovl: add assertions in dir cache code
  ovl: set OVL_WHITEOUTS before publishing the upper dir
  ovl: fortify ovl_copy_xattr() against underlying layer modifications
  ovl: do not copy up an xattr value that was never read
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
Pull overlayfs fixes from Amir Goldstein:

 - Fixes for bugs found by Chris Mason's AI review, several of them
   reachable by unprivileged users: an info leak, use-after-free and
   NULL deref bugs, an RCU walk stall with remote layers and data loss
   with nfs_export

 - Fix copy up of files with an empty security.capability xattr, which
   any user could set to block copy up

 - Fix a regression from v6.16 with the override_creds mount option

 - Two casefold fixes

* tag 'ovl-fixes-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: check the casefold flag of directories only
  ovl: reserve space for the casefolded name terminator
  ovl: fix mount setup failing when override_creds drops CAP_SYS_ADMIN
  selftests/ovl: fix detached layer mountinfo expectations
  ovl: set the revalidate flags after d_instantiate()
  ovl: always encode the origin fh when index is enabled
  ovl: read the nlink xattr of every indexed non-dir
  ovl: skip copy up of empty security.capability
  ovl: fix a torn path in ovl_get_redirect()
  ovl: do not copy up a dentry that was removed
  ovl: hold a reference on the parent in readdir
  ovl: use the pinned upper dir in ovl_fix_origin()
  ovl: do not take a name snapshot in RCU walk
  vfs: add try_take_dentry_name_snapshot()
  ovl: take a snapshot of the name for copy up
  ovl: check that the parents of volatile/dirty are dirs
  ovl: add assertions in dir cache code
  ovl: set OVL_WHITEOUTS before publishing the upper dir
  ovl: fortify ovl_copy_xattr() against underlying layer modifications
  ovl: do not copy up an xattr value that was never read
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: check the casefold flag of directories only</title>
<updated>2026-10-29T12:40:31+00:00</updated>
<author>
<name>Chris Mason</name>
<email>mason@kernel.org</email>
</author>
<published>2026-10-16T21:17:17+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=1d27da7de3f74405b1de85ad26c5ea90f826114b'/>
<id>1d27da7de3f74405b1de85ad26c5ea90f826114b</id>
<content type='text'>
On an overlay over casefolded (case-insensitive) directories, every
lookup of a file that is not a directory fails with EREMOTE, and the
first file create triggers a kernel warning. No privilege is needed: an
unprivileged user can cause both in a user namespace, with the layers on
a tmpfs that they have mounted with the casefold option.

Some filesystems support casefolding, which is case-insensitive name
lookup. It is enabled per directory, and the inode of a casefolded
directory has the S_CASEFOLD flag.

overlayfs accepts layers on a filesystem that supports casefolding as
long as casefolding is enabled on every directory of the layers, or on
none. The mount records which of the two applies in ofs-&gt;casefold.
ovl_lookup_single() compares the S_CASEFOLD flag of each file that it
finds against ofs-&gt;casefold and returns -EREMOTE on a mismatch.

Filesystems only set S_CASEFOLD on directories, so with casefolded
layers a regular file, a symlink or a special file never matches and its
lookup fails.

ovl_get_inode() does the same comparison in a WARN_ON_ONCE() for each
new overlay inode. A failed lookup never gets that far, so it is the
first file create that trips the warning.

Reproduced on an overlay over casefolded tmpfs or ext4 directories, with
stat() of a regular file, a symlink and a FIFO, and with a file create:
each stat() failed with EREMOTE. For an unprivileged user in a user
namespace, on tmpfs, stat() of a regular file failed in the same way,
and the file create triggered the warning.

Fix this by limiting both checks to directories.

Fixes: 16754d61dc69 ("ovl: Support mounting case-insensitive enabled layers")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
On an overlay over casefolded (case-insensitive) directories, every
lookup of a file that is not a directory fails with EREMOTE, and the
first file create triggers a kernel warning. No privilege is needed: an
unprivileged user can cause both in a user namespace, with the layers on
a tmpfs that they have mounted with the casefold option.

Some filesystems support casefolding, which is case-insensitive name
lookup. It is enabled per directory, and the inode of a casefolded
directory has the S_CASEFOLD flag.

overlayfs accepts layers on a filesystem that supports casefolding as
long as casefolding is enabled on every directory of the layers, or on
none. The mount records which of the two applies in ofs-&gt;casefold.
ovl_lookup_single() compares the S_CASEFOLD flag of each file that it
finds against ofs-&gt;casefold and returns -EREMOTE on a mismatch.

Filesystems only set S_CASEFOLD on directories, so with casefolded
layers a regular file, a symlink or a special file never matches and its
lookup fails.

ovl_get_inode() does the same comparison in a WARN_ON_ONCE() for each
new overlay inode. A failed lookup never gets that far, so it is the
first file create that trips the warning.

Reproduced on an overlay over casefolded tmpfs or ext4 directories, with
stat() of a regular file, a symlink and a FIFO, and with a file create:
each stat() failed with EREMOTE. For an unprivileged user in a user
namespace, on tmpfs, stat() of a regular file failed in the same way,
and the file create triggered the warning.

Fix this by limiting both checks to directories.

Fixes: 16754d61dc69 ("ovl: Support mounting case-insensitive enabled layers")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: reserve space for the casefolded name terminator</title>
<updated>2026-10-29T12:40:07+00:00</updated>
<author>
<name>Hungyu Lin</name>
<email>dennylin0707@gmail.com</email>
</author>
<published>2026-10-28T19:51:33+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=a65eb1808177a4381718abe563dddebf840598f9'/>
<id>a65eb1808177a4381718abe563dddebf840598f9</id>
<content type='text'>
ovl_casefold() allocates NAME_MAX bytes and passes that capacity to
utf8_casefold(), which also needs room for a terminating NUL. A valid
name whose folded form is exactly NAME_MAX bytes therefore fails to
fold, and ovl_fill_merge() falls back to comparing the original bytes.

For example, when two casefolded layers contain directories named with
255 uppercase and lowercase ASCII letters respectively, readdir lists
both names even though lookup treats them as the same directory. The
same names merge correctly at 254 bytes.

Reserve one additional byte in both the allocation and the folding
capacity so that NAME_MAX-byte folded names use the canonical merge key.

Fixes: ee95c5fc86dd ("ovl: Create ovl_casefold() to support casefolded strncmp()")
Cc: stable@vger.kernel.org
Signed-off-by: Hungyu Lin &lt;dennylin0707@gmail.com&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
ovl_casefold() allocates NAME_MAX bytes and passes that capacity to
utf8_casefold(), which also needs room for a terminating NUL. A valid
name whose folded form is exactly NAME_MAX bytes therefore fails to
fold, and ovl_fill_merge() falls back to comparing the original bytes.

For example, when two casefolded layers contain directories named with
255 uppercase and lowercase ASCII letters respectively, readdir lists
both names even though lookup treats them as the same directory. The
same names merge correctly at 254 bytes.

Reserve one additional byte in both the allocation and the folding
capacity so that NAME_MAX-byte folded names use the canonical merge key.

Fixes: ee95c5fc86dd ("ovl: Create ovl_casefold() to support casefolded strncmp()")
Cc: stable@vger.kernel.org
Signed-off-by: Hungyu Lin &lt;dennylin0707@gmail.com&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: fix mount setup failing when override_creds drops CAP_SYS_ADMIN</title>
<updated>2026-10-29T12:24:51+00:00</updated>
<author>
<name>Amir Goldstein</name>
<email>amir73il@gmail.com</email>
</author>
<published>2026-10-17T16:43:30+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=333ad304d4794d6384087ea7fc16e58ab642809b'/>
<id>333ad304d4794d6384087ea7fc16e58ab642809b</id>
<content type='text'>
When a process drops CAP_SYS_ADMIN and calls fsconfig(FSCONFIG_SET_FLAG,
"override_creds"), overlayfs saves those restricted credentials in
ofs-&gt;creator_cred. ovl_fill_super() then wraps the entire mount setup in
with_ovl_creds(), switching to those restricted credentials for the
duration.

Mount setup calls clone_private_mount(), which checks CAP_SYS_ADMIN
against current credentials. With restricted credentials active, this
check fails with -EPERM even though the task issuing
FSCONFIG_CMD_CREATE is fully privileged.

Use the mounter's real credentials only for cloning the private layer
mounts. Keep all other backing filesystem operations during mount setup
under the stashed credentials.

Reported-by: Disha Goel &lt;disgoel@linux.ibm.com&gt;
Closes: https://lore.kernel.org/linux-unionfs/20260924112858.51392-1-disgoel@linux.ibm.com
Fixes: c28f922c9dcee ("clone_private_mnt(): make sure that caller has CAP_SYS_ADMIN in the right userns")
Cc: stable@vger.kernel.org
Reviewed-by: Christian Brauner (Amutable) &lt;brauner@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
When a process drops CAP_SYS_ADMIN and calls fsconfig(FSCONFIG_SET_FLAG,
"override_creds"), overlayfs saves those restricted credentials in
ofs-&gt;creator_cred. ovl_fill_super() then wraps the entire mount setup in
with_ovl_creds(), switching to those restricted credentials for the
duration.

Mount setup calls clone_private_mount(), which checks CAP_SYS_ADMIN
against current credentials. With restricted credentials active, this
check fails with -EPERM even though the task issuing
FSCONFIG_CMD_CREATE is fully privileged.

Use the mounter's real credentials only for cloning the private layer
mounts. Keep all other backing filesystem operations during mount setup
under the stashed credentials.

Reported-by: Disha Goel &lt;disgoel@linux.ibm.com&gt;
Closes: https://lore.kernel.org/linux-unionfs/20260924112858.51392-1-disgoel@linux.ibm.com
Fixes: c28f922c9dcee ("clone_private_mnt(): make sure that caller has CAP_SYS_ADMIN in the right userns")
Cc: stable@vger.kernel.org
Reviewed-by: Christian Brauner (Amutable) &lt;brauner@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: set the revalidate flags after d_instantiate()</title>
<updated>2026-10-29T12:24:00+00:00</updated>
<author>
<name>Chris Mason</name>
<email>mason@kernel.org</email>
</author>
<published>2026-10-16T21:17:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=75e89c0055c1d7f7eeb462995f11253ca5864f79'/>
<id>75e89c0055c1d7f7eeb462995f11253ca5864f79</id>
<content type='text'>
stat() of a file on an overlayfs mount triggers a kernel warning and
fails with ECHILD if another task is creating that file at the same
time. This happens on an overlay with a FUSE upper layer, which is the
layer where overlayfs creates new files. Write access to a directory on
such an overlay is all a user needs to run the two tasks.

The revalidate flags are DCACHE_OP_REVALIDATE and
DCACHE_OP_WEAK_REVALIDATE. They tell path walk to call -&gt;d_revalidate()
or -&gt;d_weak_revalidate() on a dentry, so that the filesystem can check
that the cached dentry is still valid. An overlay dentry only has one of
these flags if a dentry in one of its layers has it. FUSE dentries have
the first one.

When a flag is set on an overlay dentry, path walk calls
ovl_dentry_revalidate_common(). That function only expects a dentry
without an inode in RCU walk, while the dentry is being destroyed. For
such a dentry outside of RCU walk, it hits a WARN_ON() and
returns -ECHILD. So a negative overlay dentry must never have the flags
set.

ovl_instantiate() breaks this rule. When a task creates a file, the
overlay dentry for the new name is hashed but still negative.
ovl_instantiate() then works in this order:

- It copies the flags from the new upper dentry to the overlay dentry.
- It calls ovl_get_inode(), which can sleep in a getxattr request to the
  FUSE server.
- Only then does it call d_instantiate().

A second task that looks up the name between the first step and the last
finds a negative dentry with the flags set and revalidates it.

Reproduced with a FUSE server that delays its getxattr reply: stat() of
a name that was being created hit the WARN_ON() and failed with ECHILD.

Fix this by setting the flags after d_instantiate().

Fixes: bccece1ead36 ("ovl: allow remote upper")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
stat() of a file on an overlayfs mount triggers a kernel warning and
fails with ECHILD if another task is creating that file at the same
time. This happens on an overlay with a FUSE upper layer, which is the
layer where overlayfs creates new files. Write access to a directory on
such an overlay is all a user needs to run the two tasks.

The revalidate flags are DCACHE_OP_REVALIDATE and
DCACHE_OP_WEAK_REVALIDATE. They tell path walk to call -&gt;d_revalidate()
or -&gt;d_weak_revalidate() on a dentry, so that the filesystem can check
that the cached dentry is still valid. An overlay dentry only has one of
these flags if a dentry in one of its layers has it. FUSE dentries have
the first one.

When a flag is set on an overlay dentry, path walk calls
ovl_dentry_revalidate_common(). That function only expects a dentry
without an inode in RCU walk, while the dentry is being destroyed. For
such a dentry outside of RCU walk, it hits a WARN_ON() and
returns -ECHILD. So a negative overlay dentry must never have the flags
set.

ovl_instantiate() breaks this rule. When a task creates a file, the
overlay dentry for the new name is hashed but still negative.
ovl_instantiate() then works in this order:

- It copies the flags from the new upper dentry to the overlay dentry.
- It calls ovl_get_inode(), which can sleep in a getxattr request to the
  FUSE server.
- Only then does it call d_instantiate().

A second task that looks up the name between the first step and the last
finds a negative dentry with the flags set and revalidates it.

Reproduced with a FUSE server that delays its getxattr reply: stat() of
a name that was being created hit the WARN_ON() and failed with ECHILD.

Fix this by setting the flags after d_instantiate().

Fixes: bccece1ead36 ("ovl: allow remote upper")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: always encode the origin fh when index is enabled</title>
<updated>2026-10-29T12:23:41+00:00</updated>
<author>
<name>Chris Mason</name>
<email>mason@kernel.org</email>
</author>
<published>2026-10-16T21:17:07+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=949f9d8a5580c77a45fa759123367682aba4d173'/>
<id>949f9d8a5580c77a45fa759123367682aba4d173</id>
<content type='text'>
On an overlay with index=on,nfs_export=on, copy up of a directory
crashes the kernel once an LSM has started to deny CAP_DAC_READ_SEARCH
to the mounter, the task that mounted the overlay. The mount and the LSM
policy change require root. After that, any user who can write to a
directory that exists only in a lower layer triggers the oops by doing
so.

Before a file or directory that exists only in a lower layer is changed
for the first time, overlayfs copies it to the writable upper layer.
This is called copy up.

On copy up, ovl_get_origin_fh() encodes the file handle of the lower
inode, called the origin. It returns NULL if ovl_can_decode_fh() says
that the lower filesystem cannot decode handles. Whether the callers can
handle NULL depends on the index option:

- Without an index, NULL is fine: the callers store an empty origin
  xattr.
- With index enabled, overlayfs keeps entries for copied up inodes in an
  internal index directory. The name of an index entry is derived from
  the handle, so ovl_get_origin_fh() must return a handle or an error.

The mount only enables the index if ovl_can_decode_fh() succeeds for all
layers, but the result can change later. The function checks the export
operations of the filesystem and capable(CAP_DAC_READ_SEARCH). Copy up
runs with the mounter's credentials, and an LSM can start to deny the
capability to those credentials after the mount. ovl_get_origin_fh()
then returns NULL with index enabled.

This goes wrong in two callers:

- With nfs_export=on, copy up of a directory creates an index entry for
  the directory. ovl_create_index() passes the NULL handle to
  ovl_get_index_name_fh(), which dereferences it.
- If the result changes while mount(2) is still running,
  ovl_get_indexdir() passes a NULL from ovl_get_origin_fh() to
  ovl_verify_set_fh() in the same way.

Before the commit in the Fixes tag, these two callers encoded the handle
themselves, without consulting ovl_can_decode_fh().

Reproduced with a BPF LSM program that started to deny the capability
after the mount. An unprivileged touch in a directory that had not been
copied up yet oopsed in ovl_get_index_name_fh(). With this patch the
touch succeeds and the directory gets its index entry.

Fix this by calling ovl_can_decode_fh() in ovl_get_origin_fh() only when
index is disabled.

Fixes: 5b02bfc1e7e3 ("ovl: do not encode lower fh with upper sb_writers held")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
On an overlay with index=on,nfs_export=on, copy up of a directory
crashes the kernel once an LSM has started to deny CAP_DAC_READ_SEARCH
to the mounter, the task that mounted the overlay. The mount and the LSM
policy change require root. After that, any user who can write to a
directory that exists only in a lower layer triggers the oops by doing
so.

Before a file or directory that exists only in a lower layer is changed
for the first time, overlayfs copies it to the writable upper layer.
This is called copy up.

On copy up, ovl_get_origin_fh() encodes the file handle of the lower
inode, called the origin. It returns NULL if ovl_can_decode_fh() says
that the lower filesystem cannot decode handles. Whether the callers can
handle NULL depends on the index option:

- Without an index, NULL is fine: the callers store an empty origin
  xattr.
- With index enabled, overlayfs keeps entries for copied up inodes in an
  internal index directory. The name of an index entry is derived from
  the handle, so ovl_get_origin_fh() must return a handle or an error.

The mount only enables the index if ovl_can_decode_fh() succeeds for all
layers, but the result can change later. The function checks the export
operations of the filesystem and capable(CAP_DAC_READ_SEARCH). Copy up
runs with the mounter's credentials, and an LSM can start to deny the
capability to those credentials after the mount. ovl_get_origin_fh()
then returns NULL with index enabled.

This goes wrong in two callers:

- With nfs_export=on, copy up of a directory creates an index entry for
  the directory. ovl_create_index() passes the NULL handle to
  ovl_get_index_name_fh(), which dereferences it.
- If the result changes while mount(2) is still running,
  ovl_get_indexdir() passes a NULL from ovl_get_origin_fh() to
  ovl_verify_set_fh() in the same way.

Before the commit in the Fixes tag, these two callers encoded the handle
themselves, without consulting ovl_can_decode_fh().

Reproduced with a BPF LSM program that started to deny the capability
after the mount. An unprivileged touch in a directory that had not been
copied up yet oopsed in ovl_get_index_name_fh(). With this patch the
touch succeeds and the directory gets its index entry.

Fix this by calling ovl_can_decode_fh() in ovl_get_origin_fh() only when
index is disabled.

Fixes: 5b02bfc1e7e3 ("ovl: do not encode lower fh with upper sb_writers held")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: read the nlink xattr of every indexed non-dir</title>
<updated>2026-10-29T12:22:58+00:00</updated>
<author>
<name>Chris Mason</name>
<email>mason@kernel.org</email>
</author>
<published>2026-10-16T21:17:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=eb644ed5c6d9afdb1554c41cef1f39d150fbc554'/>
<id>eb644ed5c6d9afdb1554c41cef1f39d150fbc554</id>
<content type='text'>
With nfs_export=on, data written through a file handle to a lower file,
i.e. a file that exists only in a lower layer of the overlay, can be
lost on the next mount. A lookup of the file by name then returns -EIO.
Also, a file with one name reports st_nlink 2 once it has been copied to
the upper layer. After such a file is removed, its file handle can still
open it until the next mount.

Before a lower file is changed for the first time, overlayfs copies it
to the writable upper layer. This is called copy up.

The index directory is $workdir/index, where $workdir is the directory
given by the workdir= option. Each entry is named after the file handle
of a lower inode, so that overlayfs can get from the handle to the
copied up inode.

With nfs_export=on, copy up indexes every non-directory: it copies the
file to an entry in the index directory and then hard links that entry
into the upper directory. The index entry is an extra link to the upper
inode, so overlayfs stores the link count that users should see in the
overlay.nlink xattr.

A file can also be opened by handle, either by an NFS client or by a
process with CAP_DAC_READ_SEARCH calling open_by_handle_at(). If the
file is not in the dcache, its overlay dentry is disconnected, i.e. it
has no parent yet. Copy up then creates the index entry but no hard link
in the upper directory. The index entry has i_nlink 1 and holds the only
copy of the data written to the file.

ovl_get_nlink() reads the stored count. Its caller passes a default,
which the function returns if there is no stored count. If the lower
inode has i_nlink 1, the function returns the default without reading
the xattr. That was correct as long as only lower files with multiple
links were indexed.

On the next mount, ovl_verify_index() looks for orphans, i.e. index
entries of files whose last name was removed. For an index entry with
i_nlink 1, it passes a default of 0 to ovl_get_nlink() and treats a
result of 0 as an orphan. Here the lower inode also has i_nlink 1, so
the result is the default. ovl_indexdir_cleanup() then replaces the
entry with a whiteout, which in the index marks a file as deleted. The
written data is lost.

A lookup by name then returns -EIO, because a whiteout as the index
entry of a lower file that still exists is a bad index.

ovl_get_inode() gets the wrong count the same way. Its default is the
i_nlink of the upper inode, which includes the index entry, so a copied
up file with one name reports st_nlink 2.

When that name is removed, the count drops to 1 rather than 0.
ovl_nlink_end() replaces the index entry with a whiteout only at a count
of 0, so the entry stays, and the handle of the removed file still opens
it.

Reproduced by writing through open_by_handle_at() to an uncached lower
file with one link. After a remount the data was lost, and the name
returned EIO.

Fix this by adding an argument to ovl_get_nlink() that says whether the
caller found an index entry, and by skipping the xattr only if the
caller found none.

Known gaps:
- An index entry that a kernel without this patch left behind for a
  removed file now stays, and the handle of that file keeps working. The
  entry has i_nlink 1 and a stored count of 1, just like the entry of a
  file that was only written by handle. Without this patch, the next
  mount treated it as an orphan.

Fixes: fbd2d2074bde ("ovl: index all non-dir on copy up for NFS export")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
With nfs_export=on, data written through a file handle to a lower file,
i.e. a file that exists only in a lower layer of the overlay, can be
lost on the next mount. A lookup of the file by name then returns -EIO.
Also, a file with one name reports st_nlink 2 once it has been copied to
the upper layer. After such a file is removed, its file handle can still
open it until the next mount.

Before a lower file is changed for the first time, overlayfs copies it
to the writable upper layer. This is called copy up.

The index directory is $workdir/index, where $workdir is the directory
given by the workdir= option. Each entry is named after the file handle
of a lower inode, so that overlayfs can get from the handle to the
copied up inode.

With nfs_export=on, copy up indexes every non-directory: it copies the
file to an entry in the index directory and then hard links that entry
into the upper directory. The index entry is an extra link to the upper
inode, so overlayfs stores the link count that users should see in the
overlay.nlink xattr.

A file can also be opened by handle, either by an NFS client or by a
process with CAP_DAC_READ_SEARCH calling open_by_handle_at(). If the
file is not in the dcache, its overlay dentry is disconnected, i.e. it
has no parent yet. Copy up then creates the index entry but no hard link
in the upper directory. The index entry has i_nlink 1 and holds the only
copy of the data written to the file.

ovl_get_nlink() reads the stored count. Its caller passes a default,
which the function returns if there is no stored count. If the lower
inode has i_nlink 1, the function returns the default without reading
the xattr. That was correct as long as only lower files with multiple
links were indexed.

On the next mount, ovl_verify_index() looks for orphans, i.e. index
entries of files whose last name was removed. For an index entry with
i_nlink 1, it passes a default of 0 to ovl_get_nlink() and treats a
result of 0 as an orphan. Here the lower inode also has i_nlink 1, so
the result is the default. ovl_indexdir_cleanup() then replaces the
entry with a whiteout, which in the index marks a file as deleted. The
written data is lost.

A lookup by name then returns -EIO, because a whiteout as the index
entry of a lower file that still exists is a bad index.

ovl_get_inode() gets the wrong count the same way. Its default is the
i_nlink of the upper inode, which includes the index entry, so a copied
up file with one name reports st_nlink 2.

When that name is removed, the count drops to 1 rather than 0.
ovl_nlink_end() replaces the index entry with a whiteout only at a count
of 0, so the entry stays, and the handle of the removed file still opens
it.

Reproduced by writing through open_by_handle_at() to an uncached lower
file with one link. After a remount the data was lost, and the name
returned EIO.

Fix this by adding an argument to ovl_get_nlink() that says whether the
caller found an index entry, and by skipping the xattr only if the
caller found none.

Known gaps:
- An index entry that a kernel without this patch left behind for a
  removed file now stays, and the handle of that file keeps working. The
  entry has i_nlink 1 and a stored count of 1, just like the entry of a
  file that was only written by handle. Without this patch, the next
  mount treated it as an orphan.

Fixes: fbd2d2074bde ("ovl: index all non-dir on copy up for NFS export")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: skip copy up of empty security.capability</title>
<updated>2026-10-29T12:22:48+00:00</updated>
<author>
<name>Amir Goldstein</name>
<email>amir73il@gmail.com</email>
</author>
<published>2026-10-18T09:52:52+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=50912a1715b8a7c614b654011a0db750e372b43e'/>
<id>50912a1715b8a7c614b654011a0db750e372b43e</id>
<content type='text'>
ovl_copy_xattr() gives up if it can't read an attribute that
listxattr() has named.  An empty security.capability, which anybody has
been able to store on any file for a long time, is such a case: it is
listed, and getxattr() has nothing to return for it.  A file in a lower
layer that carries one can't be copied up: it can't be written to,
renamed or have its mode changed.

There is nothing to copy if there are no data.  Go on with the next
one.

Fixes: 8db6c34f1dbc ("Introduce v3 namespaced file capabilities")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Bacik &lt;josef@toxicpanda.com&gt;
[amir: treat -EINVAL as no capability too, and do the same in
 ovl_copy_up_meta_inode_data()]
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
ovl_copy_xattr() gives up if it can't read an attribute that
listxattr() has named.  An empty security.capability, which anybody has
been able to store on any file for a long time, is such a case: it is
listed, and getxattr() has nothing to return for it.  A file in a lower
layer that carries one can't be copied up: it can't be written to,
renamed or have its mode changed.

There is nothing to copy if there are no data.  Go on with the next
one.

Fixes: 8db6c34f1dbc ("Introduce v3 namespaced file capabilities")
Cc: stable@vger.kernel.org
Signed-off-by: Josef Bacik &lt;josef@toxicpanda.com&gt;
[amir: treat -EINVAL as no capability too, and do the same in
 ovl_copy_up_meta_inode_data()]
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
<entry>
<title>ovl: fix a torn path in ovl_get_redirect()</title>
<updated>2026-10-29T12:22:41+00:00</updated>
<author>
<name>Chris Mason</name>
<email>mason@kernel.org</email>
</author>
<published>2026-10-07T12:10:59+00:00</published>
<link rel='alternate' type='text/html' href='https://git.toradex.cn/cgit/linux-toradex.git/commit/?id=2001cd7ca5560198131e46c6ca3c3472b06145c2'/>
<id>2001cd7ca5560198131e46c6ca3c3472b06145c2</id>
<content type='text'>
A hard link of a metacopy file that races with a rename of an ancestor
stores a redirect to the wrong lower file.

Each component of an absolute redirect must be the name that a dentry
has in the parent that the walk continues with. ovl_get_redirect()
copies the name under d_lock and calls dget_parent() after the unlock,
and the callers for a hard link hold no lock that stops a rename across
directories. A rename between the two steps combines the old name with
the new parent.

With delays in ovl_get_redirect(), an unprivileged user hard-linked a
metacopy file while renaming its ancestor to another directory, the
stored redirect named another lower path, and after a remount the file
showed the data of a root-only file.

Fix this by taking the parent first and checking it again under d_lock.

Fixes: e04527fefba6 ("ovl: fix dentry leak in ovl_get_redirect")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Josef Bacik &lt;josef@toxicpanda.com&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</content>
<content type='xhtml'>
<div xmlns='http://www.w3.org/1999/xhtml'>
<pre>
A hard link of a metacopy file that races with a rename of an ancestor
stores a redirect to the wrong lower file.

Each component of an absolute redirect must be the name that a dentry
has in the parent that the walk continues with. ovl_get_redirect()
copies the name under d_lock and calls dget_parent() after the unlock,
and the callers for a hard link hold no lock that stops a rename across
directories. A rename between the two steps combines the old name with
the new parent.

With delays in ovl_get_redirect(), an unprivileged user hard-linked a
metacopy file while renaming its ancestor to another directory, the
stored redirect named another lower path, and after a remount the file
showed the data of a root-only file.

Fix this by taking the parent first and checking it again under d_lock.

Fixes: e04527fefba6 ("ovl: fix dentry leak in ovl_get_redirect")
Cc: stable@vger.kernel.org
Signed-off-by: Chris Mason &lt;mason@kernel.org&gt;
Signed-off-by: Josef Bacik &lt;josef@toxicpanda.com&gt;
Signed-off-by: Amir Goldstein &lt;amir73il@gmail.com&gt;
</pre>
</div>
</content>
</entry>
</feed>
