[PATCH] ntfs3: serialize readdir against MFT record updates

Yousef Alhouseen <[email protected]> Sun, 28 Jun 2026 02:19:31 +0200
Newsgroups dev.linux.lists.ntfs3,org.kernel.vger.linux-kernel,org.kernel.vger.stable
Message-ID <[email protected]>
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: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=33e764e33338f7b46952
Cc: [email protected]
Signed-off-by: Yousef Alhouseen <[email protected]>
---
 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