overlayfs update for 6.16

-----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQSQHSd0lITzzeNWNm3h3BK/laaZPAUCaEKzdwAKCRDh3BK/laaZ
 PLpuAQCK2B/LsbyLslWVN6lWbQNwiPHF7l49+GjS2BaWVDxnTwEAwdpaktgg7tRI
 wsMp9CEc0lbp8lMDjHDOEqhc/Qvejg4=
 =Y7HA
 -----END PGP SIGNATURE-----

Merge tag 'ovl-update-v2-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs

Pull overlayfs update from Miklos Szeredi:

 - Fix a regression in getting the path of an open file (e.g. in
   /proc/PID/maps) for a nested overlayfs setup (André Almeida)

 - Support data-only layers and verity in a user namespace (unprivileged
   composefs use case)

 - Fix a gcc warning (Kees)

 - Cleanups

* tag 'ovl-update-v2-6.16' of git://git.kernel.org/pub/scm/linux/kernel/git/overlayfs/vfs:
  ovl: Annotate struct ovl_entry with __counted_by()
  ovl: Replace offsetof() with struct_size() in ovl_stack_free()
  ovl: Replace offsetof() with struct_size() in ovl_cache_entry_new()
  ovl: Check for NULL d_inode() in ovl_dentry_upper()
  ovl: Use str_on_off() helper in ovl_show_options()
  ovl: don't require "metacopy=on" for "verity"
  ovl: relax redirect/metacopy requirements for lower -> data redirect
  ovl: make redirect/metacopy rejection consistent
  ovl: Fix nested backing file paths
pull/1260/head
Linus Torvalds 2025-06-06 17:54:09 -07:00
commit 28fb80f089
7 changed files with 84 additions and 80 deletions

View File

@ -443,6 +443,13 @@ Only the data of the files in the "data-only" lower layers may be visible
when a "metacopy" file in one of the lower layers above it, has a "redirect"
to the absolute path of the "lower data" file in the "data-only" lower layer.
Instead of explicitly enabling "metacopy=on" it is sufficient to specify at
least one data-only layer to enable redirection of data to a data-only layer.
In this case other forms of metacopy are rejected. Note: this way data-only
layers may be used toghether with "userxattr", in which case careful attention
must be given to privileges needed to change the "user.overlay.redirect" xattr
to prevent misuse.
Since kernel version v6.8, "data-only" lower layers can also be added using
the "datadir+" mount options and the fsconfig syscall from new mount api.
For example::

View File

@ -48,8 +48,8 @@ static struct file *ovl_open_realfile(const struct file *file,
if (!inode_owner_or_capable(real_idmap, realinode))
flags &= ~O_NOATIME;
realfile = backing_file_open(&file->f_path, flags, realpath,
current_cred());
realfile = backing_file_open(file_user_path((struct file *) file),
flags, realpath, current_cred());
}
ovl_revert_creds(old_cred);

View File

