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 pathspull/1260/head
commit
28fb80f089
|
|
@ -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::
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue