A circular locking dependency (AB-BA deadlock) was detected between the
ORPHAN_DIR_SYSTEM_INODE and EXTENT_ALLOC_SYSTEM_INODE locks in OCFS2.
The first path, ocfs2_wipe_inode(), acquires the lock for
ORPHAN_DIR_SYSTEM_INODE to prevent concurrent recovery from truncating
the same file. While holding this lock, it calls ocfs2_xattr_remove() to
free extended attributes, which in turn calls ocfs2_xattr_free_block().
To free the extent, ocfs2_xattr_free_block() acquires the lock for
EXTENT_ALLOC_SYSTEM_INODE. This establishes the lock order:
ORPHAN_DIR_SYSTEM_INODE -> EXTENT_ALLOC_SYSTEM_INODE.
The second path, ocfs2_dio_end_io_write(), is called when a direct IO
write finishes to mark unwritten extents as written and update the inode
size. It calls ocfs2_lock_allocators(), which acquires the lock for
EXTENT_ALLOC_SYSTEM_INODE and stores it in meta_ac. After performing the
necessary transactions and committing them, if the file was orphaned, it
calls ocfs2_del_inode_from_orphan() to remove the file from the orphan
directory. This function acquires the lock for ORPHAN_DIR_SYSTEM_INODE.
However, meta_ac (and thus the EXTENT_ALLOC_SYSTEM_INODE lock) is not
freed until the very end of ocfs2_dio_end_io_write(), after
ocfs2_del_inode_from_orphan() returns. This establishes the reverse lock
order: EXTENT_ALLOC_SYSTEM_INODE -> ORPHAN_DIR_SYSTEM_INODE, leading to
a potential deadlock.
To fix this, release the allocator locks (data_ac and meta_ac) before
calling ocfs2_del_inode_from_orphan() in ocfs2_dio_end_io_write(). The
allocator locks are only needed for the transaction that marks extents
as written and updates the inode size. Once the transaction is
committed, data_ac and meta_ac are no longer needed. By calling
ocfs2_free_alloc_context() on data_ac and meta_ac right after the
transaction is committed and before ocfs2_del_inode_from_orphan(), we
release EXTENT_ALLOC_SYSTEM_INODE before acquiring
ORPHAN_DIR_SYSTEM_INODE, breaking the circular dependency and preventing
the deadlock.
Fixes: d647c5b2fbf81560818dacade360abc8c00a9665 ("ocfs2: split transactions in dio completion to avoid credit exhaustion")
Assisted-by: Gemini:gemini-3.1-pro-preview
Reported-by:
syzbot+7df4af...@syzkaller.appspotmail.com
Link:
https://syzkaller.appspot.com/bug?extid=7df4afa14aeda476a7a1
Link:
https://syzkaller.appspot.com/ai_job?id=4acc303c-bf15-4295-b70b-d13d7ef6a323
To: <
linux-...@vger.kernel.org>
Cc: <
jl...@evilplan.org>
Cc: <
jose...@linux.alibaba.com>
Cc: <
ma...@fasheh.com>
Cc: <
ocfs2...@lists.linux.dev>
---
diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c
index 6ec198bda..74b41709b 100644
--- a/fs/ocfs2/aops.c
+++ b/fs/ocfs2/aops.c
@@ -2372,6 +2372,15 @@ static int ocfs2_dio_end_io_write(struct inode *inode,
unlock:
up_write(&oi->ip_alloc_sem);
+ if (data_ac) {
+ ocfs2_free_alloc_context(data_ac);
+ data_ac = NULL;
+ }
+ if (meta_ac) {
+ ocfs2_free_alloc_context(meta_ac);
+ meta_ac = NULL;
+ }
+
/* everything looks good, let's start the cleanup */
if (!ret && dwc->dw_orphaned) {
BUG_ON(dwc->dw_writer_pid != task_pid_nr(current));
base-commit: 5d6919055dec134de3c40167a490f33c74c12581
--
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to send it to the mailing list.
Reply with '#syz reject' to reject it.
See for more information.