Commit 24f1a316c4bfeed2e1d9a7803545839e2d2e4a0b
Committed by
Greg Kroah-Hartman
1 parent
3436c0aadc
fix deadlock in cifs_ioctl_clone()
commit 378ff1a53b5724f3ac97b0aba3c9ecac072f6fcd upstream. 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(). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Showing 1 changed file with 5 additions and 16 deletions Side-by-side Diff
fs/cifs/ioctl.c
... | ... | @@ -86,22 +86,17 @@ |
86 | 86 | } |
87 | 87 | |
88 | 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 | 94 | * Note: cifs case is easier than btrfs since server responsible for |
92 | 95 | * checks for proper open modes and file type and if it wants |
93 | 96 | * server could even support copy of range where source = target |
94 | 97 | */ |
98 | + lock_two_nondirectories(target_inode, src_inode); | |
95 | 99 | |
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 | - | |
105 | 100 | /* determine range to clone */ |
106 | 101 | rc = -EINVAL; |
107 | 102 | if (off + len > src_inode->i_size || off + len < off) |
... | ... | @@ -124,13 +119,7 @@ |
124 | 119 | out_unlock: |
125 | 120 | /* although unlocking in the reverse order from locking is not |
126 | 121 | strictly necessary here it is a little cleaner to be consistent */ |
127 | - if (target_inode < src_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 | - } | |
122 | + unlock_two_nondirectories(src_inode, target_inode); | |
134 | 123 | out_fput: |
135 | 124 | fdput(src_file); |
136 | 125 | out_drop_write: |