|
Kmods SIG |
c540c3 |
From bcf0d9d4b76976f892154efdfc509b256fd898e8 Mon Sep 17 00:00:00 2001
|
|
Kmods SIG |
c540c3 |
From: Al Viro <viro@zeniv.linux.org.uk>
|
|
Kmods SIG |
c540c3 |
Date: Sun, 3 Nov 2019 12:07:15 -0500
|
|
Kmods SIG |
c540c3 |
Subject: [Backport bcf0d9d4b769] ecryptfs: fix unlink and rmdir in face of
|
|
Kmods SIG |
c540c3 |
underlying fs modifications
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
A problem similar to the one caught in commit 74dd7c97ea2a ("ecryptfs_rename():
|
|
Kmods SIG |
c540c3 |
verify that lower dentries are still OK after lock_rename()") exists for
|
|
Kmods SIG |
c540c3 |
unlink/rmdir as well.
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
Instead of playing with dget_parent() of underlying dentry of victim
|
|
Kmods SIG |
c540c3 |
and hoping it's the same as underlying dentry of our directory,
|
|
Kmods SIG |
c540c3 |
do the following:
|
|
Kmods SIG |
c540c3 |
* find the underlying dentry of victim
|
|
Kmods SIG |
c540c3 |
* find the underlying directory of victim's parent (stable
|
|
Kmods SIG |
c540c3 |
since the victim is ecryptfs dentry and inode of its parent is
|
|
Kmods SIG |
c540c3 |
held exclusive by the caller).
|
|
Kmods SIG |
c540c3 |
* lock the inode of dentry underlying the victim's parent
|
|
Kmods SIG |
c540c3 |
* check that underlying dentry of victim is still hashed and
|
|
Kmods SIG |
c540c3 |
has the right parent - it can be moved, but it can't be moved to/from
|
|
Kmods SIG |
c540c3 |
the directory we are holding exclusive. So while ->d_parent itself
|
|
Kmods SIG |
c540c3 |
might not be stable, the result of comparison is.
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
If the check passes, everything is fine - underlying directory is locked,
|
|
Kmods SIG |
c540c3 |
underlying victim is still a child of that directory and we can go ahead
|
|
Kmods SIG |
c540c3 |
and feed them to vfs_unlink(). As in the current mainline we need to
|
|
Kmods SIG |
c540c3 |
pin the underlying dentry of victim, so that it wouldn't go negative under
|
|
Kmods SIG |
c540c3 |
us, but that's the only temporary reference that needs to be grabbed there.
|
|
Kmods SIG |
c540c3 |
Underlying dentry of parent won't go away (it's pinned by the parent,
|
|
Kmods SIG |
c540c3 |
which is held by caller), so there's no need to grab it.
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
The same problem (with the same solution) exists for rmdir. Moreover,
|
|
Kmods SIG |
c540c3 |
rename gets simpler and more robust with the same "don't bother with
|
|
Kmods SIG |
c540c3 |
dget_parent()" approach.
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
Fixes: 74dd7c97ea2 "ecryptfs_rename(): verify that lower dentries are still OK after lock_rename()"
|
|
Kmods SIG |
c540c3 |
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
|
|
Kmods SIG |
c540c3 |
---
|
|
Kmods SIG |
c540c3 |
src/inode.c | 65 ++++++++++++++++++++++++++++-----------------
|
|
Kmods SIG |
c540c3 |
1 file changed, 40 insertions(+), 25 deletions(-)
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
diff --git a/src/inode.c b/src/inode.c
|
|
Kmods SIG |
c540c3 |
index 18426f4855f11b3e561f1cc35ee381fac3f2cfcc..a905d5f4f3b0726303595e39ba40309d68baa422 100644
|
|
Kmods SIG |
c540c3 |
--- a/src/inode.c
|
|
Kmods SIG |
c540c3 |
+++ b/src/inode.c
|
|
Kmods SIG |
c540c3 |
@@ -128,13 +128,20 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
|
|
Kmods SIG |
c540c3 |
struct inode *inode)
|
|
Kmods SIG |
c540c3 |
{
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
|
Kmods SIG |
c540c3 |
- struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir);
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_dir_dentry;
|
|
Kmods SIG |
c540c3 |
+ struct inode *lower_dir_inode;
|
|
Kmods SIG |
c540c3 |
int rc;
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
- dget(lower_dentry);
|
|
Kmods SIG |
c540c3 |
- lower_dir_dentry = lock_parent(lower_dentry);
|
|
Kmods SIG |
c540c3 |
- rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
|
|
Kmods SIG |
c540c3 |
+ lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
|
|
Kmods SIG |
c540c3 |
+ lower_dir_inode = d_inode(lower_dir_dentry);
|
|
Kmods SIG |
c540c3 |
+ inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
|
|
Kmods SIG |
c540c3 |
+ dget(lower_dentry); // don't even try to make the lower negative
|
|
Kmods SIG |
c540c3 |
+ if (lower_dentry->d_parent != lower_dir_dentry)
|
|
Kmods SIG |
c540c3 |
+ rc = -EINVAL;
|
|
Kmods SIG |
c540c3 |
+ else if (d_unhashed(lower_dentry))
|
|
Kmods SIG |
c540c3 |
+ rc = -EINVAL;
|
|
Kmods SIG |
c540c3 |
+ else
|
|
Kmods SIG |
c540c3 |
+ rc = vfs_unlink(lower_dir_inode, lower_dentry, NULL);
|
|
Kmods SIG |
c540c3 |
if (rc) {
|
|
Kmods SIG |
c540c3 |
printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc);
|
|
Kmods SIG |
c540c3 |
goto out_unlock;
|
|
Kmods SIG |
c540c3 |
@@ -142,10 +149,11 @@ static int ecryptfs_do_unlink(struct inode *dir, struct dentry *dentry,
|
|
Kmods SIG |
c540c3 |
fsstack_copy_attr_times(dir, lower_dir_inode);
|
|
Kmods SIG |
c540c3 |
set_nlink(inode, ecryptfs_inode_to_lower(inode)->i_nlink);
|
|
Kmods SIG |
c540c3 |
inode->i_ctime = dir->i_ctime;
|
|
Kmods SIG |
c540c3 |
- d_drop(dentry);
|
|
Kmods SIG |
c540c3 |
out_unlock:
|
|
Kmods SIG |
c540c3 |
- unlock_dir(lower_dir_dentry);
|
|
Kmods SIG |
c540c3 |
dput(lower_dentry);
|
|
Kmods SIG |
c540c3 |
+ inode_unlock(lower_dir_inode);
|
|
Kmods SIG |
c540c3 |
+ if (!rc)
|
|
Kmods SIG |
c540c3 |
+ d_drop(dentry);
|
|
Kmods SIG |
c540c3 |
return rc;
|
|
Kmods SIG |
c540c3 |
}
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
@@ -512,22 +520,30 @@ static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|
Kmods SIG |
c540c3 |
{
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_dentry;
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_dir_dentry;
|
|
Kmods SIG |
c540c3 |
+ struct inode *lower_dir_inode;
|
|
Kmods SIG |
c540c3 |
int rc;
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
lower_dentry = ecryptfs_dentry_to_lower(dentry);
|
|
Kmods SIG |
c540c3 |
- dget(dentry);
|
|
Kmods SIG |
c540c3 |
- lower_dir_dentry = lock_parent(lower_dentry);
|
|
Kmods SIG |
c540c3 |
- dget(lower_dentry);
|
|
Kmods SIG |
c540c3 |
- rc = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry);
|
|
Kmods SIG |
c540c3 |
- dput(lower_dentry);
|
|
Kmods SIG |
c540c3 |
- if (!rc && d_really_is_positive(dentry))
|
|
Kmods SIG |
c540c3 |
+ lower_dir_dentry = ecryptfs_dentry_to_lower(dentry->d_parent);
|
|
Kmods SIG |
c540c3 |
+ lower_dir_inode = d_inode(lower_dir_dentry);
|
|
Kmods SIG |
c540c3 |
+
|
|
Kmods SIG |
c540c3 |
+ inode_lock_nested(lower_dir_inode, I_MUTEX_PARENT);
|
|
Kmods SIG |
c540c3 |
+ dget(lower_dentry); // don't even try to make the lower negative
|
|
Kmods SIG |
c540c3 |
+ if (lower_dentry->d_parent != lower_dir_dentry)
|
|
Kmods SIG |
c540c3 |
+ rc = -EINVAL;
|
|
Kmods SIG |
c540c3 |
+ else if (d_unhashed(lower_dentry))
|
|
Kmods SIG |
c540c3 |
+ rc = -EINVAL;
|
|
Kmods SIG |
c540c3 |
+ else
|
|
Kmods SIG |
c540c3 |
+ rc = vfs_rmdir(lower_dir_inode, lower_dentry);
|
|
Kmods SIG |
c540c3 |
+ if (!rc) {
|
|
Kmods SIG |
c540c3 |
clear_nlink(d_inode(dentry));
|
|
Kmods SIG |
c540c3 |
- fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry));
|
|
Kmods SIG |
c540c3 |
- set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink);
|
|
Kmods SIG |
c540c3 |
- unlock_dir(lower_dir_dentry);
|
|
Kmods SIG |
c540c3 |
+ fsstack_copy_attr_times(dir, lower_dir_inode);
|
|
Kmods SIG |
c540c3 |
+ set_nlink(dir, lower_dir_inode->i_nlink);
|
|
Kmods SIG |
c540c3 |
+ }
|
|
Kmods SIG |
c540c3 |
+ dput(lower_dentry);
|
|
Kmods SIG |
c540c3 |
+ inode_unlock(lower_dir_inode);
|
|
Kmods SIG |
c540c3 |
if (!rc)
|
|
Kmods SIG |
c540c3 |
d_drop(dentry);
|
|
Kmods SIG |
c540c3 |
- dput(dentry);
|
|
Kmods SIG |
c540c3 |
return rc;
|
|
Kmods SIG |
c540c3 |
}
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
@@ -565,20 +581,22 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_new_dentry;
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_old_dir_dentry;
|
|
Kmods SIG |
c540c3 |
struct dentry *lower_new_dir_dentry;
|
|
Kmods SIG |
c540c3 |
- struct dentry *trap = NULL;
|
|
Kmods SIG |
c540c3 |
+ struct dentry *trap;
|
|
Kmods SIG |
c540c3 |
struct inode *target_inode;
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
if (flags)
|
|
Kmods SIG |
c540c3 |
return -EINVAL;
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
+ lower_old_dir_dentry = ecryptfs_dentry_to_lower(old_dentry->d_parent);
|
|
Kmods SIG |
c540c3 |
+ lower_new_dir_dentry = ecryptfs_dentry_to_lower(new_dentry->d_parent);
|
|
Kmods SIG |
c540c3 |
+
|
|
Kmods SIG |
c540c3 |
lower_old_dentry = ecryptfs_dentry_to_lower(old_dentry);
|
|
Kmods SIG |
c540c3 |
lower_new_dentry = ecryptfs_dentry_to_lower(new_dentry);
|
|
Kmods SIG |
c540c3 |
- dget(lower_old_dentry);
|
|
Kmods SIG |
c540c3 |
- dget(lower_new_dentry);
|
|
Kmods SIG |
c540c3 |
- lower_old_dir_dentry = dget_parent(lower_old_dentry);
|
|
Kmods SIG |
c540c3 |
- lower_new_dir_dentry = dget_parent(lower_new_dentry);
|
|
Kmods SIG |
c540c3 |
+
|
|
Kmods SIG |
c540c3 |
target_inode = d_inode(new_dentry);
|
|
Kmods SIG |
c540c3 |
+
|
|
Kmods SIG |
c540c3 |
trap = lock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
|
Kmods SIG |
c540c3 |
+ dget(lower_new_dentry);
|
|
Kmods SIG |
c540c3 |
rc = -EINVAL;
|
|
Kmods SIG |
c540c3 |
if (lower_old_dentry->d_parent != lower_old_dir_dentry)
|
|
Kmods SIG |
c540c3 |
goto out_lock;
|
|
Kmods SIG |
c540c3 |
@@ -606,11 +624,8 @@ ecryptfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
Kmods SIG |
c540c3 |
if (new_dir != old_dir)
|
|
Kmods SIG |
c540c3 |
fsstack_copy_attr_all(old_dir, d_inode(lower_old_dir_dentry));
|
|
Kmods SIG |
c540c3 |
out_lock:
|
|
Kmods SIG |
c540c3 |
- unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
|
Kmods SIG |
c540c3 |
- dput(lower_new_dir_dentry);
|
|
Kmods SIG |
c540c3 |
- dput(lower_old_dir_dentry);
|
|
Kmods SIG |
c540c3 |
dput(lower_new_dentry);
|
|
Kmods SIG |
c540c3 |
- dput(lower_old_dentry);
|
|
Kmods SIG |
c540c3 |
+ unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry);
|
|
Kmods SIG |
c540c3 |
return rc;
|
|
Kmods SIG |
c540c3 |
}
|
|
Kmods SIG |
c540c3 |
|
|
Kmods SIG |
c540c3 |
--
|
|
Kmods SIG |
c540c3 |
2.31.1
|
|
Kmods SIG |
c540c3 |
|