[BUG] ntfs: mrec_lock ABBA deadlock between writeback/fsync and rename

7 views
Skip to first unread message

Peiyang He

unread,
12:25 AM (19 hours ago) 12:25 AM
to linki...@kernel.org, hyc...@gmail.com, syzk...@googlegroups.com, linux-...@vger.kernel.org, linux-...@vger.kernel.org
Hello Linux kernel developers and maintainers,

We found two task hungs in the new NTFS when fuzzing with Syzkaller.
After our digging, we believe both hangs are caused by the SAME AB-BA type DEADLOCK issue,
so we report them together here.

Title of task hung #1: INFO: task hung in __ntfs_write_inode
Title of task hung #2: INFO: task hung in ntfs_inode_sync_filename

Kernel version: commit 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 (tag v7.1).
And we believe the bug is also possible in the current mainline.
Note that the code line numbers in the following analysis are also based on this kernel version.

Relevant kernel config: (the complete config is included in the attachments)

CONFIG_NTFS_FS=y
CONFIG_DETECT_HUNG_TASK=y
CONFIG_DETECT_HUNG_TASK_BLOCKER=y
CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=140
CONFIG_PROVE_LOCKING=y
CONFIG_LOCKDEP=y
CONFIG_DEBUG_LOCK_ALLOC=y
CONFIG_DEBUG_MUTEXES=y

The original Syzkaller reports are included in the attachements since they are too long.

===========================
Root cause for task hung #1
===========================

A = the EXSITING target file inode of rename(), i.e. new_inode/new_ni.
B = A's parent directory inode
C = the source file inode of rename(), i.e. old_inode/old_ni.

Race timeline:

CPU0 CPU1
==== ====

fdatasync(A)
--> ntfs_file_fsync()
fs/ntfs/file.c:156
--> mutex_lock_nested(&A->mrec_lock,
NTFS_INODE_MUTEX_NORMAL_CHILD)
fs/ntfs/file.c:183

rename(C in dir B to A) /* "B/C" would be "B/A" */
--> ntfs_rename()
fs/ntfs/namei.c:1252
--> old_inode = C
fs/ntfs/namei.c:1299
--> new_inode = A
fs/ntfs/namei.c:1300
--> old_ni = NTFS_I(old_inode) /* Get C here */
fs/ntfs/namei.c:1301
--> mutex_lock_nested(&C->mrec_lock,
NTFS_INODE_MUTEX_NORMAL)
fs/ntfs/namei.c:1306
--> mutex_lock_nested(&B->mrec_lock,
NTFS_INODE_MUTEX_PARENT)
fs/ntfs/namei.c:1307
--> parent_vi = ntfs_iget() /* Get B here */
fs/ntfs/file.c:189
--> mutex_lock_nested(&B->mrec_lock,
NTFS_INODE_MUTEX_NORMAL)
fs/ntfs/file.c:192
--> blocks because CPU1 owns B->mrec_lock

--> new_ni = NTFS_I(new_inode) /* Get A here */
/* A MUST be existing since this line is guarded by if (new_inode) */
fs/ntfs/namei.c:1317
--> mutex_lock_nested(&A->mrec_lock,
NTFS_INODE_MUTEX_NORMAL_2)
fs/ntfs/namei.c:1318
--> blocks because CPU0 owns A->mrec_lock

DEADLOCK:
CPU0 owns A->mrec_lock and waits for B->mrec_lock.
CPU1 owns B->mrec_lock and waits for A->mrec_lock.


===========================
Root cause for task hung #2
===========================

A = the EXSITING target file inode of rename(), i.e. new_inode/new_ni, and the inode being written back.
B = A's parent directory inode
C = the source file inode of rename(), i.e. old_inode/old_ni.

Race timeline:

CPU0 CPU1
==== ====

writeback(A)
--> __ntfs_write_inode()
fs/ntfs/inode.c:2704
--> ni = NTFS_I(vi) /* Get A here */
fs/ntfs/inode.c:2706
--> mutex_lock_nested(&A->mrec_lock, NTFS_INODE_MUTEX_NORMAL)
fs/ntfs/inode.c:2733

