diff options
author | Jeff Layton <jlayton@redhat.com> | 2014-03-10 09:54:15 -0400 |
---|---|---|
committer | Jeff Layton <jlayton@redhat.com> | 2014-03-31 08:24:43 -0400 |
commit | d7a06983a01a33605191c0766857b832ac32a2b6 (patch) | |
tree | d6a0f83579e0d7a20a235ddb568d935fe54b24fb | |
parent | 90478939dce096ed5b239cad16237dca0a59d66f (diff) |
locks: fix locks_mandatory_locked to respect file-private locks
As Trond pointed out, you can currently deadlock yourself by setting a
file-private lock on a file that requires mandatory locking and then
trying to do I/O on it.
Avoid this problem by plumbing some knowledge of file-private locks into
the mandatory locking code. In order to do this, we must pass down
information about the struct file that's being used to
locks_verify_locked.
Reported-by: Trond Myklebust <trond.myklebust@primarydata.com>
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Acked-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | fs/locks.c | 7 | ||||
-rw-r--r-- | fs/namei.c | 2 | ||||
-rw-r--r-- | include/linux/fs.h | 22 | ||||
-rw-r--r-- | mm/mmap.c | 2 | ||||
-rw-r--r-- | mm/nommu.c | 2 |
5 files changed, 18 insertions, 17 deletions
diff --git a/fs/locks.c b/fs/locks.c index 09d6c8c33c81..d82c51c4fcd2 100644 --- a/fs/locks.c +++ b/fs/locks.c | |||
@@ -1155,13 +1155,14 @@ EXPORT_SYMBOL(posix_lock_file_wait); | |||
1155 | 1155 | ||
1156 | /** | 1156 | /** |
1157 | * locks_mandatory_locked - Check for an active lock | 1157 | * locks_mandatory_locked - Check for an active lock |
1158 | * @inode: the file to check | 1158 | * @file: the file to check |
1159 | * | 1159 | * |
1160 | * Searches the inode's list of locks to find any POSIX locks which conflict. | 1160 | * Searches the inode's list of locks to find any POSIX locks which conflict. |
1161 | * This function is called from locks_verify_locked() only. | 1161 | * This function is called from locks_verify_locked() only. |
1162 | */ | 1162 | */ |
1163 | int locks_mandatory_locked(struct inode *inode) | 1163 | int locks_mandatory_locked(struct file *file) |
1164 | { | 1164 | { |
1165 | struct inode *inode = file_inode(file); | ||
1165 | fl_owner_t owner = current->files; | 1166 | fl_owner_t owner = current->files; |
1166 | struct file_lock *fl; | 1167 | struct file_lock *fl; |
1167 | 1168 | ||
@@ -1172,7 +1173,7 @@ int locks_mandatory_locked(struct inode *inode) | |||
1172 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | 1173 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { |
1173 | if (!IS_POSIX(fl)) | 1174 | if (!IS_POSIX(fl)) |
1174 | continue; | 1175 | continue; |
1175 | if (fl->fl_owner != owner) | 1176 | if (fl->fl_owner != owner && fl->fl_owner != (fl_owner_t)file) |
1176 | break; | 1177 | break; |
1177 | } | 1178 | } |
1178 | spin_unlock(&inode->i_lock); | 1179 | spin_unlock(&inode->i_lock); |
diff --git a/fs/namei.c b/fs/namei.c index d580df2e6804..dc51bac037c9 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -2542,7 +2542,7 @@ static int handle_truncate(struct file *filp) | |||
2542 | /* | 2542 | /* |
2543 | * Refuse to truncate files with mandatory locks held on them. | 2543 | * Refuse to truncate files with mandatory locks held on them. |
2544 | */ | 2544 | */ |
2545 | error = locks_verify_locked(inode); | 2545 | error = locks_verify_locked(filp); |
2546 | if (!error) | 2546 | if (!error) |
2547 | error = security_path_truncate(path); | 2547 | error = security_path_truncate(path); |
2548 | if (!error) { | 2548 | if (!error) { |
diff --git a/include/linux/fs.h b/include/linux/fs.h index ae91dce8a547..4aa81e6ae067 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1912,6 +1912,11 @@ extern int current_umask(void); | |||
1912 | extern void ihold(struct inode * inode); | 1912 | extern void ihold(struct inode * inode); |
1913 | extern void iput(struct inode *); | 1913 | extern void iput(struct inode *); |
1914 | 1914 | ||
1915 | static inline struct inode *file_inode(struct file *f) | ||
1916 | { | ||
1917 | return f->f_inode; | ||
1918 | } | ||
1919 | |||
1915 | /* /sys/fs */ | 1920 | /* /sys/fs */ |
1916 | extern struct kobject *fs_kobj; | 1921 | extern struct kobject *fs_kobj; |
1917 | 1922 | ||
@@ -1921,7 +1926,7 @@ extern struct kobject *fs_kobj; | |||
1921 | #define FLOCK_VERIFY_WRITE 2 | 1926 | #define FLOCK_VERIFY_WRITE 2 |
1922 | 1927 | ||
1923 | #ifdef CONFIG_FILE_LOCKING | 1928 | #ifdef CONFIG_FILE_LOCKING |
1924 | extern int locks_mandatory_locked(struct inode *); | 1929 | extern int locks_mandatory_locked(struct file *); |
1925 | extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t); | 1930 | extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t); |
1926 | 1931 | ||
1927 | /* | 1932 | /* |
@@ -1944,10 +1949,10 @@ static inline int mandatory_lock(struct inode *ino) | |||
1944 | return IS_MANDLOCK(ino) && __mandatory_lock(ino); | 1949 | return IS_MANDLOCK(ino) && __mandatory_lock(ino); |
1945 | } | 1950 | } |
1946 | 1951 | ||
1947 | static inline int locks_verify_locked(struct inode *inode) | 1952 | static inline int locks_verify_locked(struct file *file) |
1948 | { | 1953 | { |
1949 | if (mandatory_lock(inode)) | 1954 | if (mandatory_lock(file_inode(file))) |
1950 | return locks_mandatory_locked(inode); | 1955 | return locks_mandatory_locked(file); |
1951 | return 0; | 1956 | return 0; |
1952 | } | 1957 | } |
1953 | 1958 | ||
@@ -2008,7 +2013,7 @@ static inline int break_deleg_wait(struct inode **delegated_inode) | |||
2008 | } | 2013 | } |
2009 | 2014 | ||
2010 | #else /* !CONFIG_FILE_LOCKING */ | 2015 | #else /* !CONFIG_FILE_LOCKING */ |
2011 | static inline int locks_mandatory_locked(struct inode *inode) | 2016 | static inline int locks_mandatory_locked(struct file *file) |
2012 | { | 2017 | { |
2013 | return 0; | 2018 | return 0; |
2014 | } | 2019 | } |
@@ -2030,7 +2035,7 @@ static inline int mandatory_lock(struct inode *inode) | |||
2030 | return 0; | 2035 | return 0; |
2031 | } | 2036 | } |
2032 | 2037 | ||
2033 | static inline int locks_verify_locked(struct inode *inode) | 2038 | static inline int locks_verify_locked(struct file *file) |
2034 | { | 2039 | { |
2035 | return 0; | 2040 | return 0; |
2036 | } | 2041 | } |
@@ -2297,11 +2302,6 @@ static inline bool execute_ok(struct inode *inode) | |||
2297 | return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode); | 2302 | return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode); |
2298 | } | 2303 | } |
2299 | 2304 | ||
2300 | static inline struct inode *file_inode(struct file *f) | ||
2301 | { | ||
2302 | return f->f_inode; | ||
2303 | } | ||
2304 | |||
2305 | static inline void file_start_write(struct file *file) | 2305 | static inline void file_start_write(struct file *file) |
2306 | { | 2306 | { |
2307 | if (!S_ISREG(file_inode(file)->i_mode)) | 2307 | if (!S_ISREG(file_inode(file)->i_mode)) |
@@ -1299,7 +1299,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr, | |||
1299 | /* | 1299 | /* |
1300 | * Make sure there are no mandatory locks on the file. | 1300 | * Make sure there are no mandatory locks on the file. |
1301 | */ | 1301 | */ |
1302 | if (locks_verify_locked(inode)) | 1302 | if (locks_verify_locked(file)) |
1303 | return -EAGAIN; | 1303 | return -EAGAIN; |
1304 | 1304 | ||
1305 | vm_flags |= VM_SHARED | VM_MAYSHARE; | 1305 | vm_flags |= VM_SHARED | VM_MAYSHARE; |
diff --git a/mm/nommu.c b/mm/nommu.c index 8740213b1647..a554e5a451cd 100644 --- a/mm/nommu.c +++ b/mm/nommu.c | |||
@@ -995,7 +995,7 @@ static int validate_mmap_request(struct file *file, | |||
995 | (file->f_mode & FMODE_WRITE)) | 995 | (file->f_mode & FMODE_WRITE)) |
996 | return -EACCES; | 996 | return -EACCES; |
997 | 997 | ||
998 | if (locks_verify_locked(file_inode(file))) | 998 | if (locks_verify_locked(file)) |
999 | return -EAGAIN; | 999 | return -EAGAIN; |
1000 | 1000 | ||
1001 | if (!(capabilities & BDI_CAP_MAP_DIRECT)) | 1001 | if (!(capabilities & BDI_CAP_MAP_DIRECT)) |