From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.5-rc2 commit 7e63c87fc2dcf3be9d3aab82d4a0ea085880bdca category: bugfix bugzilla: 37632 CVE: NA
---------------------------
In the past, overlayfs required that lower fs have non null uuid in order to support nfs export and decode copy up origin file handles.
Commit 9df085f3c9a2 ("ovl: relax requirement for non null uuid of lower fs") relaxed this requirement for nfs export support, as long as uuid (even if null) is unique among all lower fs.
However, said commit unintentionally also relaxed the non null uuid requirement for decoding copy up origin file handles, regardless of the unique uuid requirement.
Amend this mistake by disabling decoding of copy up origin file handle from lower fs with a conflicting uuid.
We still encode copy up origin file handles from those fs, because file handles like those already exist in the wild and because they might provide useful information in the future.
There is an unhandled corner case described by Miklos this way: - two filesystems, A and B, both have null uuid - upper layer is on A - lower layer 1 is also on A - lower layer 2 is on B
In this case bad_uuid won't be set for B, because the check only involves the list of lower fs. Hence we'll try to decode a layer 2 origin on layer 1 and fail.
We will deal with this corner case later.
Reported-by: Colin Ian King colin.king@canonical.com Tested-by: Colin Ian King colin.king@canonical.com Link: https://lore.kernel.org/lkml/20191106234301.283006-1-colin.king@canonical.co... Fixes: 9df085f3c9a2 ("ovl: relax requirement for non null uuid ...") Cc: stable@vger.kernel.org # v4.20+ Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/namei.c | 8 ++++++++ fs/overlayfs/ovl_entry.h | 2 ++ fs/overlayfs/super.c | 24 +++++++++++++++++------- 3 files changed, 27 insertions(+), 7 deletions(-)
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index badf039267a2..62515ec25481 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -328,6 +328,14 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, int i;
for (i = 0; i < ofs->numlower; i++) { + /* + * If lower fs uuid is not unique among lower fs we cannot match + * fh->uuid to layer. + */ + if (ofs->lower_layers[i].fsid && + ofs->lower_layers[i].fs->bad_uuid) + continue; + origin = ovl_decode_real_fh(fh, ofs->lower_layers[i].mnt, connected); if (origin) diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 1a1adc697c55..787fe4be835e 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -25,6 +25,8 @@ struct ovl_config { struct ovl_sb { struct super_block *sb; dev_t pseudo_dev; + /* Unusable (conflicting) uuid */ + bool bad_uuid; };
struct ovl_layer { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index ddcf8d3a83bf..3881f07a2434 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1259,7 +1259,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) { unsigned int i;
- if (!ofs->config.nfs_export && !(ofs->config.index && ofs->upper_mnt)) + if (!ofs->config.nfs_export && !ofs->upper_mnt) return true;
for (i = 0; i < ofs->numlowerfs; i++) { @@ -1267,9 +1267,13 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) * We use uuid to associate an overlay lower file handle with a * lower layer, so we can accept lower fs with null uuid as long * as all lower layers with null uuid are on the same fs. + * if we detect multiple lower fs with the same uuid, we + * disable lower file handle decoding on all of them. */ - if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid)) + if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid)) { + ofs->lower_fs[i].bad_uuid = true; return false; + } } return true; } @@ -1281,6 +1285,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) unsigned int i; dev_t dev; int err; + bool bad_uuid = false;
/* fsid 0 is reserved for upper fs even with non upper overlay */ if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb) @@ -1292,11 +1297,15 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) }
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) { - ofs->config.index = false; - ofs->config.nfs_export = false; - pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n", - uuid_is_null(&sb->s_uuid) ? "null" : "conflicting", - path->dentry); + bad_uuid = true; + if (ofs->config.index || ofs->config.nfs_export) { + ofs->config.index = false; + ofs->config.nfs_export = false; + pr_warn("overlayfs: %s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n", + uuid_is_null(&sb->s_uuid) ? "null" : + "conflicting", + path->dentry); + } }
err = get_anon_bdev(&dev); @@ -1307,6 +1316,7 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
ofs->lower_fs[ofs->numlowerfs].sb = sb; ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev; + ofs->lower_fs[ofs->numlowerfs].bad_uuid = bad_uuid; ofs->numlowerfs++;
return ofs->numlowerfs;
From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.6-rc1 commit 94375f9d5103c2eb2f905381993a2fb70c297364 category: bugfix bugzilla: 37632 CVE: NA
---------------------------
Rename lower_layers[] array to layers[], extend its size by one and initialize layers[0] with upper layer values. Lower layers are now addressed with index 1..numlower. layers[0] is reserved even with lower only overlay.
[SzM: replace ofs->numlower with ofs->numlayer, the latter's value is incremented by one]
Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/export.c | 6 ++---- fs/overlayfs/namei.c | 12 +++++------ fs/overlayfs/ovl_entry.h | 4 ++-- fs/overlayfs/super.c | 44 +++++++++++++++++++++++----------------- 4 files changed, 35 insertions(+), 31 deletions(-)
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c index 6fe303850c9e..4e2d2c2b8dec 100644 --- a/fs/overlayfs/export.c +++ b/fs/overlayfs/export.c @@ -436,7 +436,6 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb, struct ovl_layer *layer) { struct ovl_fs *ofs = sb->s_fs_info; - struct ovl_layer upper_layer = { .mnt = ofs->upper_mnt }; struct dentry *index = NULL; struct dentry *this = NULL; struct inode *inode; @@ -478,7 +477,7 @@ static struct dentry *ovl_lookup_real_inode(struct super_block *sb, * recursive call walks back from indexed upper to the topmost * connected/hashed upper parent (or up to root). */ - this = ovl_lookup_real(sb, upper, &upper_layer); + this = ovl_lookup_real(sb, upper, &ofs->layers[0]); dput(upper); }
@@ -658,8 +657,7 @@ static struct dentry *ovl_get_dentry(struct super_block *sb, struct dentry *index) { struct ovl_fs *ofs = sb->s_fs_info; - struct ovl_layer upper_layer = { .mnt = ofs->upper_mnt }; - struct ovl_layer *layer = upper ? &upper_layer : lowerpath->layer; + struct ovl_layer *layer = upper ? &ofs->layers[0] : lowerpath->layer; struct dentry *real = upper ?: (index ?: lowerpath->dentry);
/* diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c index 62515ec25481..145bfdde53fe 100644 --- a/fs/overlayfs/namei.c +++ b/fs/overlayfs/namei.c @@ -327,16 +327,16 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, struct dentry *origin = NULL; int i;
- for (i = 0; i < ofs->numlower; i++) { + for (i = 1; i < ofs->numlayer; i++) { /* * If lower fs uuid is not unique among lower fs we cannot match * fh->uuid to layer. */ - if (ofs->lower_layers[i].fsid && - ofs->lower_layers[i].fs->bad_uuid) + if (ofs->layers[i].fsid && + ofs->layers[i].fs->bad_uuid) continue;
- origin = ovl_decode_real_fh(fh, ofs->lower_layers[i].mnt, + origin = ovl_decode_real_fh(fh, ofs->layers[i].mnt, connected); if (origin) break; @@ -359,7 +359,7 @@ int ovl_check_origin_fh(struct ovl_fs *ofs, struct ovl_fh *fh, bool connected, } **stackp = (struct ovl_path){ .dentry = origin, - .layer = &ofs->lower_layers[i] + .layer = &ofs->layers[i] };
return 0; @@ -894,7 +894,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (!d.stop && poe->numlower) { err = -ENOMEM; - stack = kcalloc(ofs->numlower, sizeof(struct ovl_path), + stack = kcalloc(ofs->numlayer - 1, sizeof(struct ovl_path), GFP_KERNEL); if (!stack) goto out_put_upper; diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 787fe4be835e..5ab6985eecc5 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -48,10 +48,10 @@ struct ovl_path { /* private information held for overlayfs's superblock */ struct ovl_fs { struct vfsmount *upper_mnt; - unsigned int numlower; + unsigned int numlayer; /* Number of unique lower sb that differ from upper sb */ unsigned int numlowerfs; - struct ovl_layer *lower_layers; + struct ovl_layer *layers; struct ovl_sb *lower_fs; /* workbasedir is the path at workdir= mount option */ struct dentry *workbasedir; diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 3881f07a2434..566b92fbbcd9 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -229,13 +229,13 @@ static void ovl_free_fs(struct ovl_fs *ofs) if (ofs->upperdir_locked) ovl_inuse_unlock(ofs->upper_mnt->mnt_root); mntput(ofs->upper_mnt); - for (i = 0; i < ofs->numlower; i++) { - iput(ofs->lower_layers[i].trap); - mntput(ofs->lower_layers[i].mnt); + for (i = 1; i < ofs->numlayer; i++) { + iput(ofs->layers[i].trap); + mntput(ofs->layers[i].mnt); } for (i = 0; i < ofs->numlowerfs; i++) free_anon_bdev(ofs->lower_fs[i].pseudo_dev); - kfree(ofs->lower_layers); + kfree(ofs->layers); kfree(ofs->lower_fs);
kfree(ofs->config.lowerdir); @@ -1322,16 +1322,16 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) return ofs->numlowerfs; }
-static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs, - struct path *stack, unsigned int numlower) +static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, + struct path *stack, unsigned int numlower) { int err; unsigned int i;
err = -ENOMEM; - ofs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer), - GFP_KERNEL); - if (ofs->lower_layers == NULL) + ofs->layers = kcalloc(numlower + 1, sizeof(struct ovl_layer), + GFP_KERNEL); + if (ofs->layers == NULL) goto out;
ofs->lower_fs = kcalloc(numlower, sizeof(struct ovl_sb), @@ -1339,6 +1339,12 @@ static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs, if (ofs->lower_fs == NULL) goto out;
+ /* idx 0 is reserved for upper fs even with lower only overlay */ + ofs->layers[0].mnt = ofs->upper_mnt; + ofs->layers[0].idx = 0; + ofs->layers[0].fsid = 0; + ofs->numlayer = 1; + for (i = 0; i < numlower; i++) { struct vfsmount *mnt; struct inode *trap; @@ -1372,15 +1378,15 @@ static int ovl_get_lower_layers(struct super_block *sb, struct ovl_fs *ofs, */ mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
- ofs->lower_layers[ofs->numlower].trap = trap; - ofs->lower_layers[ofs->numlower].mnt = mnt; - ofs->lower_layers[ofs->numlower].idx = i + 1; - ofs->lower_layers[ofs->numlower].fsid = fsid; + ofs->layers[ofs->numlayer].trap = trap; + ofs->layers[ofs->numlayer].mnt = mnt; + ofs->layers[ofs->numlayer].idx = ofs->numlayer; + ofs->layers[ofs->numlayer].fsid = fsid; if (fsid) { - ofs->lower_layers[ofs->numlower].fs = + ofs->layers[ofs->numlayer].fs = &ofs->lower_fs[fsid - 1]; } - ofs->numlower++; + ofs->numlayer++; }
/* @@ -1467,7 +1473,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, goto out_err; }
- err = ovl_get_lower_layers(sb, ofs, stack, numlower); + err = ovl_get_layers(sb, ofs, stack, numlower); if (err) goto out_err;
@@ -1478,7 +1484,7 @@ static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
for (i = 0; i < numlower; i++) { oe->lowerstack[i].dentry = dget(stack[i].dentry); - oe->lowerstack[i].layer = &ofs->lower_layers[i]; + oe->lowerstack[i].layer = &ofs->layers[i+1]; }
if (remote) @@ -1559,9 +1565,9 @@ static int ovl_check_overlapping_layers(struct super_block *sb, return err; }
- for (i = 0; i < ofs->numlower; i++) { + for (i = 1; i < ofs->numlayer; i++) { err = ovl_check_layer(sb, ofs, - ofs->lower_layers[i].mnt->mnt_root, + ofs->layers[i].mnt->mnt_root, "lowerdir"); if (err) return err;
From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.6-rc1 commit 0f831ec85eda1ae27490baba106aba632c1d8e94 category: bugfix bugzilla: 37632 CVE: NA
---------------------------
No code uses the sb returned from this helper, so make it retrun a boolean and rename it to ovl_same_fs().
The xino mode is irrelevant when all layers are on same fs, so instead of describing samefs with mode OVL_XINO_OFF, use a new xino_mode state, which is 0 in the case of samefs, -1 in the case of xino=off and > 0 with xino enabled.
Create a new helper ovl_same_dev(), to use instead of the common check for (ovl_same_fs() || xinobits).
Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com
Conflicts: fs/overlayfs/overlayfs.h [ Non bugfix patch 1e92e3072c14("ovl: abstract ovl_inode lock with a helper") reconstructed code is not applied. ] fs/overlayfs/super.c [ Non bugfix patch 1bd0a3aea435("ovl: use pr_fmt auto generate prefix") reconstructed code is not applied. ]
Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/inode.c | 8 ++++---- fs/overlayfs/overlayfs.h | 17 +++++++++++++---- fs/overlayfs/ovl_entry.h | 9 +++++++-- fs/overlayfs/readdir.c | 4 ++-- fs/overlayfs/super.c | 21 +++++++++++---------- fs/overlayfs/util.c | 12 ------------ 6 files changed, 37 insertions(+), 34 deletions(-)
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 8b3c284ce92e..681c706f1bec 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -81,7 +81,7 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, struct ovl_layer *lower_layer) { - bool samefs = ovl_same_sb(dentry->d_sb); + bool samefs = ovl_same_fs(dentry->d_sb); unsigned int xinobits = ovl_xino_bits(dentry->d_sb);
if (samefs) { @@ -149,7 +149,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, struct path realpath; const struct cred *old_cred; bool is_dir = S_ISDIR(dentry->d_inode->i_mode); - bool samefs = ovl_same_sb(dentry->d_sb); + bool samefs = ovl_same_fs(dentry->d_sb); struct ovl_layer *lower_layer = NULL; int err; bool metacopy_blocks = false; @@ -171,7 +171,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, * If lower filesystem supports NFS file handles, this also guaranties * persistent st_ino across mount cycle. */ - if (!is_dir || samefs || ovl_xino_bits(dentry->d_sb)) { + if (!is_dir || ovl_same_dev(dentry->d_sb)) { if (!OVL_TYPE_UPPER(type)) { lower_layer = ovl_layer_lower(dentry); } else if (OVL_TYPE_ORIGIN(type)) { @@ -568,7 +568,7 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev, * ovl_new_inode(), ino arg is 0, so i_ino will be updated to real * upper inode i_ino on ovl_inode_init() or ovl_inode_update(). */ - if (ovl_same_sb(inode->i_sb) || xinobits) { + if (ovl_same_dev(inode->i_sb)) { inode->i_ino = ino; if (xinobits && fsid && !(ino >> (64 - xinobits))) inode->i_ino |= (unsigned long)fsid << (64 - xinobits); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 265bf9cfde08..a342015e0c23 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -208,7 +208,6 @@ int ovl_want_write(struct dentry *dentry); void ovl_drop_write(struct dentry *dentry); struct dentry *ovl_workdir(struct dentry *dentry); const struct cred *ovl_override_creds(struct super_block *sb); -struct super_block *ovl_same_sb(struct super_block *sb); int ovl_can_decode_fh(struct super_block *sb); struct dentry *ovl_indexdir(struct super_block *sb); bool ovl_index_all(struct super_block *sb); @@ -286,11 +285,21 @@ static inline bool ovl_is_impuredir(struct dentry *dentry) return ovl_check_dir_xattr(dentry, OVL_XATTR_IMPURE); }
-static inline unsigned int ovl_xino_bits(struct super_block *sb) +/* All layers on same fs? */ +static inline bool ovl_same_fs(struct super_block *sb) { - struct ovl_fs *ofs = sb->s_fs_info; + return OVL_FS(sb)->xino_mode == 0; +}
- return ofs->xino_bits; +/* All overlay inodes have same st_dev? */ +static inline bool ovl_same_dev(struct super_block *sb) +{ + return OVL_FS(sb)->xino_mode >= 0; +} + +static inline unsigned int ovl_xino_bits(struct super_block *sb) +{ + return ovl_same_dev(sb) ? OVL_FS(sb)->xino_mode : 0; }
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 5ab6985eecc5..590a95398038 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -74,10 +74,15 @@ struct ovl_fs { struct inode *workbasedir_trap; struct inode *workdir_trap; struct inode *indexdir_trap; - /* Inode numbers in all layers do not use the high xino_bits */ - unsigned int xino_bits; + /* -1: disabled, 0: same fs, 1..32: number of unused ino bits */ + int xino_mode; };
+static inline struct ovl_fs *OVL_FS(struct super_block *sb) +{ + return (struct ovl_fs *)sb->s_fs_info; +} + /* private information held for every overlayfs dentry */ struct ovl_entry { union { diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 11b7941c5dbc..ae99b90a8b98 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -472,7 +472,7 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) int xinobits = ovl_xino_bits(dir->d_sb); int err = 0;
- if (!ovl_same_sb(dir->d_sb) && !xinobits) + if (!ovl_same_dev(dir->d_sb)) goto out;
if (p->name[0] == '.') { @@ -747,7 +747,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) * entries. */ if (ovl_xino_bits(dentry->d_sb) || - (ovl_same_sb(dentry->d_sb) && + (ovl_same_fs(dentry->d_sb) && (ovl_is_impure_dir(file) || OVL_TYPE_MERGE(ovl_path_type(dentry->d_parent))))) { return ovl_iterate_real(file, ctx); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 566b92fbbcd9..14d1bc31a490 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -363,7 +363,7 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry) if (ofs->config.nfs_export != ovl_nfs_export_def) seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ? "on" : "off"); - if (ofs->config.xino != ovl_xino_def()) + if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(sb)) seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]); if (ofs->config.metacopy != ovl_metacopy_def) seq_printf(m, ",metacopy=%s", @@ -814,7 +814,7 @@ static int ovl_lower_dir(const char *name, struct path *path,
/* Check if lower fs has 32bit inode numbers */ if (fh_type != FILEID_INO32_GEN) - ofs->xino_bits = 0; + ofs->xino_mode = -1;
return 0;
@@ -1145,7 +1145,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
/* Check if upper fs has 32bit inode numbers */ if (fh_type != FILEID_INO32_GEN) - ofs->xino_bits = 0; + ofs->xino_mode = -1;
/* NFS export of r/w mount depends on index */ if (ofs->config.nfs_export && !ofs->config.index) { @@ -1398,21 +1398,22 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, * inode number. */ if (!ofs->numlowerfs || (ofs->numlowerfs == 1 && !ofs->upper_mnt)) { - ofs->xino_bits = 0; - ofs->config.xino = OVL_XINO_OFF; - } else if (ofs->config.xino == OVL_XINO_ON && !ofs->xino_bits) { + if (ofs->config.xino == OVL_XINO_ON) + pr_info(""xino=on" is useless with all layers on same fs, ignore.\n"); + ofs->xino_mode = 0; + } else if (ofs->config.xino == OVL_XINO_ON && ofs->xino_mode < 0) { /* * This is a roundup of number of bits needed for numlowerfs+1 * (i.e. ilog2(numlowerfs+1 - 1) + 1). fsid 0 is reserved for * upper fs even with non upper overlay. */ BUILD_BUG_ON(ilog2(OVL_MAX_STACK) > 31); - ofs->xino_bits = ilog2(ofs->numlowerfs) + 1; + ofs->xino_mode = ilog2(ofs->numlowerfs) + 1; }
- if (ofs->xino_bits) { + if (ofs->xino_mode > 0) { pr_info("overlayfs: "xino" feature enabled using %d upper inode bits.\n", - ofs->xino_bits); + ofs->xino_mode); }
err = 0; @@ -1613,7 +1614,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_maxbytes = MAX_LFS_FILESIZE; /* Assume underlaying fs uses 32bit inodes unless proven otherwise */ if (ofs->config.xino != OVL_XINO_OFF) - ofs->xino_bits = BITS_PER_LONG - 32; + ofs->xino_mode = BITS_PER_LONG - 32;
/* alloc/destroy_inode needed for setting up traps in inode cache */ sb->s_op = &ovl_super_operations; diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index d684dc08a6bf..d0570ac2788b 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -43,18 +43,6 @@ const struct cred *ovl_override_creds(struct super_block *sb) return override_creds(ofs->creator_cred); }
-struct super_block *ovl_same_sb(struct super_block *sb) -{ - struct ovl_fs *ofs = sb->s_fs_info; - - if (!ofs->numlowerfs) - return ofs->upper_mnt->mnt_sb; - else if (ofs->numlowerfs == 1 && !ofs->upper_mnt) - return ofs->lower_fs[0].sb; - else - return NULL; -} - /* * Check if underlying fs supports file handles and try to determine encoding * type, in order to deduce maximum inode number used by fs.
From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.6-rc1 commit 07f1e59637a8e5a8bddba5da7567d46635da510f category: bugfix bugzilla: 37632 CVE: NA
---------------------------
Rename lower_fs[] array to fs[], extend its size by one and use index fsid (instead of fsid-1) to access the fs[] array.
Initialize fs[0] with upper fs values. fsid 0 is reserved even with lower only overlay, so fs[0] remains null in this case.
Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com
Conflicts: fs/overlayfs/super.c [ Non bugfix patch 1bd0a3aea435("ovl: use pr_fmt auto generate prefix") reconstructed code is not applied. ]
Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/inode.c | 21 ++++++------- fs/overlayfs/ovl_entry.h | 6 ++-- fs/overlayfs/super.c | 65 +++++++++++++++++++++------------------- 3 files changed, 46 insertions(+), 46 deletions(-)
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 681c706f1bec..b20202de78fd 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -78,8 +78,7 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) return err; }
-static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, - struct ovl_layer *lower_layer) +static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid) { bool samefs = ovl_same_fs(dentry->d_sb); unsigned int xinobits = ovl_xino_bits(dentry->d_sb); @@ -106,9 +105,7 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, pr_warn_ratelimited("overlayfs: inode number too big (%pd2, ino=%llu, xinobits=%d)\n", dentry, stat->ino, xinobits); } else { - if (lower_layer) - stat->ino |= ((u64)lower_layer->fsid) << shift; - + stat->ino |= ((u64)fsid) << shift; stat->dev = dentry->d_sb->s_dev; return 0; } @@ -127,7 +124,7 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, */ stat->dev = dentry->d_sb->s_dev; stat->ino = dentry->d_inode->i_ino; - } else if (lower_layer && lower_layer->fsid) { + } else if (fsid) { /* * For non-samefs setup, if we cannot map all layers st_ino * to a unified address space, we need to make sure that st_dev @@ -135,7 +132,7 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, * lower layers use the unique anonymous bdev assigned to the * lower fs. */ - stat->dev = lower_layer->fs->pseudo_dev; + stat->dev = OVL_FS(dentry->d_sb)->fs[fsid].pseudo_dev; }
return 0; @@ -150,7 +147,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, const struct cred *old_cred; bool is_dir = S_ISDIR(dentry->d_inode->i_mode); bool samefs = ovl_same_fs(dentry->d_sb); - struct ovl_layer *lower_layer = NULL; + int fsid = 0; int err; bool metacopy_blocks = false;
@@ -173,7 +170,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, */ if (!is_dir || ovl_same_dev(dentry->d_sb)) { if (!OVL_TYPE_UPPER(type)) { - lower_layer = ovl_layer_lower(dentry); + fsid = ovl_layer_lower(dentry)->fsid; } else if (OVL_TYPE_ORIGIN(type)) { struct kstat lowerstat; u32 lowermask = STATX_INO | STATX_BLOCKS | @@ -203,13 +200,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat, if (ovl_test_flag(OVL_INDEX, d_inode(dentry)) || (!ovl_verify_lower(dentry->d_sb) && (is_dir || lowerstat.nlink == 1))) { - lower_layer = ovl_layer_lower(dentry); + fsid = ovl_layer_lower(dentry)->fsid; /* * Cannot use origin st_dev;st_ino because * origin inode content may differ from overlay * inode content. */ - if (samefs || lower_layer->fsid) + if (samefs || fsid) stat->ino = lowerstat.ino; }
@@ -244,7 +241,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, } }
- err = ovl_map_dev_ino(dentry, stat, lower_layer); + err = ovl_map_dev_ino(dentry, stat, fsid); if (err) goto out;
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 590a95398038..40e5ac36768c 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -49,10 +49,10 @@ struct ovl_path { struct ovl_fs { struct vfsmount *upper_mnt; unsigned int numlayer; - /* Number of unique lower sb that differ from upper sb */ - unsigned int numlowerfs; + /* Number of unique fs among layers including upper fs */ + unsigned int numfs; struct ovl_layer *layers; - struct ovl_sb *lower_fs; + struct ovl_sb *fs; /* workbasedir is the path at workdir= mount option */ struct dentry *workbasedir; /* workdir is the 'work' directory under workbasedir */ diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 14d1bc31a490..b5d966e30766 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -233,10 +233,11 @@ static void ovl_free_fs(struct ovl_fs *ofs) iput(ofs->layers[i].trap); mntput(ofs->layers[i].mnt); } - for (i = 0; i < ofs->numlowerfs; i++) - free_anon_bdev(ofs->lower_fs[i].pseudo_dev); kfree(ofs->layers); - kfree(ofs->lower_fs); + /* fs[0].pseudo_dev is either null or real upper st_dev */ + for (i = 1; i < ofs->numfs; i++) + free_anon_bdev(ofs->fs[i].pseudo_dev); + kfree(ofs->fs);
kfree(ofs->config.lowerdir); kfree(ofs->config.upperdir); @@ -1262,7 +1263,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) if (!ofs->config.nfs_export && !ofs->upper_mnt) return true;
- for (i = 0; i < ofs->numlowerfs; i++) { + for (i = 1; i < ofs->numfs; i++) { /* * We use uuid to associate an overlay lower file handle with a * lower layer, so we can accept lower fs with null uuid as long @@ -1270,8 +1271,8 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) * if we detect multiple lower fs with the same uuid, we * disable lower file handle decoding on all of them. */ - if (uuid_equal(&ofs->lower_fs[i].sb->s_uuid, uuid)) { - ofs->lower_fs[i].bad_uuid = true; + if (uuid_equal(&ofs->fs[i].sb->s_uuid, uuid)) { + ofs->fs[i].bad_uuid = true; return false; } } @@ -1287,13 +1288,9 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) int err; bool bad_uuid = false;
- /* fsid 0 is reserved for upper fs even with non upper overlay */ - if (ofs->upper_mnt && ofs->upper_mnt->mnt_sb == sb) - return 0; - - for (i = 0; i < ofs->numlowerfs; i++) { - if (ofs->lower_fs[i].sb == sb) - return i + 1; + for (i = 0; i < ofs->numfs; i++) { + if (ofs->fs[i].sb == sb) + return i; }
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) { @@ -1314,12 +1311,11 @@ static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path) return err; }
- ofs->lower_fs[ofs->numlowerfs].sb = sb; - ofs->lower_fs[ofs->numlowerfs].pseudo_dev = dev; - ofs->lower_fs[ofs->numlowerfs].bad_uuid = bad_uuid; - ofs->numlowerfs++; + ofs->fs[ofs->numfs].sb = sb; + ofs->fs[ofs->numfs].pseudo_dev = dev; + ofs->fs[ofs->numfs].bad_uuid = bad_uuid;
- return ofs->numlowerfs; + return ofs->numfs++; }
static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, @@ -1334,17 +1330,27 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, if (ofs->layers == NULL) goto out;
- ofs->lower_fs = kcalloc(numlower, sizeof(struct ovl_sb), - GFP_KERNEL); - if (ofs->lower_fs == NULL) + ofs->fs = kcalloc(numlower + 1, sizeof(struct ovl_sb), GFP_KERNEL); + if (ofs->fs == NULL) goto out;
- /* idx 0 is reserved for upper fs even with lower only overlay */ + /* idx/fsid 0 are reserved for upper fs even with lower only overlay */ + ofs->numfs++; + ofs->layers[0].mnt = ofs->upper_mnt; ofs->layers[0].idx = 0; ofs->layers[0].fsid = 0; ofs->numlayer = 1;
+ /* + * All lower layers that share the same fs as upper layer, use the real + * upper st_dev. + */ + if (ofs->upper_mnt) { + ofs->fs[0].sb = ofs->upper_mnt->mnt_sb; + ofs->fs[0].pseudo_dev = ofs->upper_mnt->mnt_sb->s_dev; + } + for (i = 0; i < numlower; i++) { struct vfsmount *mnt; struct inode *trap; @@ -1382,10 +1388,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ofs->layers[ofs->numlayer].mnt = mnt; ofs->layers[ofs->numlayer].idx = ofs->numlayer; ofs->layers[ofs->numlayer].fsid = fsid; - if (fsid) { - ofs->layers[ofs->numlayer].fs = - &ofs->lower_fs[fsid - 1]; - } + ofs->layers[ofs->numlayer].fs = &ofs->fs[fsid]; ofs->numlayer++; }
@@ -1397,18 +1400,18 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, * bits reserved for fsid, it emits a warning and uses the original * inode number. */ - if (!ofs->numlowerfs || (ofs->numlowerfs == 1 && !ofs->upper_mnt)) { + if (ofs->numfs - !ofs->upper_mnt == 1) { if (ofs->config.xino == OVL_XINO_ON) pr_info(""xino=on" is useless with all layers on same fs, ignore.\n"); ofs->xino_mode = 0; } else if (ofs->config.xino == OVL_XINO_ON && ofs->xino_mode < 0) { /* - * This is a roundup of number of bits needed for numlowerfs+1 - * (i.e. ilog2(numlowerfs+1 - 1) + 1). fsid 0 is reserved for - * upper fs even with non upper overlay. + * This is a roundup of number of bits needed for encoding + * fsid, where fsid 0 is reserved for upper fs even with + * lower only overlay. */ BUILD_BUG_ON(ilog2(OVL_MAX_STACK) > 31); - ofs->xino_mode = ilog2(ofs->numlowerfs) + 1; + ofs->xino_mode = ilog2(ofs->numfs - 1) + 1; }
if (ofs->xino_mode > 0) {
From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.6-rc1 commit 1b81dddd354cf304574d79004400a6385613ae4e category: bugfix bugzilla: 37632 CVE: NA
---------------------------
This fixes ovl_lower_uuid_ok() to correctly detect the corner case: - two filesystems, A and B, both have null uuid - upper layer is on A - lower layer 1 is also on A - lower layer 2 is on B
In this case, bad_uuid would not have been set for B, because the check only involved the list of lower fs. Hence we'll try to decode a layer 2 origin on layer 1 and fail.
We check for conflicting (and null) uuid among all lower layers, including those layers that are on the same fs as the upper layer.
Reported-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/ovl_entry.h | 2 ++ fs/overlayfs/super.c | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index 40e5ac36768c..ae09310f5a6f 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -27,6 +27,8 @@ struct ovl_sb { dev_t pseudo_dev; /* Unusable (conflicting) uuid */ bool bad_uuid; + /* Used as a lower layer (but maybe also as upper) */ + bool is_lower; };
struct ovl_layer { diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index b5d966e30766..256984859e53 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1263,7 +1263,7 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) if (!ofs->config.nfs_export && !ofs->upper_mnt) return true;
- for (i = 1; i < ofs->numfs; i++) { + for (i = 0; i < ofs->numfs; i++) { /* * We use uuid to associate an overlay lower file handle with a * lower layer, so we can accept lower fs with null uuid as long @@ -1271,7 +1271,8 @@ static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid) * if we detect multiple lower fs with the same uuid, we * disable lower file handle decoding on all of them. */ - if (uuid_equal(&ofs->fs[i].sb->s_uuid, uuid)) { + if (ofs->fs[i].is_lower && + uuid_equal(&ofs->fs[i].sb->s_uuid, uuid)) { ofs->fs[i].bad_uuid = true; return false; } @@ -1345,10 +1346,12 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, /* * All lower layers that share the same fs as upper layer, use the real * upper st_dev. + * is_lower will be set if upper fs is shared with a lower layer. */ if (ofs->upper_mnt) { ofs->fs[0].sb = ofs->upper_mnt->mnt_sb; ofs->fs[0].pseudo_dev = ofs->upper_mnt->mnt_sb->s_dev; + ofs->fs[0].is_lower = false; }
for (i = 0; i < numlower; i++) { @@ -1390,6 +1393,7 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ofs->layers[ofs->numlayer].fsid = fsid; ofs->layers[ofs->numlayer].fs = &ofs->fs[fsid]; ofs->numlayer++; + ofs->fs[fsid].is_lower = true; }
/*
From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.6-rc1 commit b7bf9908e17c4dc4b80876f299ac03ddf188efd9 category: bugfix bugzilla: 37632 CVE: NA
---------------------------
On non-samefs overlay without xino, non pure upper inodes should use a pseudo_dev assigned to each unique lower fs, but if lower layer is on the same fs and upper layer, it has no pseudo_dev assigned.
In this overlay layers setup: - two filesystems, A and B - upper layer is on A - lower layer 1 is also on A - lower layer 2 is on B
Non pure upper overlay inode, whose origin is in layer 1 will have the st_dev;st_ino values of the real lower inode before copy up and the st_dev;st_ino values of the real upper inode after copy up.
Fix this inconsitency by assigning a unique pseudo_dev also for upper fs, that will be used as st_dev value along with the lower inode st_dev for overlay inodes in the case above.
Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/inode.c | 16 ++++------------ fs/overlayfs/super.c | 15 ++++++++++----- 2 files changed, 14 insertions(+), 17 deletions(-)
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index b20202de78fd..252a3c6b4744 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -124,13 +124,12 @@ static int ovl_map_dev_ino(struct dentry *dentry, struct kstat *stat, int fsid) */ stat->dev = dentry->d_sb->s_dev; stat->ino = dentry->d_inode->i_ino; - } else if (fsid) { + } else { /* * For non-samefs setup, if we cannot map all layers st_ino * to a unified address space, we need to make sure that st_dev - * is unique per lower fs. Upper layer uses real st_dev and - * lower layers use the unique anonymous bdev assigned to the - * lower fs. + * is unique per underlying fs, so we use the unique anonymous + * bdev assigned to the underlying fs. */ stat->dev = OVL_FS(dentry->d_sb)->fs[fsid].pseudo_dev; } @@ -146,7 +145,6 @@ int ovl_getattr(const struct path *path, struct kstat *stat, struct path realpath; const struct cred *old_cred; bool is_dir = S_ISDIR(dentry->d_inode->i_mode); - bool samefs = ovl_same_fs(dentry->d_sb); int fsid = 0; int err; bool metacopy_blocks = false; @@ -201,13 +199,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat, (!ovl_verify_lower(dentry->d_sb) && (is_dir || lowerstat.nlink == 1))) { fsid = ovl_layer_lower(dentry)->fsid; - /* - * Cannot use origin st_dev;st_ino because - * origin inode content may differ from overlay - * inode content. - */ - if (samefs || fsid) - stat->ino = lowerstat.ino; + stat->ino = lowerstat.ino; }
/* diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 256984859e53..48bb2f4ee05e 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -234,8 +234,7 @@ static void ovl_free_fs(struct ovl_fs *ofs) mntput(ofs->layers[i].mnt); } kfree(ofs->layers); - /* fs[0].pseudo_dev is either null or real upper st_dev */ - for (i = 1; i < ofs->numfs; i++) + for (i = 0; i < ofs->numfs; i++) free_anon_bdev(ofs->fs[i].pseudo_dev); kfree(ofs->fs);
@@ -1344,13 +1343,19 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, ofs->numlayer = 1;
/* - * All lower layers that share the same fs as upper layer, use the real - * upper st_dev. + * All lower layers that share the same fs as upper layer, use the same + * pseudo_dev as upper layer. Allocate fs[0].pseudo_dev even for lower + * only overlay to simplify ovl_fs_free(). * is_lower will be set if upper fs is shared with a lower layer. */ + err = get_anon_bdev(&ofs->fs[0].pseudo_dev); + if (err) { + pr_err("failed to get anonymous bdev for upper fs\n"); + goto out; + } + if (ofs->upper_mnt) { ofs->fs[0].sb = ofs->upper_mnt->mnt_sb; - ofs->fs[0].pseudo_dev = ofs->upper_mnt->mnt_sb->s_dev; ofs->fs[0].is_lower = false; }
From: Amir Goldstein amir73il@gmail.com
mainline inclusion from mainline-5.6-rc6 commit 53afcd310e867d25e394718558783c476301205c category: bugfix bugzilla: 37632 CVE: NA
---------------------------
Fix up two bugs in the coversion to xino_mode: 1. xino=off does not always end up in disabled mode 2. xino=auto on 32bit arch should end up in disabled mode
Take a proactive approach to disabling xino on 32bit kernel: 1. Disable XINO_AUTO config during build time 2. Disable xino with a warning on mount time
As a by product, xino=on on 32bit arch also ends up in disabled mode. We never intended to enable xino on 32bit arch and this will make the rest of the logic simpler.
Fixes: 0f831ec85eda ("ovl: simplify ovl_same_sb() helper") Signed-off-by: Amir Goldstein amir73il@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/Kconfig | 1 + fs/overlayfs/super.c | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/fs/overlayfs/Kconfig b/fs/overlayfs/Kconfig index 2ef91be2a04e..fb977df7212d 100644 --- a/fs/overlayfs/Kconfig +++ b/fs/overlayfs/Kconfig @@ -92,6 +92,7 @@ config OVERLAY_FS_XINO_AUTO bool "Overlayfs: auto enable inode number mapping" default n depends on OVERLAY_FS + depends on 64BIT help If this config option is enabled then overlay filesystems will use unused high bits in undelying filesystem inode numbers to map all diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 48bb2f4ee05e..2859caffa4dc 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -1413,6 +1413,8 @@ static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs, if (ofs->config.xino == OVL_XINO_ON) pr_info(""xino=on" is useless with all layers on same fs, ignore.\n"); ofs->xino_mode = 0; + } else if (ofs->config.xino == OVL_XINO_OFF) { + ofs->xino_mode = -1; } else if (ofs->config.xino == OVL_XINO_ON && ofs->xino_mode < 0) { /* * This is a roundup of number of bits needed for encoding @@ -1625,8 +1627,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent) sb->s_stack_depth = 0; sb->s_maxbytes = MAX_LFS_FILESIZE; /* Assume underlaying fs uses 32bit inodes unless proven otherwise */ - if (ofs->config.xino != OVL_XINO_OFF) + if (ofs->config.xino != OVL_XINO_OFF) { ofs->xino_mode = BITS_PER_LONG - 32; + if (!ofs->xino_mode) { + pr_warn("xino not supported on 32bit kernel, falling back to xino=off.\n"); + ofs->config.xino = OVL_XINO_OFF; + } + }
/* alloc/destroy_inode needed for setting up traps in inode cache */ sb->s_op = &ovl_super_operations;
From: Miklos Szeredi mszeredi@redhat.com
mainline inclusion from mainline-5.7-rc1 commit 83552eacdfc06378b0993a3705833c1fd0aead4b category: bugfix bugzilla: 37632 CVE: NA
---------------------------
Changes to underlying layers should not cause WARN_ON(), but this repro does:
mkdir w l u mnt sudo mount -t overlay -o workdir=w,lowerdir=l,upperdir=u overlay mnt touch mnt/h ln u/h u/k rm -rf mnt/k rm -rf mnt/h dmesg
------------[ cut here ]------------ WARNING: CPU: 1 PID: 116244 at fs/inode.c:302 drop_nlink+0x28/0x40
After upper hardlinks were added while overlay is mounted, unlinking all overlay hardlinks drops overlay nlink to zero before all upper inodes are unlinked.
After unlink/rename prevent i_nlink from going to zero if there are still hashed aliases (i.e. cached hard links to the victim) remaining.
Reported-by: Phasip phasip@gmail.com Signed-off-by: Miklos Szeredi mszeredi@redhat.com
Conflicts: fs/overlayfs/dir.c [ Non bugfix patch 0e32992f7fac("ovl: remove the 'locked' argument of ovl_nlink_{start,end}") removed all locked arg is not applied. ]
Signed-off-by: Zhihao Cheng chengzhihao1@huawei.com Reviewed-by: zhangyi (F) yi.zhang@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/overlayfs/dir.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-)
diff --git a/fs/overlayfs/dir.c b/fs/overlayfs/dir.c index 800bcad67325..2228b38abdb1 100644 --- a/fs/overlayfs/dir.c +++ b/fs/overlayfs/dir.c @@ -824,6 +824,28 @@ static bool ovl_pure_upper(struct dentry *dentry) !ovl_test_flag(OVL_WHITEOUTS, d_inode(dentry)); }
+static void ovl_drop_nlink(struct dentry *dentry) +{ + struct inode *inode = d_inode(dentry); + struct dentry *alias; + + /* Try to find another, hashed alias */ + spin_lock(&inode->i_lock); + hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) { + if (alias != dentry && !d_unhashed(alias)) + break; + } + spin_unlock(&inode->i_lock); + + /* + * Changes to underlying layers may cause i_nlink to lose sync with + * reality. In this case prevent the link count from going to zero + * prematurely. + */ + if (inode->i_nlink > !!alias) + drop_nlink(inode); +} + static int ovl_do_remove(struct dentry *dentry, bool is_dir) { int err; @@ -862,7 +884,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) if (is_dir) clear_nlink(dentry->d_inode); else - drop_nlink(dentry->d_inode); + ovl_drop_nlink(dentry); } ovl_nlink_end(dentry, locked);
@@ -1205,7 +1227,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old, if (new_is_dir) clear_nlink(d_inode(new)); else - drop_nlink(d_inode(new)); + ovl_drop_nlink(new); }
ovl_dir_modified(old->d_parent, ovl_type_origin(old) ||