[PATCH v2 2/3] ntfs: open the index allocation before reading or writing an index block

From: Wahid Khan

Date: Fri Nov 06 2026 - 07:54:21 EST


ntfs_ib_write() and ntfs_ib_read() use icx->ia_ni without checking it.
ntfs_ir_reparent() only opens the index allocation through
ntfs_ia_add() when the root is a small index. For a root that is already
marked as a large index it calls ntfs_ib_write() directly.

The lookup does not open the index allocation either when it ends in the
root. If the last entry of the root has no child node, ia_ni is still
NULL when the root has to be moved into an index block, and the write
dereferences it:

Oops: general protection fault, KASAN: null-ptr-deref in range
[0x0000000000000128-0x000000000000012f]
RIP: 0010:ntfs_inode_attr_pwrite+0x52/0x1600
ntfs_ir_make_space
ntfs_ie_add
ntfs_index_add_filename
__ntfs_link
ntfs_link
vfs_link

A single byte of an image from mkntfs is enough. In a directory that has
one empty index block, clear INDEX_ENTRY_NODE (flags 3 -> 2) of the last
entry of the $INDEX_ROOT and link() a file into the directory.

Commit 9365c4a1357e ("ntfs: fix NULL dereference on index allocation open failure")
covers the case where ntfs_ia_open() fails in the lookup. Here it was
never tried. Open the index allocation on demand in both
helpers and fail with -EIO if that does not work.

ntfs_ia_open() also succeeds for a directory that has no
$INDEX_ALLOCATION attribute. The inode it returns is then an empty resident
stub that does not describe an index allocation, so reject that case too.

Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations")
Cc: stable@xxxxxxxxxxxxxxx # v7.1+
Signed-off-by: Wahid Khan <wahidzk0091@xxxxxxxxx>
---
fs/ntfs/index.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/fs/ntfs/index.c b/fs/ntfs/index.c
index 322cb371d..74716f916 100644
--- a/fs/ntfs/index.c
+++ b/fs/ntfs/index.c
@@ -99,12 +99,30 @@ static s64 ntfs_ib_pos_to_vcn(struct ntfs_index_context *icx, s64 pos)
return pos >> icx->vcn_size_bits;
}

+/* Open the index allocation inode if no earlier step of the operation did. */
+static int ntfs_icx_ia_ensure(struct ntfs_index_context *icx)
+{
+ if (!icx->ia_ni) {
+ icx->ia_ni = ntfs_ia_open(icx, icx->idx_ni);
+ if (!icx->ia_ni)
+ return -EIO;
+ }
+ /* Without $INDEX_ALLOCATION the index inode is an empty resident stub. */
+ if (!NInoNonResident(icx->ia_ni))
+ return -EIO;
+ return 0;
+}
+
static int ntfs_ib_write(struct ntfs_index_context *icx, struct index_block *ib)
{
s64 ret, vcn = le64_to_cpu(ib->index_block_vcn);

ntfs_debug("vcn: %lld\n", vcn);

+ ret = ntfs_icx_ia_ensure(icx);
+ if (ret)
+ return ret;
+
ret = pre_write_mst_fixup((struct ntfs_record *)ib, icx->block_size);
if (ret)
return -EIO;
@@ -805,6 +823,10 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl

ntfs_debug("vcn: %lld\n", vcn);

+ ret = ntfs_icx_ia_ensure(icx);
+ if (ret)
+ return ret;
+
pos = ntfs_ib_vcn_to_pos(icx, vcn);

ret = ntfs_inode_attr_pread(VFS_I(icx->ia_ni), pos, icx->block_size, (u8 *)dst);
--
2.53.0