staging: android: ashmem: Fix lockdep issue during llseek
authorJoel Fernandes <joelaf@google.com>
Fri, 16 Feb 2018 19:02:01 +0000 (11:02 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 19 Mar 2018 07:42:45 +0000 (08:42 +0100)
commit cb57469c9573f6018cd1302953dd45d6e05aba7b upstream.

ashmem_mutex create a chain of dependencies like so:

(1)
mmap syscall ->
  mmap_sem ->  (acquired)
  ashmem_mmap
  ashmem_mutex (try to acquire)
  (block)

(2)
llseek syscall ->
  ashmem_llseek ->
  ashmem_mutex ->  (acquired)
  inode_lock ->
  inode->i_rwsem (try to acquire)
  (block)

(3)
getdents ->
  iterate_dir ->
  inode_lock ->
  inode->i_rwsem   (acquired)
  copy_to_user ->
  mmap_sem         (try to acquire)

There is a lock ordering created between mmap_sem and inode->i_rwsem
causing a lockdep splat [2] during a syzcaller test, this patch fixes
the issue by unlocking the mutex earlier. Functionally that's Ok since
we don't need to protect vfs_llseek.

[1] https://patchwork.kernel.org/patch/10185031/
[2] https://lkml.org/lkml/2018/1/10/48

Acked-by: Todd Kjos <tkjos@google.com>
Cc: Arve Hjonnevag <arve@android.com>
Cc: stable@vger.kernel.org
Reported-by: syzbot+8ec30bb7bf1a981a2012@syzkaller.appspotmail.com
Signed-off-by: Joel Fernandes <joelaf@google.com>
Acked-by: Greg Hackmann <ghackmann@google.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/android/ashmem.c

index e7541dc..d9941b0 100644 (file)
@@ -334,24 +334,23 @@ static loff_t ashmem_llseek(struct file *file, loff_t offset, int origin)
        mutex_lock(&ashmem_mutex);
 
        if (asma->size == 0) {
-               ret = -EINVAL;
-               goto out;
+               mutex_unlock(&ashmem_mutex);
+               return -EINVAL;
        }
 
        if (!asma->file) {
-               ret = -EBADF;
-               goto out;
+               mutex_unlock(&ashmem_mutex);
+               return -EBADF;
        }
 
+       mutex_unlock(&ashmem_mutex);
+
        ret = vfs_llseek(asma->file, offset, origin);
        if (ret < 0)
-               goto out;
+               return ret;
 
        /** Copy f_pos from backing file, since f_ops->llseek() sets it */
        file->f_pos = asma->file->f_pos;
-
-out:
-       mutex_unlock(&ashmem_mutex);
        return ret;
 }