[PATCH] ntfs3: serialize readdir against MFT record updates

From: Yousef Alhouseen

Date: Sat Jun 27 2026 - 20:20:02 EST


ntfs_readdir() walks the directory index root and resident bitmap directly
from the inode MFT record without holding ni_lock. Writeback takes ni_lock
but may remove the attribute list and replace or free that record while the
directory walk is using pointers into it.

In particular, ntfs_dir_emit() can pass an MFT_REF from the resident index
to iget5_locked(), which may sleep before its set callback dereferences the
reference. This leaves a use-after-free window against writeback.

Hold ni_lock across the directory walk so resident record pointers remain
stable. The index run semaphore is already designed to nest below ni_lock.

Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation")
Reported-by: syzbot+33e764e33338f7b46952@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=33e764e33338f7b46952
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yousef Alhouseen <alhouseenyousef@xxxxxxxxx>
---
fs/ntfs3/dir.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/ntfs3/dir.c b/fs/ntfs3/dir.c
index d99ab086ef6f..a6861f1b1b99 100644
--- a/fs/ntfs3/dir.c
+++ b/fs/ntfs3/dir.c
@@ -472,19 +472,18 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
if (!name)
return -ENOMEM;

+ ni_lock(ni);
+
if (!ni->mi_loaded && ni->attr_list.size) {
/*
- * Directory inode is locked for read.
* Load all subrecords to avoid 'write' access to 'ni' during
* directory reading.
*/
- ni_lock(ni);
if (!ni->mi_loaded && ni->attr_list.size) {
err = ni_load_all_mi(ni);
if (!err)
ni->mi_loaded = true;
}
- ni_unlock(ni);
if (err)
goto out;
}
@@ -543,6 +542,7 @@ static int ntfs_readdir(struct file *file, struct dir_context *ctx)
}

out:
+ ni_unlock(ni);
kfree(name);
put_indx_node(node);

--
2.54.0