@ -16,6 +16,7 @@
struct ovl_lookup_data {
struct super_block *sb;
struct dentry *dentry;
const struct ovl_layer *layer;
struct qstr name;
bool is_dir;
@ -24,6 +25,7 @@ struct ovl_lookup_data {
bool stop;
bool last;
char *redirect;
char *upperredirect;
int metacopy;
/* Referring to last redirect xattr */
bool absolute_redirect;
@ -1024,6 +1026,31 @@ int ovl_verify_lowerdata(struct dentry *dentry)
return ovl_maybe_validate_verity(dentry);
}
/*
* Following redirects/metacopy can have security consequences: it's like a
* symlink into the lower layer without the permission checks.
*
* This is only a problem if the upper layer is untrusted (e.g comes from an USB
* drive). This can allow a non-readable file or directory to become readable.
*
* Only following redirects when redirects are enabled disables this attack
* vector when not necessary.
*/
static bool ovl_check_follow_redirect(struct ovl_lookup_data *d)
{
struct ovl_fs *ofs = OVL_FS(d->sb);
if (d->metacopy && !ofs->config.metacopy) {
pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", d->dentry);
return false;
}
if ((d->redirect || d->upperredirect) && !ovl_redirect_follow(ofs)) {
pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n", d->dentry);
return false;
}
return true;
}
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
@ -1039,7 +1066,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
unsigned int ctr = 0;
struct inode *inode = NULL;
bool upperopaque = false;
char *upperredirect = NULL;
bool check_redirect = (ovl_redirect_follow(ofs) || ofs->numdatalayer);
struct dentry *this;
unsigned int i;
int err;
@ -1047,12 +1074,14 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
int metacopy_size = 0;
struct ovl_lookup_data d = {
.sb = dentry->d_sb,
.dentry = dentry,
.name = dentry->d_name,
.is_dir = false,
.opaque = false,
.stop = false,
.last = ovl_redirect_follow(ofs) ? false : !ovl_numlower(poe),
.last = check_redirect ? false : !ovl_numlower(poe),
.redirect = NULL,
.upperredirect = NULL,
.metacopy = 0,
};
@ -1094,8 +1123,8 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (d.redirect) {
err = -ENOMEM;
upperredirect = kstrdup(d.redirect, GFP_KERNEL);
if (!upperredirect)
d.upperredirect = kstrdup(d.redirect, GFP_KERNEL);
if (!d.upperredirect)
goto out_put_upper;
if (d.redirect[0] == '/')
poe = roe;
@ -1113,7 +1142,12 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
for (i = 0; !d.stop && i < ovl_numlower(poe); i++) {
struct ovl_path lower = ovl_lowerstack(poe)[i];
if (!ovl_redirect_follow(ofs))
if (!ovl_check_follow_redirect(&d)) {
err = -EPERM;
goto out_put;
}
if (!check_redirect)
d.last = i == ovl_numlower(poe) - 1;
else if (d.is_dir || !ofs->numdatalayer)
d.last = lower.layer->idx == ovl_numlower(roe);
@ -1126,13 +1160,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (!this)
continue;
if ((uppermetacopy || d.metacopy) && !ofs->config.metacopy) {
dput(this);
err = -EPERM;
pr_warn_ratelimited("refusing to follow metacopy origin for (%pd2)\n", dentry);
goto out_put;
}
/*
* If no origin fh is stored in upper of a merge dir, store fh
* of lower dir and set upper parent "impure".
@ -1185,23 +1212,6 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
ctr++;
}
/*
* Following redirects can have security consequences: it's like
* a symlink into the lower layer without the permission checks.
* This is only a problem if the upper layer is untrusted (e.g
* comes from an USB drive). This can allow a non-readable file
* or directory to become readable.
*
* Only following redirects when redirects are enabled disables
* this attack vector when not necessary.
*/
err = -EPERM;
if (d.redirect && !ovl_redirect_follow(ofs)) {
pr_warn_ratelimited("refusing to follow redirect for (%pd2)\n",
dentry);
goto out_put;
}
if (d.stop)
break;
@ -1212,10 +1222,16 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
}
}
/* Defer lookup of lowerdata in data-only layers to first access */
/*
* Defer lookup of lowerdata in data-only layers to first access.
* Don't require redirect=follow and metacopy=on in this case.
*/
if (d.metacopy && ctr && ofs->numdatalayer && d.absolute_redirect) {
d.metacopy = 0;
ctr++;
} else if (!ovl_check_follow_redirect(&d)) {
err = -EPERM;
goto out_put;
}
/*
@ -1298,20 +1314,26 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
/*
* It's safe to assign upperredirect here: the previous
* assignment of happens only if upperdentry is non-NULL, and
* assignment happens only if upperdentry is non-NULL, and
* this one only if upperdentry is NULL.
*/
upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
if (IS_ERR(upperredirect)) {
err = PTR_ERR(upperredirect);
upperredirect = NULL;
d.upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
if (IS_ERR(d.upperredirect)) {
err = PTR_ERR(d.upperredirect);
d.upperredirect = NULL;
goto out_free_oe;
}
err = ovl_check_metacopy_xattr(ofs, &upperpath, NULL);
if (err < 0)
goto out_free_oe;
uppermetacopy = err;
d.metacopy = uppermetacopy = err;
metacopy_size = err;
if (!ovl_check_follow_redirect(&d)) {
err = -EPERM;
goto out_free_oe;
}
}
if (upperdentry || ctr) {
@ -1319,7 +1341,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
.upperdentry = upperdentry,
.oe = oe,
.index = index,
.redirect = upperredirect,
.redirect = d.upperredirect,
};
/* Store lowerdata redirect for lazy lookup */
@ -1361,7 +1383,7 @@ out_put_upper:
kfree(origin_path);
}
dput(upperdentry);
kfree(upperredirect);
kfree(d.upperredirect);
out:
kfree(d.redirect);
ovl_revert_creds(old_cred);

View File

