Skip to content

Commit 11f3710

Browse files
Miklos Szerediszmi
authored andcommitted
ovl: verify upper dentry before unlink and rename
Unlink and rename in overlayfs checked the upper dentry for staleness by verifying upper->d_parent against upperdir. However the dentry can go stale also by being unhashed, for example. Expand the verification to actually look up the name again (under parent lock) and check if it matches the upper dentry. This matches what the VFS does before passing the dentry to filesytem's unlink/rename methods, which excludes any inconsistency caused by overlayfs. Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 3c2de27 commit 11f3710

File tree

1 file changed

+38
-21
lines changed

1 file changed

+38
-21
lines changed

fs/overlayfs/dir.c

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -596,21 +596,25 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
596596
{
597597
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
598598
struct inode *dir = upperdir->d_inode;
599-
struct dentry *upper = ovl_dentry_upper(dentry);
599+
struct dentry *upper;
600600
int err;
601601

602602
inode_lock_nested(dir, I_MUTEX_PARENT);
603+
upper = lookup_one_len(dentry->d_name.name, upperdir,
604+
dentry->d_name.len);
605+
err = PTR_ERR(upper);
606+
if (IS_ERR(upper))
607+
goto out_unlock;
608+
603609
err = -ESTALE;
604-
if (upper->d_parent == upperdir) {
605-
/* Don't let d_delete() think it can reset d_inode */
606-
dget(upper);
610+
if (upper == ovl_dentry_upper(dentry)) {
607611
if (is_dir)
608612
err = vfs_rmdir(dir, upper);
609613
else
610614
err = vfs_unlink(dir, upper, NULL);
611-
dput(upper);
612615
ovl_dentry_version_inc(dentry->d_parent);
613616
}
617+
dput(upper);
614618

615619
/*
616620
* Keeping this dentry hashed would mean having to release
@@ -620,6 +624,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
620624
*/
621625
if (!err)
622626
d_drop(dentry);
627+
out_unlock:
623628
inode_unlock(dir);
624629

625630
return err;
@@ -840,29 +845,39 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
840845

841846
trap = lock_rename(new_upperdir, old_upperdir);
842847

843-
olddentry = ovl_dentry_upper(old);
844-
newdentry = ovl_dentry_upper(new);
845-
if (newdentry) {
848+
849+
olddentry = lookup_one_len(old->d_name.name, old_upperdir,
850+
old->d_name.len);
851+
err = PTR_ERR(olddentry);
852+
if (IS_ERR(olddentry))
853+
goto out_unlock;
854+
855+
err = -ESTALE;
856+
if (olddentry != ovl_dentry_upper(old))
857+
goto out_dput_old;
858+
859+
newdentry = lookup_one_len(new->d_name.name, new_upperdir,
860+
new->d_name.len);
861+
err = PTR_ERR(newdentry);
862+
if (IS_ERR(newdentry))
863+
goto out_dput_old;
864+
865+
err = -ESTALE;
866+
if (ovl_dentry_upper(new)) {
846867
if (opaquedir) {
847-
newdentry = opaquedir;
848-
opaquedir = NULL;
868+
if (newdentry != opaquedir)
869+
goto out_dput;
849870
} else {
850-
dget(newdentry);
871+
if (newdentry != ovl_dentry_upper(new))
872+
goto out_dput;
851873
}
852874
} else {
853875
new_create = true;
854-
newdentry = lookup_one_len(new->d_name.name, new_upperdir,
855-
new->d_name.len);
856-
err = PTR_ERR(newdentry);
857-
if (IS_ERR(newdentry))
858-
goto out_unlock;
876+
if (!d_is_negative(newdentry) &&
877+
(!new_opaque || !ovl_is_whiteout(newdentry)))
878+
goto out_dput;
859879
}
860880

861-
err = -ESTALE;
862-
if (olddentry->d_parent != old_upperdir)
863-
goto out_dput;
864-
if (newdentry->d_parent != new_upperdir)
865-
goto out_dput;
866881
if (olddentry == trap)
867882
goto out_dput;
868883
if (newdentry == trap)
@@ -925,6 +940,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
925940

926941
out_dput:
927942
dput(newdentry);
943+
out_dput_old:
944+
dput(olddentry);
928945
out_unlock:
929946
unlock_rename(new_upperdir, old_upperdir);
930947
out_revert_creds:

0 commit comments

Comments
 (0)