locks: fix locks_mandatory_locked to respect file-private locks
authorJeff Layton <jlayton@redhat.com>
Mon, 10 Mar 2014 13:54:15 +0000 (09:54 -0400)
committerJeff Layton <jlayton@redhat.com>
Mon, 31 Mar 2014 12:24:43 +0000 (08:24 -0400)
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>
fs/locks.c
fs/namei.c
include/linux/fs.h
mm/mmap.c
mm/nommu.c

index 09d6c8c33c815d168b5bbaf5a36619a21ccf322e..d82c51c4fcd22d0a44b78f3274676c25a8bbbdff 100644 (file)
@@ -1155,13 +1155,14 @@ EXPORT_SYMBOL(posix_lock_file_wait);
 
 /**
  * locks_mandatory_locked - Check for an active lock
- * @inode: the file to check
+ * @file: the file to check
  *
  * Searches the inode's list of locks to find any POSIX locks which conflict.
  * This function is called from locks_verify_locked() only.
  */
-int locks_mandatory_locked(struct inode *inode)
+int locks_mandatory_locked(struct file *file)
 {
+       struct inode *inode = file_inode(file);
        fl_owner_t owner = current->files;
        struct file_lock *fl;
 
@@ -1172,7 +1173,7 @@ int locks_mandatory_locked(struct inode *inode)
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!IS_POSIX(fl))
                        continue;
-               if (fl->fl_owner != owner)
+               if (fl->fl_owner != owner && fl->fl_owner != (fl_owner_t)file)
                        break;
        }
        spin_unlock(&inode->i_lock);
index d580df2e6804d0863d555234388a34397387c3e6..dc51bac037c9a0f3338729a26663e48e9fdb8ce7 100644 (file)
@@ -2542,7 +2542,7 @@ static int handle_truncate(struct file *filp)
        /*
         * Refuse to truncate files with mandatory locks held on them.
         */
-       error = locks_verify_locked(inode);
+       error = locks_verify_locked(filp);
        if (!error)
                error = security_path_truncate(path);
        if (!error) {
index ae91dce8a54762bc7987ecf54329e98250d24e44..4aa81e6ae067dde1bf950c301c5668a10eb3cc6d 100644 (file)
@@ -1912,6 +1912,11 @@ extern int current_umask(void);
 extern void ihold(struct inode * inode);
 extern void iput(struct inode *);
 
+static inline struct inode *file_inode(struct file *f)
+{
+       return f->f_inode;
+}
+
 /* /sys/fs */
 extern struct kobject *fs_kobj;
 
@@ -1921,7 +1926,7 @@ extern struct kobject *fs_kobj;
 #define FLOCK_VERIFY_WRITE 2
 
 #ifdef CONFIG_FILE_LOCKING
-extern int locks_mandatory_locked(struct inode *);
+extern int locks_mandatory_locked(struct file *);
 extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
 
 /*
@@ -1944,10 +1949,10 @@ static inline int mandatory_lock(struct inode *ino)
        return IS_MANDLOCK(ino) && __mandatory_lock(ino);
 }
 
-static inline int locks_verify_locked(struct inode *inode)
+static inline int locks_verify_locked(struct file *file)
 {
-       if (mandatory_lock(inode))
-               return locks_mandatory_locked(inode);
+       if (mandatory_lock(file_inode(file)))
+               return locks_mandatory_locked(file);
        return 0;
 }
 
@@ -2008,7 +2013,7 @@ static inline int break_deleg_wait(struct inode **delegated_inode)
 }
 
 #else /* !CONFIG_FILE_LOCKING */
-static inline int locks_mandatory_locked(struct inode *inode)
+static inline int locks_mandatory_locked(struct file *file)
 {
        return 0;
 }
@@ -2030,7 +2035,7 @@ static inline int mandatory_lock(struct inode *inode)
        return 0;
 }
 
-static inline int locks_verify_locked(struct inode *inode)
+static inline int locks_verify_locked(struct file *file)
 {
        return 0;
 }
@@ -2297,11 +2302,6 @@ static inline bool execute_ok(struct inode *inode)
        return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
 }
 
-static inline struct inode *file_inode(struct file *f)
-{
-       return f->f_inode;
-}
-
 static inline void file_start_write(struct file *file)
 {
        if (!S_ISREG(file_inode(file)->i_mode))
index 20ff0c33274c27cbb23a78335f484765dc9f9e27..5932ce961218fefc82f1442b1831baa0e20f714f 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1299,7 +1299,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
                        /*
                         * Make sure there are no mandatory locks on the file.
                         */
-                       if (locks_verify_locked(inode))
+                       if (locks_verify_locked(file))
                                return -EAGAIN;
 
                        vm_flags |= VM_SHARED | VM_MAYSHARE;
index 8740213b1647019c9fdbaaf355da3f2e5e632d99..a554e5a451cdb4622d32ce1a5b6cacf1773f848f 100644 (file)
@@ -995,7 +995,7 @@ static int validate_mmap_request(struct file *file,
                            (file->f_mode & FMODE_WRITE))
                                return -EACCES;
 
-                       if (locks_verify_locked(file_inode(file)))
+                       if (locks_verify_locked(file))
                                return -EAGAIN;
 
                        if (!(capabilities & BDI_CAP_MAP_DIRECT))