rename(C in dir B to A) /* "B/C" would be "B/A" */
--> ntfs_rename()
fs/ntfs/namei.c:1252
--> old_inode = C
fs/ntfs/namei.c:1299
--> new_inode = A
fs/ntfs/namei.c:1300
--> old_ni = NTFS_I(old_inode) /* Get C here */
fs/ntfs/namei.c:1301
--> mutex_lock_nested(&C->mrec_lock,
NTFS_INODE_MUTEX_NORMAL)
fs/ntfs/namei.c:1306
--> mutex_lock_nested(&B->mrec_lock,
NTFS_INODE_MUTEX_PARENT)
fs/ntfs/namei.c:1307

--> NInoTestClearFileNameDirty(A)
fs/ntfs/inode.c:2762
--> ntfs_inode_sync_filename(A)
fs/ntfs/inode.c:2535
--> ntfs_attr_lookup(AT_FILE_NAME, ...)
fs/ntfs/inode.c:2568
--> index_vi = ntfs_iget() /* Get B here */
fs/ntfs/inode.c:2574
--> index_ni = NTFS_I(index_vi) /* Get B here */
fs/ntfs/inode.c:2581
--> mutex_lock_nested(&B->mrec_lock,
NTFS_INODE_MUTEX_PARENT)
fs/ntfs/inode.c:2583
--> blocks because CPU1 owns B->mrec_lock

--> new_ni = NTFS_I(new_inode) /* Get A here */
/* A MUST be existing since this line is guarded by if (new_inode) */
fs/ntfs/namei.c:1317
--> mutex_lock_nested(&A->mrec_lock,
NTFS_INODE_MUTEX_NORMAL_2)
fs/ntfs/namei.c:1318
--> blocks because CPU0 owns A->mrec_lock

DEADLOCK:
CPU0 owns A->mrec_lock and waits for B->mrec_lock.
CPU1 owns B->mrec_lock and waits for A->mrec_lock.

====
Note
====
LLM assistance was used when analyzing the root cause and writing this report.
But we have made our best effort to ensure the description and analysis in this report are accurate and correct.

Thanks,
Peiyang
task_hung_#1_report
task_hung_#2_report
.config

Namjae Jeon

unread,
1:18 AM (18 hours ago) 1:18 AM
to Peiyang He, hyc...@gmail.com, syzk...@googlegroups.com, linux-...@vger.kernel.org, linux-...@vger.kernel.org
On Mon, Jun 29, 2026 at 1:25 PM Peiyang He <peiya...@smail.nju.edu.cn> wrote:
>
> Hello Linux kernel developers and maintainers,
Hello Peiyang,

>
> We found two task hungs in the new NTFS when fuzzing with Syzkaller.
> After our digging, we believe both hangs are caused by the SAME AB-BA type DEADLOCK issue,
> so we report them together here.
>
> Title of task hung #1: INFO: task hung in __ntfs_write_inode
> Title of task hung #2: INFO: task hung in ntfs_inode_sync_filename
>
> Kernel version: commit 8cd9520d35a6c38db6567e97dd93b1f11f185dc6 (tag v7.1).
> And we believe the bug is also possible in the current mainline.
> Note that the code line numbers in the following analysis are also based on this kernel version.
>
> Relevant kernel config: (the complete config is included in the attachments)
Do you have a suggested patch you can get credit for?
Thanks.

Peiyang He

unread,
5:40 AM (14 hours ago) 5:40 AM
to Namjae Jeon, hyc...@gmail.com, syzk...@googlegroups.com, linux-...@vger.kernel.org, linux-...@vger.kernel.org
Hi Namjae,
Thanks for your reply. Yes, I'm working on the patch, and hopefully I can get it done very soon.

> Thanks.
>


Reply all
Reply to author
Forward
0 new messages