@ -51,7 +51,7 @@ struct ovl_path {
struct ovl_entry {
unsigned int __numlower;
struct ovl_path __lowerstack[];
struct ovl_path __lowerstack[] __counted_by(__numlower);
};
/* private information held for overlayfs's superblock */

View File

@ -871,18 +871,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
config->uuid = OVL_UUID_NULL;
}
/* Resolve verity -> metacopy dependency */
if (config->verity_mode && !config->metacopy) {
/* Don't allow explicit specified conflicting combinations */
if (set.metacopy) {
pr_err("conflicting options: metacopy=off,verity=%s\n",
ovl_verity_mode(config));
return -EINVAL;
}
/* Otherwise automatically enable metacopy. */
config->metacopy = true;
}
/*
* This is to make the logic below simpler. It doesn't make any other
* difference, since redirect_dir=on is only used for upper.
@ -890,18 +878,13 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
if (!config->upperdir && config->redirect_mode == OVL_REDIRECT_FOLLOW)
config->redirect_mode = OVL_REDIRECT_ON;
/* Resolve verity -> metacopy -> redirect_dir dependency */
/* metacopy -> redirect_dir dependency */
if (config->metacopy && config->redirect_mode != OVL_REDIRECT_ON) {
if (set.metacopy && set.redirect) {
pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
ovl_redirect_mode(config));
return -EINVAL;
}
if (config->verity_mode && set.redirect) {
pr_err("conflicting options: verity=%s,redirect_dir=%s\n",
ovl_verity_mode(config), ovl_redirect_mode(config));
return -EINVAL;
}
if (set.redirect) {
/*
* There was an explicit redirect_dir=... that resulted
@ -970,7 +953,7 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
}
/* Resolve userxattr -> !redirect && !metacopy && !verity dependency */
/* Resolve userxattr -> !redirect && !metacopy dependency */
if (config->userxattr) {
if (set.redirect &&
config->redirect_mode != OVL_REDIRECT_NOFOLLOW) {
@ -982,11 +965,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
pr_err("conflicting options: userxattr,metacopy=on\n");
return -EINVAL;
}
if (config->verity_mode) {
pr_err("conflicting options: userxattr,verity=%s\n",
ovl_verity_mode(config));
return -EINVAL;
}
/*
* Silently disable default setting of redirect and metacopy.
* This shall be the default in the future as well: these
@ -1025,11 +1003,6 @@ int ovl_fs_params_verify(const struct ovl_fs_context *ctx,
*/
}
if (ctx->nr_data > 0 && !config->metacopy) {
pr_err("lower data-only dirs require metacopy support.\n");
return -EINVAL;
}
return 0;
}
@ -1078,17 +1051,16 @@ int ovl_show_options(struct seq_file *m, struct dentry *dentry)
seq_printf(m, ",redirect_dir=%s",
ovl_redirect_mode(&ofs->config));
if (ofs->config.index != ovl_index_def)
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
seq_printf(m, ",index=%s", str_on_off(ofs->config.index));
if (ofs->config.uuid != ovl_uuid_def())
seq_printf(m, ",uuid=%s", ovl_uuid_mode(&ofs->config));
if (ofs->config.nfs_export != ovl_nfs_export_def)
seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
"on" : "off");
seq_printf(m, ",nfs_export=%s",
str_on_off(ofs->config.nfs_export));
if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(ofs))
seq_printf(m, ",xino=%s", ovl_xino_mode(&ofs->config));
if (ofs->config.metacopy != ovl_metacopy_def)
seq_printf(m, ",metacopy=%s",
ofs->config.metacopy ? "on" : "off");
seq_printf(m, ",metacopy=%s", str_on_off(ofs->config.metacopy));
if (ofs->config.ovl_volatile)
seq_puts(m, ",volatile");
if (ofs->config.userxattr)

View File

@ -13,6 +13,7 @@
#include <linux/security.h>
#include <linux/cred.h>
#include <linux/ratelimit.h>
#include <linux/overflow.h>
#include "overlayfs.h"
struct ovl_cache_entry {
@ -147,9 +148,8 @@ static struct ovl_cache_entry *ovl_cache_entry_new(struct ovl_readdir_data *rdd,
u64 ino, unsigned int d_type)
{
struct ovl_cache_entry *p;
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
p = kmalloc(size, GFP_KERNEL);
p = kmalloc(struct_size(p, name, len + 1), GFP_KERNEL);
if (!p)
return NULL;

View File

@ -15,6 +15,7 @@
#include <linux/uuid.h>
#include <linux/namei.h>
#include <linux/ratelimit.h>
#include <linux/overflow.h>
#include "overlayfs.h"
/* Get write access to upper mnt - may fail if upper sb was remounted ro */
@ -145,9 +146,9 @@ void ovl_stack_free(struct ovl_path *stack, unsigned int n)
struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
{
size_t size = offsetof(struct ovl_entry, __lowerstack[numlower]);
struct ovl_entry *oe = kzalloc(size, GFP_KERNEL);
struct ovl_entry *oe;
oe = kzalloc(struct_size(oe, __lowerstack, numlower), GFP_KERNEL);
if (oe)
oe->__numlower = numlower;
@ -305,7 +306,9 @@ enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path)
struct dentry *ovl_dentry_upper(struct dentry *dentry)
{
return ovl_upperdentry_dereference(OVL_I(d_inode(dentry)));
struct inode *inode = d_inode(dentry);
return inode ? ovl_upperdentry_dereference(OVL_I(inode)) : NULL;
}
struct dentry *ovl_dentry_lower(struct dentry *dentry)