summaryrefslogtreecommitdiff
path: root/fs/ntfs
AgeCommit message (Collapse)Author
40 hoursMerge tag 'ntfs-for-7.2-rc3' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs Pull ntfs fixes from Namjae Jeon: - fix stale runlist element dereferences in MFT writeback and fallocate - fix mrec_lock ABBA deadlock in rename - prevent userspace modification of NTFS system files - avoid inode eviction/writeback self-deadlocks - reject malformed resident attributes in non-resident runlist mapping - avoid post_write_mst_fixup() on invalid index blocks - fix a hole runlist leak in insert-range error handling - sanitize directory lookup MFT references from disk - fail attribute-list updates after SB_ACTIVE is cleared during teardown * tag 'ntfs-for-7.2-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs: ntfs: fail attrlist updates when the superblock is inactive ntfs: sanitize MFT references returned from ntfs_lookup_inode_by_name() ntfs: fix hole runlist memory leak in insert range error path ntfs: avoid calling post_write_mst_fixup() for invalid index_block ntfs: fix WARN_ON for resident attribute in ntfs_map_runlist_nolock() ntfs: avoid self-deadlock during inode eviction ntfs: make system files immutable to prevent corruption ntfs: fix mrec_lock ABBA deadlock in rename ntfs: avoid stale runlist element dereference in fallocate ntfs: avoid stale runlist element dereference in MFT writeback
6 daysntfs: fail attrlist updates when the superblock is inactivePeiyang He
generic_shutdown_super() clears SB_ACTIVE before evicting cached inodes. If eviction selects the fake inode for a base inode's unnamed $ATTRIBUTE_LIST attribute, ntfs_evict_big_inode() drops the fake inode's reference on the base inode while the fake inode is still hashed and marked I_FREEING. That iput can synchronously write back the base inode. The writeback path may update mapping pairs and call ntfs_attrlist_update(), which unconditionally calls ntfs_attr_iget() for the same $ATTRIBUTE_LIST fake inode. VFS then finds the I_FREEING inode and waits for eviction to finish, but the current task is still inside that eviction path, causing a self-deadlock in find_inode(). Fix this by mirroring the teardown guard used by __ntfs_write_inode(): once SB_ACTIVE has been cleared, do not try to iget the attribute-list fake inode. Return -EIO so teardown aborts the update instead of waiting on the inode it is evicting. Reported-by: Peiyang He <peiyang_he@smail.nju.edu.cn> Closes: https://lore.kernel.org/all/AB8D5E603E6EA856+ae5f622a-dd3a-4e38-bdd2-42276ae0e1a8@smail.nju.edu.cn/ Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn> Assisted-by: Codex:gpt-5.5 Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
6 daysntfs: sanitize MFT references returned from ntfs_lookup_inode_by_name()Namjae Jeon
ntfs_lookup_inode_by_name() returns MFT references read from directory index entries on disk. These values are untrusted, but the function can currently return an error-marked MFT reference to its callers without validating it. Callers later decode lookup failures with MREF_ERR(). A crafted NTFS image can set the MREF error bit while leaving the low bits as an arbitrary value, causing callers to consume a bogus pseudo-errno instead of treating the lookup result as corrupted on-disk metadata. Fix this at the source by normalizing every error-marked MFT reference returned from ntfs_lookup_inode_by_name() to ERR_MREF(-EIO). Apply this to all four directory lookup return paths so every caller gets a validated result without needing additional checks or an API change. This keeps the sanitization in the common lookup helper, which is cleaner than duplicating validation in each caller. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Cc: stable@vger.kernel.org Reported-by: Hongling Zeng <zenghongling@kylinos.cn> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
6 daysntfs: fix hole runlist memory leak in insert range error pathPeiyang He
ntfs_non_resident_attr_insert_range() allocates hole_rl before mapping the whole runlist. If ntfs_attr_map_whole_runlist() fails, the error path drops ni->runlist.lock and returns without freeing hole_rl. This leaks memory of sizeof(*hole_rl) * 2 bytes. Fix this memory leak by freeing hole_rl before returning from that error path, matching the later error paths in the same function. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
6 daysntfs: avoid calling post_write_mst_fixup() for invalid index_blockValeriy Yashnikov
ntfs_icx_ib_sync_write() calls post_write_mst_fixup() when ntfs_ib_write() returns an error, intending to restore the buffer after a failed write. However, ntfs_ib_write() returns an error immediately if pre_write_mst_fixup() validation fails. The caller, ntfs_icx_ib_sync_write(), interprets any error as a write failure requiring rollback. It does not differentiate between I/O errors and validation failures, and calls post_write_mst_fixup() anyway. Since post_write_mst_fixup() assumes that the index_block contents is correct, it doesn't perform the boundary checks, which results in out-of-bounds memory access. An attacker can craft a malicious NTFS image with: - large index_block.usa_ofs offset, pointing outside the ntfs_record - index_block.usa_count = 0, causing integer underflow - or index_block.usa_count larger than actual number of sectors in the ntfs_record, causing out-of-bounds access KASAN reports describing the memory corruption: ================================================================== BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x19c/0x1d0 Read of size 2 at addr ffff8881586c9018 by task p/9428 Call Trace: <TASK> dump_stack_lvl+0x100/0x190 print_report+0x139/0x4ad ? post_write_mst_fixup+0x19c/0x1d0 ? __virt_addr_valid+0x262/0x500 ? post_write_mst_fixup+0x19c/0x1d0 kasan_report+0xe4/0x1d0 ? post_write_mst_fixup+0x19c/0x1d0 post_write_mst_fixup+0x19c/0x1d0 ntfs_icx_ib_sync_write+0x179/0x220 ntfs_inode_sync_filename+0x83d/0x1080 __ntfs_write_inode+0x1049/0x1480 ntfs_file_fsync+0x131/0x9b0 ================================================================== BUG: KASAN: slab-out-of-bounds in post_write_mst_fixup+0x1aa/0x1d0 Write of size 2 at addr ffff8881586c91fe by task p/9428 Call Trace: <TASK> dump_stack_lvl+0x100/0x190 print_report+0x139/0x4ad ? post_write_mst_fixup+0x1aa/0x1d0 ? __virt_addr_valid+0x262/0x500 ? post_write_mst_fixup+0x1aa/0x1d0 kasan_report+0xe4/0x1d0 ? post_write_mst_fixup+0x1aa/0x1d0 post_write_mst_fixup+0x1aa/0x1d0 ntfs_icx_ib_sync_write+0x179/0x220 ntfs_inode_sync_filename+0x83d/0x1080 __ntfs_write_inode+0x1049/0x1480 ntfs_file_fsync+0x131/0x9b0 ================================================================== Let's move the post_write_mst_fixup() call to ntfs_ib_write(). The ntfs_ib_write() function calls pre_write_mst_fixup() at the beginning. If the index_block contents is invalid, pre_write_mst_fixup() fails and ntfs_ib_write() returns early without calling post_write_mst_fixup() on bad index_block. Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations") Cc: stable@vger.kernel.org Signed-off-by: Valeriy Yashnikov <yashnikov.valeriy@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
6 daysntfs: fix WARN_ON for resident attribute in ntfs_map_runlist_nolock()Namjae Jeon
When ntfs_map_runlist_nolock() needs to look up the attribute extent containing a target VCN (ctx_needs_reset == true), it calls ntfs_attr_lookup() and then expects the result to be a non-resident attribute, since only non-resident attributes have a mapping pairs array to decompress. A crafted NTFS image can place a resident attribute where a non-resident one is expected, causing ntfs_attr_lookup() to succeed but return a resident attribute record. Previously this was caught only by a WARN_ON(), which does not stop execution. The code then falls through to read a->data.non_resident.highest_vcn from what is actually a resident attribute, accessing the wrong union member and corrupting the VCN range check. The caller path triggering this warning during mount is: ntfs_map_runlist_nolock ntfs_empty_logfile load_system_files ntfs_fill_super In this path ctx is NULL, so ntfs_map_runlist_nolock() allocates a temporary search context internally and sets ctx_needs_reset = true. The existing resident-attribute guard in the ctx != NULL branch already returns -EIO silently for the same condition; make the ctx_needs_reset path consistent by replacing the WARN_ON() with the same -EIO error return. This causes the crafted image to be rejected with a mount error instead of triggering a kernel warning. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Cc: stable@vger.kernel.org Reported-by: Sangho Lee <kudo3228@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
6 daysntfs: avoid self-deadlock during inode evictionHyunchul Lee
An attribute-list update performed while allocating clusters can drop the last reference to the temporary attribute inode. Evicting that inode drops its reference to the base inode and can invoke ntfs_drop_big_inode() for the base inode from within the base inode's own writeback path. If the base inode is unlinked, ntfs_drop_big_inode() calls truncate_setsize(), which waits for the inode's folio writeback to complete. The same writeback worker is responsible for completing that writeback, so it waits for itself indefinitely. Prevent this self-deadlock by grabbing a reference to the base inode at the beginning of ntfs_writepages() and releasing it at the end of the function. This defers eviction until all bios have been submitted, allowing the wait for folio writeback to complete safely. Fixes: b041ca562526 ("ntfs: update iomap and address space operations") Cc: stable@vger.kernel.org Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
6 daysntfs: make system files immutable to prevent corruptionNamjae Jeon
When a system file such as $Bitmap is exposed via show_sys_files and written from userspace, the volume is corrupted and, because the cluster allocator scans $Bitmap through the same inode's page cache, a write to $Bitmap also deadlocks writeback against the folio it already holds locked. These files are maintained by the driver itself and have no valid reason to be written through the file interface. Mark base metadata files (mft_no < FILE_first_user) as immutable during inode read so the VFS rejects write, mmap, truncate and unlink with -EPERM. Directories are skipped so the root and $Extend remain usable. Internal metadata updates do not go through the VFS write path and are unaffected. Fixes: af0db57d4293 ("ntfs: update inode operations") Cc: stable@vger.kernel.org Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
11 daysiomap: consolidate bio submissionChristoph Hellwig
Add a iomap_bio_submit_read_endio helper factored out of iomap_bio_submit_read to that all ->submit_read implementations for iomap_read_ops that use iomap_bio_read_folio_range can shared the logic. Right now that logic is mostly trivial, but already has a bug for XFS because the XFS version is too trivial: file system integrity validation needs a workqueue context and thus can't happen from the default iomap bi_end_io I/O handler. Unfortunately the iomap refactoring just before fs integrity landed moved code around here and the call go misplaced, meaning it never got called. The PI information still is verified by the block layer, but the offloading is less efficient (and the future userspace interface can't get at it). Fixes: 0b10a370529c ("iomap: support T10 protection information") Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260629121750.3392300-2-hch@lst.de Acked-by: Namjae Jeon <linkinjeon@kernel.org> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
12 daysntfs: fix mrec_lock ABBA deadlock in renamePeiyang He
ntfs_file_fsync(), ntfs_dir_fsync() and __ntfs_write_inode() lock an inode's mrec_lock before taking the mrec_lock of its parent directory. ntfs_rename() takes old_ni->mrec_lock and old_dir_ni->mrec_lock before taking new_ni->mrec_lock for an existing target, or new_dir_ni->mrec_lock for a cross-directory rename. This can deadlock when ntfs_file_fsync() or __ntfs_write_inode() holds the target inode, or when ntfs_dir_fsync() holds a child target directory, while rename() holds the parent directory and waits for the target. Fix this by locking the existing target inode before taking any parent directory mrec_lock. For cross-directory renames where the target parent is a descendant of the source parent, lock the target parent before the source parent so the directory order matches the child-to-parent order used by ntfs_file_fsync(), ntfs_dir_fsync(), and __ntfs_write_inode(). Reported-by: Peiyang He <peiyang_he@smail.nju.edu.cn> Closes: https://lore.kernel.org/all/C4D296F0E9F3D66C+9397ffbc-eb55-44bb-9b3f-5da4809e7955@smail.nju.edu.cn/ Fixes: af0db57d4293 ("ntfs: update inode operations") Cc: stable@vger.kernel.org Signed-off-by: Peiyang He <peiyang_he@smail.nju.edu.cn> Assisted-by: Codex:gpt-5.5 Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
12 daysntfs: avoid stale runlist element dereference in fallocateCen Zhang
ntfs_attr_fallocate() allocates holes and delayed allocations inside initialized size by looking up the current runlist element under ni->runlist.lock. The returned struct runlist_element is only a borrowed pointer into ni->runlist.rl. A writer can replace and free that array after the read lock is dropped, so later reads of rl->lcn, rl->length and rl->vcn can touch freed memory. The buggy scenario involves two paths, with each column showing the order within that path: ntfs_attr_fallocate(): 1. Take ni->runlist.lock for read. 2. Get rl from ntfs_attr_find_vcn_nolock(). 3. Drop ni->runlist.lock. 4. Read rl->lcn, rl->length and rl->vcn. mmap page_mkwrite: 1. Enter ntfs_filemap_page_mkwrite(). 2. Reach __ntfs_write_iomap_begin() and ntfs_attr_map_cluster(). 3. Merge allocation state with ntfs_runlists_merge(). 4. Reallocate ni->runlist.rl in ntfs_rl_realloc(), freeing the old array. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfs_attr_fallocate+0xbb8/0xd00 Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? ntfs_attr_fallocate+0xbb8/0xd00 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? ntfs_attr_fallocate+0xbb8/0xd00 kasan_report+0xe0/0x110 ? ntfs_attr_fallocate+0xbb8/0xd00 ntfs_attr_fallocate+0xbb8/0xd00 ? lock_acquire+0x2b8/0x2f0 ? __pfx_ntfs_attr_fallocate+0x10/0x10 ? 0xffffffffc0000095 ? down_write+0x10d/0x1e0 ntfs_fallocate+0x5c9/0x1d00 ? __pfx_ntfs_fallocate+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x2b8/0x2f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? selinux_file_permission+0x3a7/0x510 vfs_fallocate+0x29d/0xd30 __x64_sys_fallocate+0xc7/0x150 ? do_syscall_64+0x81/0x6a0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Allocated by task 410: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmalloc_node_noprof+0x353/0x920 ntfs_rl_realloc+0x3f/0x110 ntfs_runlists_merge+0xaa3/0x3010 ntfs_attr_map_cluster+0x4e5/0xf80 ntfs_attr_fallocate+0x53f/0xd00 ntfs_fallocate+0x5c9/0x1d00 vfs_fallocate+0x29d/0xd30 __x64_sys_fallocate+0xc7/0x150 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 424: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 ntfs_rl_realloc+0x6f/0x110 ntfs_runlists_merge+0x7b1/0x3010 ntfs_attr_map_cluster+0x4e5/0xf80 __ntfs_write_iomap_begin+0x8cd/0x2280 iomap_iter+0x6de/0x11e0 iomap_page_mkwrite+0x391/0x650 ntfs_filemap_page_mkwrite+0x1ac/0x400 do_page_mkwrite+0x15c/0x280 __handle_mm_fault+0xd6d/0x1ca0 handle_mm_fault+0x19c/0x470 do_user_addr_fault+0x23b/0x9c0 exc_page_fault+0x5c/0xc0 asm_exc_page_fault+0x26/0x30 Fix this by copying the needed runlist fields while the read lock is still held and using only those scalar snapshots after unlocking. After the snapshot, ntfs_attr_map_cluster() can also find that the range is already mapped and return balloc=false. Only call ntfs_dio_zero_range() when new clusters were allocated, matching the write iomap path and preserving the zero-newly-allocated-holes behavior. Fixes: 495e90fa3348 ("ntfs: update attrib operations") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-25ntfs: avoid stale runlist element dereference in MFT writebackCen Zhang
ntfs_write_mft_block() maps each $MFT record through the $MFT data runlist. For sub-folio clusters it looks up a struct runlist_element under ni->runlist.lock, drops the lock, and later uses rl->length and rl->vcn when choosing folio_sz. That pointer is only borrowed from ni->runlist.rl. Concurrent $MFT allocation extension can merge a replacement runlist under the same lock, and ntfs_rl_realloc() can free the old backing array. If that happens between the lookup and the later folio_sz decision, writeback can dereference freed runlist storage. The buggy scenario involves two paths, with each column showing the order within that path: MFT writeback path: $MFT allocation extension: 1. Look up rl under 1. Extend the $MFT data allocation. ni->runlist.lock. 2. Publish a replacement runlist. 2. Drop ni->runlist.lock. 3. Free the old runlist array. 3. Read rl->length and rl->vcn to choose folio_sz. Compute the remaining run length while ni->runlist.lock is still held, and use that scalar after unlock. This preserves the existing folio sizing decision without carrying a borrowed runlist_element across the lock boundary. Validation reproduced this kernel report: BUG: KASAN: slab-use-after-free in ntfs_mft_writepages+0x1c8d/0x1fb0 Call Trace: <TASK> dump_stack_lvl+0x66/0xa0 print_report+0xce/0x630 ? ntfs_mft_writepages+0x1c8d/0x1fb0 ? srso_alias_return_thunk+0x5/0xfbef5 ? __virt_addr_valid+0x20d/0x410 ? ntfs_mft_writepages+0x1c8d/0x1fb0 kasan_report+0xe0/0x110 ? ntfs_mft_writepages+0x1c8d/0x1fb0 ntfs_mft_writepages+0x1c8d/0x1fb0 ? __pfx_ntfs_mft_writepages+0x10/0x10 ? __pfx___mutex_unlock_slowpath+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? iput+0x92/0xa80 do_writepages+0x219/0x530 ? __pfx_do_writepages+0x10/0x10 __writeback_single_inode+0x117/0xf50 ? do_raw_spin_lock+0x130/0x270 ? __pfx_do_raw_spin_lock+0x10/0x10 ? __pfx___writeback_single_inode+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 writeback_sb_inodes+0x65b/0x1810 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_acquire+0x2b8/0x2f0 ? __pfx_writeback_sb_inodes+0x10/0x10 ? lock_release+0x1e0/0x280 ? _raw_spin_unlock+0x23/0x40 ? move_expired_inodes+0x2b8/0x850 __writeback_inodes_wb+0xf4/0x270 ? __pfx___writeback_inodes_wb+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? queue_io+0x2e4/0x410 wb_writeback+0x666/0x880 ? srso_alias_return_thunk+0x5/0xfbef5 ? __pfx_wb_writeback+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? srso_alias_return_thunk+0x5/0xfbef5 ? get_nr_dirty_inodes+0x1c/0x170 wb_workfn+0x75e/0xbb0 ? srso_alias_return_thunk+0x5/0xfbef5 ? _raw_spin_unlock_irqrestore+0x27/0x60 ? __pfx_wb_workfn+0x10/0x10 ? __pfx_debug_object_deactivate+0x10/0x10 ? lock_acquire+0x2b8/0x2f0 ? srso_alias_return_thunk+0x5/0xfbef5 ? lock_release+0x1e0/0x280 process_one_work+0x8d0/0x1870 ? __pfx_process_one_work+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 worker_thread+0x575/0xf80 ? __pfx_worker_thread+0x10/0x10 kthread+0x2e7/0x3c0 ? __pfx_kthread+0x10/0x10 ret_from_fork+0x576/0x810 ? __pfx_ret_from_fork+0x10/0x10 ? srso_alias_return_thunk+0x5/0xfbef5 ? __switch_to+0x57e/0xe10 ? __switch_to_asm+0x33/0x70 ? __pfx_kthread+0x10/0x10 ret_from_fork_asm+0x1a/0x30 </TASK> Allocated by task 970: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 __kasan_kmalloc+0xaa/0xb0 __kvmalloc_node_noprof+0x353/0x920 ntfs_rl_realloc+0x3c/0x80 ntfs_runlists_merge+0x1212/0x3010 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40 ntfs_mft_record_alloc+0x1ab4/0x4f10 __ntfs_create+0x680/0x2e50 ntfs_create+0x1e6/0x3a0 path_openat+0x2b55/0x3c10 do_file_open+0x1f4/0x460 do_sys_openat2+0xde/0x170 __x64_sys_openat+0x122/0x1e0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Freed by task 1294: kasan_save_stack+0x33/0x60 kasan_save_track+0x14/0x30 kasan_save_free_info+0x3b/0x60 __kasan_slab_free+0x5f/0x80 kfree+0x307/0x580 ntfs_rl_realloc+0x66/0x80 ntfs_runlists_merge+0x1212/0x3010 ntfs_mft_data_extend_allocation_nolock+0x3e0/0x1f40 ntfs_mft_record_alloc+0x1ab4/0x4f10 __ntfs_create+0x680/0x2e50 ntfs_create+0x1e6/0x3a0 path_openat+0x2b55/0x3c10 do_file_open+0x1f4/0x460 do_sys_openat2+0xde/0x170 __x64_sys_openat+0x122/0x1e0 do_syscall_64+0x115/0x6a0 entry_SYSCALL_64_after_hwframe+0x77/0x7f Fixes: 115380f9a2f9 ("ntfs: update mft operations") Assisted-by: Codex:gpt-5.5 Signed-off-by: Cen Zhang <zzzccc427@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-19Merge tag 'ntfs-for-7.2-rc1' of ↵Linus Torvalds
git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs Pull ntfs updates from Namjae Jeon: - Harden handling of malformed on-disk metadata. This adds stricter validation for attributes, attribute lists, index roots and entries, EA entries, mapping pairs, and $LogFile restart areas. These changes fix several out-of-bounds access, integer overflow, and inconsistent metadata handling issues. - Prevent a writeback deadlock involving extent MFT records - Fix resource leaks in fill_super() failure paths and the name cache - Serialize volume label access and improving its error handling - Fix mapping-pairs decoding bounds and LCN overflow checks - Keep resident index root metadata consistent during resize - Fix the reported size of symbolic links - Avoid an unnecessary allocation for resident inline data - Add support for following and creating Windows native symbolic links. Relative links, absolute links, and junctions are handled, with new mount options controlling native symlink creation and absolute target translation. The existing WSL symlink behavior remains the default. - The unsupported quota code is removed, along with several smaller cleanups * tag 'ntfs-for-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/linkinjeon/ntfs: (39 commits) docs/fs/ntfs: add mount options to support Windows native symbolic links ntfs: support creating Windows native symlinks ntfs: clean up target name conversion for WSL symlinks ntfs: add native_symlink mount option ntfs: support following Windows native symlink with absolute paths ntfs: support following Windows native symlink with relative paths ntfs: fix incorrect size of symbolic link ntfs: use direct pointer for inline data to avoid redundant allocation ntfs: validate resident index root values on lookup ntfs: update index root allocated size before shrink ntfs: grow index root value before reparent header update ntfs: reject non-resident records for resident-only attributes ntfs: fix u16 truncation of restart-area length check ntfs: bound the attribute-list entry in ntfs_read_inode_mount() ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find() ntfs: validate resident attribute lists and harden the validator ntfs: validate resident volume name values on lookup ntfs: reinit search context before volume information lookup ntfs: do not replace volume name after lookup errors ntfs: validate attribute values on lookup ...
2026-06-15ntfs: support creating Windows native symlinksHyunchul Lee
And introduce the symlink=<value> mount option to configure how symbolic links are created. The option accepts "wsl" or "native", with "wsl" being the default. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15ntfs: clean up target name conversion for WSL symlinksHyunchul Lee
WSL symlink target names are stored as narrow NLS/UTF-8 strings on disk. Converting the target name to Unicode in ntfs_symlink and converting it back to NLS in ntfs_reparse_set_wsl_symlink is redundant. Remove this conversion and pass the symname directly to the reparse data setter. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15ntfs: add native_symlink mount optionHyunchul Lee
Because bind-mounted subtrees of the volume may resolve to unexpected locations, change converting junctions and non-relative symbolic links into paths relative to the NTFS volume to be allowed only if the native_symlink=rel mount option is specified. Add the native_symlink=<value> mount option to configure how absolute symbolic links and mount points (junctions) are handled. The option accepts "raw" or "rel", with "raw" being the default. Under "raw", the absolute target path (ni->target) is returned as-is without translation. Under "rel", ntfs_translate_junction() is called to rewrite the absolute path as a relative path anchored at the volume root. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15ntfs: support following Windows native symlink with absolute pathsHyunchul Lee
Extend reparse-point handling beyond relative symlinks so NTFS can expose the Windows absolute forms used by non-relative symbolic links and junctions. * Store the reparse tag and symlink flags in the inode. * Validate junction payloads, and parse targets from substitute_name. * Add function to rewrite supported Windows absolute path into Linux path relative to the mounted NTFS volume. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15ntfs: support following Windows native symlink with relative pathsHyunchul Lee
Make ntfs_make_symlink() parse native Windows symbolic link reparse payloads when the SYMLINK_FLAG_RELATIVE bit is set. Implement the following changes: * Add a dedicated on-disk layout definition for symbolic link reparse data. * validate the UTF-16 name ranges before decoding them. * convert the substitute name into the mount's NLS and normalize path separators. Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-15ntfs: fix incorrect size of symbolic linkHyunchul Lee
This patch fixes the issue where a symbolic link size is displayed as 0. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-09ntfs: use direct pointer for inline data to avoid redundant allocationNamjae Jeon
Previously, NTFS used page allocation for IOMAP_INLINE to ensure that the inline_data pointer was page-aligned, avoiding strict boundary checks in the iomap core. Since the previous patch has removed the over-strict PAGE_SIZE boundary check in iomap, NTFS can now safely point iomap::inline_data directly to the MFT record. This change eliminates redundant memory allocations and memcpy operations in both read and write paths. It also simplifies the iomap_ops by removing the need for a iomap_end callback that was previously used to free the temporary page. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-09ntfs: validate resident index root values on lookupDaeMyung Kang
Resident $INDEX_ROOT values carry index header fields that callers consume after lookup. Some callers already validate parts of the layout before walking entries, but those checks are scattered and do not cover all root header invariants, such as entries_offset alignment and lower bound, index_length, and allocated_size consistency. The resident root resize paths now keep these header fields consistent while the value size changes: ntfs_ir_truncate() lowers index.allocated_size before shrinking the resident value, and ntfs_ir_reparent() grows the resident value before publishing a larger root header. Lookup-time validation can therefore cover these invariants without tripping over the driver's own resize paths. Add $INDEX_ROOT to the minimum resident value size table and validate the resident index header fields before returning the attribute from lookup. Require 8-byte aligned index header fields, a sane entries_offset, an index_length within allocated_size, allocated_size within the resident value, and enough entry space for at least an index entry header. The shared validator already rejects non-resident records for resident-only attribute types, including $INDEX_ROOT. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-09ntfs: update index root allocated size before shrinkDaeMyung Kang
ntfs_ir_truncate() currently shrinks the resident $INDEX_ROOT value first and only updates index.allocated_size after re-looking up the attribute. During that relookup, the resident value_length can already be smaller while index.allocated_size still contains the old larger size. That leaves a transiently inconsistent $INDEX_ROOT layout and prevents lookup-time $INDEX_ROOT validation from being enabled: validation can correctly reject allocated_size extending past the newly shrunk resident value. When shrinking, lower index.allocated_size before shrinking value_length. If the truncate fails, restore the old allocated_size. Keep the existing grow ordering because the old allocated_size remains within the enlarged resident value until it is updated after the relookup. The shrink path is safe because the new value_length still covers struct index_root, so the index.allocated_size field remains present while it is updated first. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-09ntfs: grow index root value before reparent header updateDaeMyung Kang
ntfs_ir_reparent() moves the resident index root entries into an index block and leaves a small root stub containing the child VCN. That root stub can be larger than the existing resident value. For example, an empty root with value_length 48 has an index area of 32 bytes, while the large-index root stub needs index_length and allocated_size of 40 bytes. The current code publishes the larger index.index_length and index.allocated_size before resizing the resident value. If the resize returns -ENOSPC, the recovery path can call ntfs_inode_add_attrlist(), which looks attributes up again while the root header says allocated_size 40 but the resident value still only provides 32 bytes of index area. Lookup-time $INDEX_ROOT validation then correctly rejects that transient layout as corrupt. This reproduces as a generic/013 failure under qemu. In the failing run, the transient root had value_len=48, index_size=32, index_length=40, and allocated_size=40, and ntfsprogs-plus ntfsck reported "Corrupt index root in MFT record 1177". When the root stub grows, resize the resident value before publishing the larger root header. If the resize fails, the old root remains valid for recovery lookups. Keep the existing header-before-resize ordering for shrink or same-size cases so the resident value never temporarily exposes an allocated_size beyond its bounds. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-09ntfs: reject non-resident records for resident-only attributesDaeMyung Kang
The shared lookup-time attribute validator rejects non-resident $FILE_NAME and $VOLUME_NAME records because their formats require resident values and callers handle returned records as resident attributes. Other resident-only attribute types still pass through the generic non-resident mapping-pairs checks. That leaves real resident/non-resident union confusion paths. Inode load looks up $STANDARD_INFORMATION and then reads data.resident.value_offset without checking a->non_resident. ntfs_inode_sync_standard_information() does the same when updating the standard information value. ntfs_write_volume_flags() also looks up $VOLUME_INFORMATION and reads data.resident.value_offset directly. $INDEX_ROOT callers in dir.c and index.c depend on the same lookup contract before consuming the resident index root value. Reject non-resident records for all resident-only attribute types in the shared validator. Keep the existing $FILE_NAME and $VOLUME_NAME behavior, but factor it through a helper and extend it to $STANDARD_INFORMATION, $OBJECT_ID, $VOLUME_INFORMATION, $INDEX_ROOT, and $EA_INFORMATION. For $OBJECT_ID and $EA_INFORMATION this is contract hardening for resident-only formats; this patch only rejects the non-resident form and does not add new resident value validation for those types. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-08ntfs: fix u16 truncation of restart-area length checkBryam Vargas
ntfs_check_restart_area() validates that the $LogFile restart area and its trailing log client record array fit within the system page size: u16 ra_ofs, ra_len, ca_ofs; ... ra_len = ca_ofs + le16_to_cpu(ra->log_clients) * sizeof(struct log_client_record); if (ra_ofs + ra_len > le32_to_cpu(rp->system_page_size) || ...) return false; ra_len is u16, but the right-hand side is computed in size_t (sizeof(struct log_client_record) == 160). Both ca_ofs and log_clients come straight from the on-disk restart area. With an on-disk log_clients of 410 the product 410 * 160 = 65600; adding ca_ofs and storing into the u16 ra_len truncates modulo 65536 (e.g. ca_ofs 64 gives ra_len 128), so the "fits in the page" check passes even though the client array described by log_clients extends far beyond the page. ntfs_check_log_client_array() then walks the array bounded only by the on-disk log_clients count: cr = ca + idx; if (cr->prev_client != LOGFILE_NO_CLIENT) ... For log_clients 410 it dereferences records up to ca + 409 * 160, ~64 KiB past the kvzalloc(system_page_size) restart-page buffer -- an out-of-bounds read of attacker-controlled extent, reachable when a crafted NTFS image is mounted (load_and_check_logfile() at mount time). This is the in-kernel analogue of CVE-2022-30789, fixed in the ntfs-3g userspace driver but never in this revived classic driver. Compute the restart-area length in a u32 so the existing bounds check rejects an over-large client array instead of being defeated by the truncation. Widen ra_ofs and ca_ofs to u32 as well: both are loaded from __le16 on-disk fields and every comparison already promotes to int/size_t, so this changes no result and keeps the declaration uniform. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-08ntfs: bound the attribute-list entry in ntfs_read_inode_mount()Bryam Vargas
The $MFT attribute-list walk in ntfs_read_inode_mount() validates each entry only with "(u8 *)al_entry + 6 > al_end" and "(u8 *)al_entry + le16_to_cpu(al_entry->length) > al_end", but then reads al_entry->lowest_vcn (an __le64 at offset 8) and al_entry->mft_reference (offset 16) -- fields beyond the 6 bytes proven in range. al_entry->length is attacker-controlled and only required non-zero, so a short entry (e.g. length 8) placed at the tail passes both checks while the lowest_vcn / mft_reference reads fall past al_end. al_end is ni->attr_list + attr_list_size (the on-disk size); the buffer is kvzalloc(round_up(attr_list_size, SECTOR_SIZE)), so the sector rounding usually absorbs the over-read -- but when attr_list_size is a multiple of SECTOR_SIZE there is no slack and a crafted $MFT attribute list produces an out-of-bounds read at mount time. Validate the entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing it, matching the bound the other attribute-list walks now use. The validator already requires the length to cover the fixed header, which makes the separate "!al_entry->length" check redundant, so drop it too. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-08ntfs: bound the look-ahead attribute-list entry in ntfs_external_attr_find()Bryam Vargas
When resolving an attribute lookup with a non-zero @lowest_vcn, ntfs_external_attr_find() peeks at the next $ATTRIBUTE_LIST entry to decide whether to keep searching, but bounds that not-yet-validated entry only with "(u8 *)next_al_entry + 6 < al_end" (which proves just bytes 0..6 are in range) and "(u8 *)next_al_entry + length <= al_end" with an attacker-controlled, non-8-aligned length. It then reads next_al_entry->lowest_vcn (an __le64 at offset 8) and the name at next_al_entry->name_offset, both of which can lie past al_end -- the exact end of the kvmalloc'd attribute-list buffer (allocated at the on-disk attr_list_size, no rounding). A crafted on-disk $ATTRIBUTE_LIST whose last entry sits a few bytes before al_end therefore yields a slab out-of-bounds read when the inode is read. Validate the look-ahead entry with ntfs_attr_list_entry_is_valid() (added in patch 1/3) before dereferencing lowest_vcn and the name, so the same fixed-header, length and name bounds the main attribute-list walk uses now guard this read too. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-08ntfs: validate resident attribute lists and harden the validatorBryam Vargas
A base inode's $ATTRIBUTE_LIST is sanity-checked by load_attribute_list() only on the non-resident path; ntfs_read_locked_inode() copies a *resident* attribute list into ni->attr_list with a plain memcpy() and no validation at all. Every subsequent walk of ni->attr_list -- ntfs_external_attr_find(), ntfs_inode_attach_all_extents() and ntfs_attrlist_need() -- then trusts the entries are well-formed and reads attr_list_entry fixed-header fields (lowest_vcn at offset 8, mft_reference at offset 16, and the name) with bounds that assume validation already happened. A crafted resident attribute list therefore reaches those walks unvalidated and can drive out-of-bounds reads of the attribute-list buffer. load_attribute_list() itself reads ale->name_offset (offset 7), ale->mft_reference (offset 16) and the name length under only an "al < al_start + size" bound, so its own validation loop can over-read the fixed header of a truncated trailing entry by a few bytes. Factor the per-entry validation into ntfs_attr_list_entry_is_valid(), which requires each entry's fixed header (offsetof(struct attr_list_entry, name)) to be in range before any field is dereferenced, that ale->length is a multiple of 8 covering the fixed header plus the name, and that the entry is in use and carries a live MFT reference. ntfs_attr_list_is_valid() walks the buffer with it and checks the entries tile it exactly. Use the list validator in load_attribute_list() (replacing the open-coded loop, closing its own over-read) and on the resident path in ntfs_read_locked_inode() (which previously skipped validation entirely); patches 2/3 reuse the per-entry helper at the other two attribute-list walks. Fixes: 1e9ea7e04472 ("Revert "fs: Remove NTFS classic"") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: use d_splice_alias() for ->lookup() return valueAl Viro
Reviewed-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2026-06-06ntfs: validate resident volume name values on lookupDaeMyung Kang
The shared lookup-time attribute validator now has a safe caller path for $VOLUME_NAME corruption: ntfs_write_volume_label() no longer treats lookup errors as an absent label, and the mount path reinitializes its search context before continuing to $VOLUME_INFORMATION. Add $VOLUME_NAME-specific resident value validation. A volume name is stored as a UTF-16LE string, so reject odd byte lengths, and reject values longer than the NTFS volume label limit. Empty labels remain valid. Also reject non-resident $VOLUME_NAME records. $VOLUME_NAME is required to be resident, like $FILE_NAME; a crafted non-resident record would otherwise pass lookup and ntfs_write_volume_label() would remove it as if it were a normal resident attribute. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: reinit search context before volume information lookupDaeMyung Kang
On mount the volume inode is searched for $VOLUME_NAME and then, reusing the same search context, for $VOLUME_INFORMATION. The $VOLUME_NAME lookup is optional and its result is otherwise ignored. Once lookup-time validation can reject a corrupt $VOLUME_NAME with -EIO, the search context is left in an undefined state: ntfs_attr_find() documents that on an actual error @ctx->attr is undefined. Continuing the $VOLUME_INFORMATION search from that context is not contractually valid. Reinitialize the search context before the $VOLUME_INFORMATION lookup so it always starts from a well-defined state regardless of the $VOLUME_NAME lookup outcome. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: do not replace volume name after lookup errorsDaeMyung Kang
ntfs_write_volume_label() removes an existing $VOLUME_NAME attribute and then adds the replacement. The old code only distinguished lookup success from all other results, so any lookup error was treated like an absent label and the add path still ran. That is unsafe once lookup-time validation rejects corrupt $VOLUME_NAME records with -EIO: the corrupt record would remain in place and a second $VOLUME_NAME record could be appended next to it. Only add the replacement after the old label was removed successfully or after lookup returned -ENOENT. Propagate all other lookup errors, and also stop if removing the old attribute fails. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: validate attribute values on lookupDaeMyung Kang
ntfs_attr_find() and ntfs_external_attr_find() check that generic resident attribute values fit in their attribute records and that fixed-size resident values are large enough. For variable-length resident formats, however, the fixed part is not enough: embedded length fields can still point callers past the resident value. A crafted image can set a small resident $FILE_NAME value_length while leaving file_name_length large. Callers then trust file_name_length and read past the resident value when converting or comparing the name. This was reproduced with a crafted image under KASAN as a slab-out-of-bounds read from the kmalloc-1k MFT record copy. The stack included ntfs_lookup(), ntfs_iget(), ntfs_read_locked_inode(), ntfs_attr_name_get(), ntfs_ucstonls(), and utf16s_to_utf8s(). Add a shared attribute value validator and use it before a lookup path can return an attribute, including the AT_UNUSED enumeration case where callers inspect returned attributes directly. The helper validates resident value bounds, minimum resident value sizes, variable-length $FILE_NAME fields, and non-resident mapping-pairs metadata that was previously checked separately in both lookup paths. This also preserves the intended resident @val matching semantics in the external attribute lookup path. The old duplicated validation block overwrote the actual resident value length with the type-specific minimum length before comparing @val, so variable-length resident values could fail to match even when the bytes were identical. Keep the comparison on the actual value length, and make ntfs_attrlist_entry_add() compare resident attributes with lowest_vcn zero instead of reading the non-resident union member after a successful resident match. Reject non-resident $FILE_NAME records too: the format requires $FILE_NAME to be resident and callers treat returned records as resident. Cc: stable@vger.kernel.org # v7.1 Fixes: 6ceb4cc81ef3 ("ntfs: add bound checking to ntfs_attr_find") Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: detect mapping-pairs LCN accumulator overflowSamuel Moelius
The NTFS mapping-pairs parser accumulates relative LCN deltas in a signed integer. A corrupted attribute can drive that addition past the representable range. One corrupt runlist shape sets the accumulated LCN to S64_MAX and then adds a delta of 1 in the next mapping-pairs entry. Signed overflow is undefined and can turn an invalid runlist into a different set of physical clusters. Check the LCN addition for overflow before storing the next run. Cc: stable@vger.kernel.org # v7.1 Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius <sam.moelius@trailofbits.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: Add WQ_PERCPU to alloc_workqueue usersMarco Crivellari
This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") The refactoring is going to alter the default behavior of alloc_workqueue() to be unbound by default. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. For more details see the Link tag below. In order to keep alloc_workqueue() behavior identical, explicitly request WQ_PERCPU. Cc: stable@vger.kernel.org # v7.1 Link: https://lore.kernel.org/all/20250221112003.1dSuoGyc@linutronix.de/ Suggested-by: Tejun Heo <tj@kernel.org> Signed-off-by: Marco Crivellari <marco.crivellari@suse.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: serialize volume label accessesHyunchul Lee
Protect vol->volume_label with a mutex and snaphost the label before copy_to_user. This prevent a use-after-free when FS_IOC_SETFSLABEL replaces the vol->volume_label and FS_IOC_GETTSLABEL reads it concurrently. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: fix off-by-one in mapping pairs decoding bounds checksRon de Bruijn
In ntfs_mapping_pairs_decompress(), attr_end points one byte past the end of the attribute record: attr_end = (u8 *)attr + le32_to_cpu(attr->length); The two bounds checks validating that mapping pair data bytes fit within the attribute use strict greater-than (>), which allows a one-byte out-of-bounds read when the data extends exactly to attr_end: b = *buf & 0xf; if (b) { if (unlikely(buf + b > attr_end)) // off-by-one goto io_error; for (deltaxcn = (s8)buf[b--]; b; b--) deltaxcn = (deltaxcn << 8) + buf[b]; } When buf + b == attr_end, the check evaluates to false and buf[b] reads one byte past the valid attribute boundary. The same pattern appears in the LCN delta bytes check. Fix both checks to use >= so that buf[b] at exactly attr_end is correctly rejected as out of bounds. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Ron de Bruijn <rmbruijn@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: not change 0-byte $DATA attribute to non-residentHyunchul Lee
When ntfs_resident_attr_resize() cannot grow a resident attribute in place, it retries after converting other resident attributes to non-resident to free space in the MFT recrord. Do not select zero-length resident $DATA attributes for this conversion. fsck treats 0-byte non-resident $DATA attribute as corruptions. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: Fix spelling mistake "etnry" -> "entry"Colin Ian King
There is a spelling mistake in a ntfs_error message. Fix it. Signed-off-by: Colin Ian King <colin.i.king@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: free link name from ntfs_name_cacheDaeMyung Kang
ntfs_link() converts the new link name with ntfs_nlstoucs() using NTFS_MAX_NAME_LEN. In this case ntfs_nlstoucs() allocates the result from ntfs_name_cache, and its contract requires callers to release the buffer with kmem_cache_free(ntfs_name_cache, ...). All other ntfs_nlstoucs() callers in namei.c do that, but ntfs_link() uses kfree(), which mismatches the allocator for successfully converted names. The conversion failure path reaches the common out label with uname == NULL. That was harmless for kfree(), but kmem_cache_free() does not provide the same NULL contract. Return directly on conversion failure and free successful conversions with ntfs_name_cache. Fixes: af0db57d4293 ("ntfs: update inode operations") Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: remove unnecessary NULL checks before kfreeNamjae Jeon
NULL check before kfree() is unnecessary and triggers coccinelle warnings. Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: remove unnecessary ternary boolean conversionNamjae Jeon
Coccinelle warned about unnecessary patterns when assigning to bool variables. Simply assign the condition directly. Reported-by: kernel test robot <lkp@intel.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: remove unsupported quota handlingDaeMyung Kang
The ntfs driver does not implement quota accounting. It creates new inodes with the NTFS 1.2 $STANDARD_INFORMATION layout and does not maintain the NTFS 3.x owner_id/quota_charged fields or the $Quota usage records that Windows would need for meaningful quota accounting. The only runtime quota path left in the driver is the remount-rw code that tries to mark $Quota/$Q out of date, plus the mount-time code that loads $Quota and its $Q index solely to support that marker. Since the driver does not maintain the per-file quota metadata, setting QUOTA_FLAG_OUT_OF_DATE does not make the quota state meaningful, and failures in this unsupported path can unnecessarily block remount-rw or force a mount read-only. Remove the quota marker, the $Quota/$Q loading state, and the unused quota volume flag. Keep the on-disk quota layout definitions in layout.h so the documented NTFS structures remain available. Suggested-by: Hyunchul Lee <hyc.lee@gmail.com> Link: https://lore.kernel.org/all/CANFS6bYTzioqZjYt=51Kb9RdR3MKXaez_fh_WCLoym093VxFmg@mail.gmail.com/ Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: use str_plural in ntfs_attr_make_non_residentThorsten Blum
Replace the manual ternary "s" pluralization with str_plural() to simplify the code. Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: add bounds check before accessing EA entriesHyunchul Lee
in ntfs_ea_lookup and ntfs_listxattr, this verifies that there is enough space in the EA entry before accessing the next_entry_offset field of the EA entry. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: validate index entries on readingHyunchul Lee
Validate index entries immediately after reading an index root or index block from disk. This eliminates repeated checks in lookup and readdir, and reduce the risk of missing checks in those paths. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 <woot000@woot000.com> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: centalize $INDEX_ROOT header validationHyunchul Lee
Add a dedicated helper to perform stricter validation of $INDEX_ROOT and use it for both directory inodes and named index inodes. This keeps the root size and header geometry checks consistent across both read paths. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 <woot000@woot000.com> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: validate index block header more strictlyHyunchul Lee
Modify ntfs_index_block_inconsisent() to perform stricter validation of INDEX_HEADER geometry in INDX blocks, and update ntfs_lookup_inode_by_name() to use that function to validate INDX blocks. Cc: stable@vger.kernel.org # v7.1 Tested-by: woot000 <woot000@woot000.com> Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: avoid heap allocation for free-cluster readahead stateDaeMyung Kang
get_nr_free_clusters() allocates a temporary file_ra_state before it publishes the precomputed free cluster count, sets NVolFreeClusterKnown(), and wakes vol->free_waitq. If that allocation fails, the worker returns without setting the flag or waking waiters, so callers waiting for the free count can block indefinitely. The readahead state is only used synchronously while scanning the bitmap. Keep it on the stack and pass it by address to the readahead helper. This eliminates the early allocation failure path instead of adding a special case that publishes a conservative count and wakes the waitqueue. Zero-initialize the on-stack state because file_ra_state_init() only sets ra_pages and prev_pos. Apply the same treatment to __get_nr_free_mft_records(), which scans the MFT bitmap with the same short-lived readahead state. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: DaeMyung Kang <charsyam@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-06-06ntfs: skip extent mft records in writeback to prevent deadlockHyunchul Lee
This patch fixes the ABBA deadlock between extent_lock and extent mrec_lock triggered by xfstests generic/113, that occurs since the commit 6994acf33bae ("ntfs: use base mft_no when looking up base inode for extent record"). Path A (inode writeback): VFS writeback -> ntfs_write_inode() -> __ntfs_write_inode() -> mutex_lock(&ni->extent_lock) -> mutex_lock(&tni->mrec_lock) Path B (MFT folio writeback): VFS writeback of $MFT dirty folios -> ntfs_mft_writepages() -> ntfs_write_mft_block() -> ntfs_may_write_mft_record() -> holds one extent mrec_lock from a previous iteration -> tries to acquire another base inode extent_lock By removing all extent_lock and extent mrec_lock acquisition from the MFT folio writeback path, the ABBA lock ordering is eliminated: Path A: __ntfs_write_inode(): extent_lock -> mrec_lock Path B (removed): ntfs_write_mft_block(): mrec_lock -> extent_lock Path B is always redundant for extent records because: 1. mark_mft_record_dirty(ext_ni) does NOT dirty the MFT folio. It only sets NInoDirty(ext_ni) and marks the base VFS inode dirty via __mark_inode_dirty(I_DIRTY_DATASYNC), which triggers Path A. Therefore, normal extent modifications never create a situation where the MFT folio is dirty and Path B is not scheduled. 2. The MFT folio only gets dirtied via ntfs_mft_mark_dirty() inside ntfs_mft_record_alloc(). But all identified callers in attrib.c (ntfs_attr_add, ntfs_attr_record_move_away, ntfs_attr_make_non_resident, ntfs_attr_record_resize) follow through with mark_mft_record_dirty(), which triggers Path A to write the complete record. 3. ntfs_evict_big_inode() calls ntfs_commit_inode() before freeing extent inodes, ensuring all dirty extents are flushed via Path A before the base inode leaves the icache. Cc: stable@vger.kernel.org # v7.1 Signed-off-by: Hyunchul Lee <hyc.lee@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>