ocfs2: fix deadlock in ocfs2_get_system_file_inode
authorMohammed Anees <pvmohammedanees2003@gmail.com>
Tue, 24 Sep 2024 09:32:57 +0000 (09:32 +0000)
committerAndrew Morton <akpm@linux-foundation.org>
Thu, 26 Sep 2024 21:01:44 +0000 (14:01 -0700)
syzbot has found a possible deadlock in ocfs2_get_system_file_inode [1].

The scenario is depicted here,

CPU0 CPU1
lock(&ocfs2_file_ip_alloc_sem_key);
                               lock(&osb->system_file_mutex);
                               lock(&ocfs2_file_ip_alloc_sem_key);
lock(&osb->system_file_mutex);

The function calls which could lead to this are:

CPU0
ocfs2_mknod - lock(&ocfs2_file_ip_alloc_sem_key);
.
.
.
ocfs2_get_system_file_inode - lock(&osb->system_file_mutex);

CPU1 -
ocfs2_fill_super - lock(&osb->system_file_mutex);
.
.
.
ocfs2_read_virt_blocks - lock(&ocfs2_file_ip_alloc_sem_key);

This issue can be resolved by making the down_read -> down_read_try
in the ocfs2_read_virt_blocks.

[1] https://syzkaller.appspot.com/bug?extid=e0055ea09f1f5e6fabdd

Link: https://lkml.kernel.org/r/20240924093257.7181-1-pvmohammedanees2003@gmail.com
Signed-off-by: Mohammed Anees <pvmohammedanees2003@gmail.com>
Reviewed-by: Joseph Qi <joseph.qi@linux.alibaba.com>
Reported-by: <syzbot+e0055ea09f1f5e6fabdd@syzkaller.appspotmail.com>
Closes: https://syzkaller.appspot.com/bug?extid=e0055ea09f1f5e6fabdd
Tested-by: syzbot+e0055ea09f1f5e6fabdd@syzkaller.appspotmail.com
Cc: Mark Fasheh <mark@fasheh.com>
Cc: Joel Becker <jlbec@evilplan.org>
Cc: Junxiao Bi <junxiao.bi@oracle.com>
Cc: Changwei Ge <gechangwei@live.cn>
Cc: Gang He <ghe@suse.com>
Cc: Jun Piao <piaojun@huawei.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
fs/ocfs2/extent_map.c

index 70a768b623cf40f29c8896752ac62065d522e03d..f7672472fa8279bc52f963df0dfa124e749336eb 100644 (file)
@@ -973,7 +973,13 @@ int ocfs2_read_virt_blocks(struct inode *inode, u64 v_block, int nr,
        }
 
        while (done < nr) {
-               down_read(&OCFS2_I(inode)->ip_alloc_sem);
+               if (!down_read_trylock(&OCFS2_I(inode)->ip_alloc_sem)) {
+                       rc = -EAGAIN;
+                       mlog(ML_ERROR,
+                                "Inode #%llu ip_alloc_sem is temporarily unavailable\n",
+                                (unsigned long long)OCFS2_I(inode)->ip_blkno);
+                       break;
+               }
                rc = ocfs2_extent_map_get_blocks(inode, v_block + done,
                                                 &p_block, &p_count, NULL);
                up_read(&OCFS2_I(inode)->ip_alloc_sem);