diff --git a/fs/ntfs/attrib.c b/fs/ntfs/attrib.c index 5f5a91265af5..2e72cd816d04 100644 --- a/fs/ntfs/attrib.c +++ b/fs/ntfs/attrib.c @@ -103,6 +103,17 @@ int ntfs_map_runlist_nolock(struct ntfs_inode *ni, s64 vcn, struct ntfs_attr_sea base_ni = ni; else base_ni = ni->ext.base_ntfs_ino; + /* + * ntfs_read_inode_mount() builds $MFT's runlist itself, so nothing + * should reach here for $MFT. A crafted image can: the read that + * gets here already holds the $MFT folio lock it would wait on. + */ + if (unlikely(NVolMftBootstrap(ni->vol) && + base_ni == NTFS_I(ni->vol->mft_ino))) { + ntfs_error(ni->vol->sb, + "$MFT needs its own extent records to describe itself; cannot mount."); + return -EIO; + } if (!ctx) { ctx_is_temporary = ctx_needs_reset = true; m = map_mft_record(base_ni); diff --git a/fs/ntfs/inode.c b/fs/ntfs/inode.c index de848180749d..6356026f732d 100644 --- a/fs/ntfs/inode.c +++ b/fs/ntfs/inode.c @@ -2087,6 +2087,11 @@ int ntfs_read_inode_mount(struct inode *vi) /* Now load all attribute extents. */ a = NULL; next_vcn = last_vcn = highest_vcn = 0; + /* + * Reading one of $MFT's own extent records in this loop can re-enter + * ntfs_map_runlist_nolock() for $MFT; see the check there. + */ + NVolSetMftBootstrap(vol); while (!(err = ntfs_attr_lookup(AT_DATA, NULL, 0, 0, next_vcn, NULL, 0, ctx))) { struct runlist_element *nrl; @@ -2169,6 +2174,7 @@ int ntfs_read_inode_mount(struct inode *vi) err = ntfs_read_locked_inode(vi); if (err) { ntfs_error(sb, "ntfs_read_inode() of $MFT failed.\n"); + NVolClearMftBootstrap(vol); ntfs_attr_put_search_ctx(ctx); /* Revert to the safe super operations. */ kfree(m); @@ -2202,6 +2208,7 @@ int ntfs_read_inode_mount(struct inode *vi) goto put_err_out; } } + NVolClearMftBootstrap(vol); if (err != -ENOENT) { ntfs_error(sb, "Failed to lookup $MFT/$DATA attribute extent. Run chkdsk.\n"); goto put_err_out; @@ -2236,6 +2243,8 @@ int ntfs_read_inode_mount(struct inode *vi) put_err_out: ntfs_attr_put_search_ctx(ctx); err_out: + /* Also reached from inside the $DATA loop. */ + NVolClearMftBootstrap(vol); ntfs_error(sb, "Failed. Marking inode as bad."); kfree(m); return -1; diff --git a/fs/ntfs/volume.h b/fs/ntfs/volume.h index fdb57279de84..0473b602084c 100644 --- a/fs/ntfs/volume.h +++ b/fs/ntfs/volume.h @@ -194,6 +194,7 @@ struct ntfs_volume { * NV_Discard Issue discard/TRIM commands for freed clusters. * NV_DisableSparse Disable creation of sparse regions. * NV_NativeSymlinkRel Translate absolute Windows reparse targets (native_symlink=rel). + * NV_MftBootstrap Mount is still assembling $MFT's own runlist. */ enum { NV_Errors, @@ -214,6 +215,7 @@ enum { NV_DisableSparse, NV_NativeSymlinkRel, NV_SymlinkNative, + NV_MftBootstrap, }; /* @@ -253,6 +255,7 @@ DEFINE_NVOL_BIT_OPS(Discard) DEFINE_NVOL_BIT_OPS(DisableSparse) DEFINE_NVOL_BIT_OPS(NativeSymlinkRel) DEFINE_NVOL_BIT_OPS(SymlinkNative) +DEFINE_NVOL_BIT_OPS(MftBootstrap) static inline void ntfs_inc_free_clusters(struct ntfs_volume *vol, s64 nr) {