aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2015-01-18 23:37:32 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2015-01-18 23:49:26 -0500
commit378ff1a53b5724f3ac97b0aba3c9ecac072f6fcd (patch)
treecd81852109bbf82a77ff54de1f00d61408c3d920 /fs/cifs
parentec6f34e5b552fb0a52e6aae1a5afbbb1605cc6cc (diff)
fix deadlock in cifs_ioctl_clone()
It really needs to check that src is non-directory *and* use {un,}lock_two_nodirectories(). As it is, it's trivial to cause double-lock (ioctl(fd, CIFS_IOC_COPYCHUNK_FILE, fd)) and if the last argument is an fd of directory, we are asking for trouble by violating the locking order - all directories go before all non-directories. If the last argument is an fd of parent directory, it has 50% odds of locking child before parent, which will cause AB-BA deadlock if we race with unlink(). Cc: stable@vger.kernel.org @ 3.13+ Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/ioctl.c21
1 files changed, 5 insertions, 16 deletions
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index 45cb59bcc791..8b7898b7670f 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -86,21 +86,16 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
86 } 86 }
87 87
88 src_inode = file_inode(src_file.file); 88 src_inode = file_inode(src_file.file);
89 rc = -EINVAL;
90 if (S_ISDIR(src_inode->i_mode))
91 goto out_fput;
89 92
90 /* 93 /*
91 * Note: cifs case is easier than btrfs since server responsible for 94 * Note: cifs case is easier than btrfs since server responsible for
92 * checks for proper open modes and file type and if it wants 95 * checks for proper open modes and file type and if it wants
93 * server could even support copy of range where source = target 96 * server could even support copy of range where source = target
94 */ 97 */
95 98 lock_two_nondirectories(target_inode, src_inode);
96 /* so we do not deadlock racing two ioctls on same files */
97 if (target_inode < src_inode) {
98 mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT);
99 mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD);
100 } else {
101 mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT);
102 mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD);
103 }
104 99
105 /* determine range to clone */ 100 /* determine range to clone */
106 rc = -EINVAL; 101 rc = -EINVAL;
@@ -124,13 +119,7 @@ static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file,
124out_unlock: 119out_unlock:
125 /* although unlocking in the reverse order from locking is not 120 /* although unlocking in the reverse order from locking is not
126 strictly necessary here it is a little cleaner to be consistent */ 121 strictly necessary here it is a little cleaner to be consistent */
127 if (target_inode < src_inode) { 122 unlock_two_nondirectories(src_inode, target_inode);
128 mutex_unlock(&src_inode->i_mutex);
129 mutex_unlock(&target_inode->i_mutex);
130 } else {
131 mutex_unlock(&target_inode->i_mutex);
132 mutex_unlock(&src_inode->i_mutex);
133 }
134out_fput: 123out_fput:
135 fdput(src_file); 124 fdput(src_file);
136out_drop_write: 125out_drop_write: