A directory whose $INDEX_ROOT has a child node but whose $INDEX_ALLOCATION is missing or damaged gets an empty index inode from ntfs_read_locked_index_inode(): size 0 and not non-resident. ntfs_lookup_inode_by_name() still follows the child node and reads a folio from that inode while it holds the mft record lock of the directory. Since the resident iomap read started to take base_ni->mrec_lock, that read waits for a lock the caller already holds: INFO: task init:1 is blocked on a mutex likely owned by task init:1. __mutex_lock __ntfs_read_iomap_begin ntfs_read_iomap_next iomap_read_folio ntfs_read_folio ntfs_lookup_inode_by_name ntfs_lookup The task never returns, and neither does anything else that needs the directory's mft record. It takes one corrupted byte on an image from mkntfs: change the first byte of the type of the $INDEX_ALLOCATION attribute header of a directory with index blocks from 0xa0 to 0x00. The first lookup in that directory hangs. Check that the index inode is non-resident and that the vcn lies inside it before every index block read, and fail with -EIO otherwise. This also rejects a negative vcn, which was only checked on later descents. Fixes: d9e00c457d4a ("ntfs: serialize resident iomap reads with mrec_lock") Cc: stable@vger.kernel.org # v7.3+ Signed-off-by: Wahid Khan --- fs/ntfs/dir.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/fs/ntfs/dir.c b/fs/ntfs/dir.c index fce10baa2..9fb5bb3d3 100644 --- a/fs/ntfs/dir.c +++ b/fs/ntfs/dir.c @@ -308,6 +308,20 @@ u64 ntfs_lookup_inode_by_name(struct ntfs_inode *dir_ni, const __le16 *uname, ia_mapping = ia_vi->i_mapping; descend_into_child_node: + /* + * The child node has to be inside $INDEX_ALLOCATION. A directory that + * lacks the attribute gets an empty index inode that is not + * non-resident, and reading a folio from it locks the mft record that + * is already locked here. + */ + if (!NInoNonResident(NTFS_I(ia_vi)) || vcn < 0 || + (u64)vcn >= (u64)i_size_read(ia_vi) >> dir_ni->itype.index.vcn_size_bits) { + ntfs_error(sb, + "Child node vcn %lld is outside $INDEX_ALLOCATION in directory inode 0x%llx.", + vcn, dir_ni->mft_no); + err = -EIO; + goto err_out; + } /* * Convert vcn to index into the index allocation attribute in units * of PAGE_SIZE and map the page cache page, reading it from -- 2.53.0