[Devel,RHEL7,COMMIT] fs: hold reference on original path

Submitted by Konstantin Khorenko on Sept. 23, 2016, 1:32 p.m.

Details

Message ID 201609231332.u8NDW1wp018608@finist_cl7.x64_64.work.ct
State New
Series "fs: hold reference on original path"
Headers show

Commit Message

Konstantin Khorenko Sept. 23, 2016, 1:32 p.m.
The commit is pushed to "branch-rh7-3.10.0-327.28.2.vz7.17.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-327.28.2.vz7.17.9
------>
commit 0c898c51709daf78d1704b90bec1395cadeb3a51
Author: Maxim Patlasov <mpatlasov@virtuozzo.com>
Date:   Fri Sep 23 17:32:01 2016 +0400

    fs: hold reference on original path
    
    struct file holds references on its f_path.mnt and f_path.dentry by calling
    path_get(&f->f_path) from do_dentry_open(). Let's use the same technique
    for f->f_original_path. Otherwise, f_original_path.dentry can be deleted while
    file still references it leading to NULL-ptr-deref on f->f_original_path.dentry->d_inode.
    
    https://jira.sw.ru/browse/PSBM-52373
    
    Signed-off-by: Maxim Patlasov <mpatlasov@virtuozzo.com>
    Reviewed-by: Kirill Tkhai <ktkhai@virtuozzo.com>
---
 fs/file_table.c |  6 ++++++
 fs/open.c       | 18 +++++++++++++++---
 2 files changed, 21 insertions(+), 3 deletions(-)

Patch hide | download patch | download mbox

diff --git a/fs/file_table.c b/fs/file_table.c
index 957c476..b8982d8 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -242,6 +242,8 @@  static void __fput(struct file *file)
 	struct dentry *dentry = file->f_path.dentry;
 	struct vfsmount *mnt = file->f_path.mnt;
 	struct inode *inode = dentry->d_inode;
+	struct dentry *original_dentry = file->f_original_path.dentry;
+	struct vfsmount *original_mnt = file->f_original_path.mnt;
 
 	might_sleep();
 
@@ -273,10 +275,14 @@  static void __fput(struct file *file)
 		drop_file_write_access(file);
 	file->f_path.dentry = NULL;
 	file->f_path.mnt = NULL;
+	file->f_original_path.dentry = NULL;
+	file->f_original_path.mnt = NULL;
 	file->f_inode = NULL;
 	file_free(file);
 	dput(dentry);
 	mntput(mnt);
+	dput(original_dentry);
+	mntput(original_mnt);
 }
 
 static DEFINE_SPINLOCK(delayed_fput_lock);
diff --git a/fs/open.c b/fs/open.c
index 8c066b1..25dbc85 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -893,16 +893,28 @@  int vfs_open(const struct path *path, struct file *filp,
 {
 	struct inode *inode = path->dentry->d_inode;
 	iop_dentry_open_t dentry_open = get_dentry_open_iop(inode);
+	int do_cleanup = 0;
+	int ret;
 
-	if (!filp->f_original_path.mnt)
+	if (!filp->f_original_path.mnt) {
 		filp->f_original_path = *path;
+		path_get(&filp->f_original_path);
+		do_cleanup = 1;
+	}
 
 	if (dentry_open)
-		return dentry_open(path->dentry, filp, cred);
+		ret = dentry_open(path->dentry, filp, cred);
 	else {
 		filp->f_path = *path;
-		return do_dentry_open(filp, NULL, cred);
+		ret = do_dentry_open(filp, NULL, cred);
 	}
+
+	if (ret && do_cleanup) {
+		path_put(&filp->f_original_path);
+		filp->f_original_path.mnt = NULL;
+		filp->f_original_path.dentry = NULL;
+	}
+	return ret;
 }
 EXPORT_SYMBOL(vfs_open);