aboutsummaryrefslogtreecommitdiff
path: root/fs/ntfs3
AgeCommit message (Collapse)AuthorFilesLines
2026-07-01iomap: consolidate bio submissionChristoph Hellwig1-4/+1
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>
2026-06-24Merge tag 'ntfs3_for_7.2' of ↵Linus Torvalds13-302/+590
https://github.com/Paragon-Software-Group/linux-ntfs3 Pull ntfs3 updates from Konstantin Komarov: "Added: - depth limit to indx_find_buffer() to prevent stack overflow - validate split-point offset in indx_insert_into_buffer() - bounds check to run_get_highest_vcn() - fileattr_get() and fileattr_set() support - zero stale pagecache beyond valid data length - handle delayed allocation overlap in run lookup - validate lcns_follow in log_replay() conversion - cap RESTART_TABLE free-chain walker at rt->used - resize log->one_page_buf when adopting on-disk page size - reject direct userspace writes to reserved $LX* xattrs Fixed: - out-of-bounds read in decompress_lznt() - avoid -Wmaybe-uninitialized warnings - hold ni_lock across readdir metadata walk - preserve non-DOS attribute bits in system.dos_attrib - validate index entry key bounds - syncing wrong inode on DIRSYNC cross-directory rename - validate Dirty Page Table capacity in log_replay() copy_lcns - wrong LCN in run_remove_range() when splitting a run - allocate iomap inline_data using alloc_page - mount failure on 64K page-size kernels - out-of-bounds read in ntfs_dir_emit() and hdr_find_e() - bound attr_off in UpdateResidentValue against data_off - bound DeleteIndexEntryAllocation memmove length - bound copy_lcns dp->page_lcns[] index in analysis pass - bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation} - prevent potential lcn remains uninitialized Changed: - bound to_move in indx_insert_into_root() before hdr_insert_head() - call _ntfs_bad_inode() when failing to rename - fold resident writeback into writepages loop - force waiting for direct I/O completion - fold file size handling into ntfs_set_size() - reject SEEK_DATA and SEEK_HOLE past EOF early - format code, add descriptive comments and remove non-useful" * tag 'ntfs3_for_7.2' of https://github.com/Paragon-Software-Group/linux-ntfs3: (34 commits) ntfs3: reject direct userspace writes to reserved $LX* xattrs fs/ntfs3: resize log->one_page_buf when adopting on-disk page size fs/ntfs3: prevent potential lcn remains uninitialized ntfs3: cap RESTART_TABLE free-chain walker at rt->used fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation} fs/ntfs3: validate lcns_follow in log_replay conversion fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis pass fs/ntfs3: bound DeleteIndexEntryAllocation memmove length fs/ntfs3: bound attr_off in UpdateResidentValue against data_off ntfs3: fix out-of-bounds read in ntfs_dir_emit() and hdr_find_e() fs/ntfs3: fix mount failure on 64K page-size kernels ntfs3: avoid another -Wmaybe-uninitialized warning ntfs3: Allocate iomap inline_data using alloc_page fs/ntfs3: format code, deal with comments fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF early fs/ntfs3: fold file size handling into ntfs_set_size() fs/ntfs3: force waiting for direct I/O completion fs/ntfs3: fold resident writeback into writepages loop fs/ntfs3: handle delayed allocation overlap in run lookup fs/ntfs3: zero stale pagecache beyond valid data length ...
2026-06-15Merge tag 'vfs-7.2-rc1.misc' of ↵Linus Torvalds1-2/+6
git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs Pull misc vfs updates from Christian Brauner: "Features: - Reduce pipe->mutex contention by pre-allocating pages outside the lock in anon_pipe_write(). anon_pipe_write() called alloc_page() once per page while holding pipe->mutex. The allocation can sleep doing direct reclaim and runs memcg charging, which extends the critical section and stalls any concurrent reader on the same mutex. Now up to 8 pages are pre-allocated before the mutex is taken, leftovers are recycled into the per-pipe tmp_page[] cache before unlock, and any remainder is released after unlock, keeping the allocator out of the critical section on both sides. On a writers x readers sweep with 64KB writes against a 1 MB pipe throughput improves 6-28% and average write latency drops 5-22%; under memory pressure - when the cost of holding the mutex across reclaim is highest - throughput improves 21-48% and latency drops 17-33%. The microbenchmark is added to selftests. - uaccess/sockptr: fix the ignored_trailing logic in copy_struct_to_user() to behave as documented and the usize check in copy_struct_from_sockptr() for user pointers, and add copy_struct_{from,to}_bounce_buffer() and copy_struct_to_sockptr() helpers for upcoming users (IPPROTO_SMBDIRECT, IPPROTO_QUIC). - bpf: add a sleepable bpf_real_inode() kfunc that resolves the real inode backing a dentry via d_real_inode(). On overlayfs the inode attached to the dentry doesn't carry the underlying device information; this is used by the filesystem restriction BPF program that was merged into systemd. - docs: add guidelines for submitting new filesystems, motivated by the maintenance burden abandoned and untestable filesystems impose on VFS developers, blocking infrastructure work like folio conversions and iomap migration. Fixes: - libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo() and drop the now-redundant assignments in callers. This began as a one-line dma-buf fix for a path_noexec() warning; a pseudo filesystem has no reason not to set SB_I_NOEXEC. All init_pseudo() callers were audited: the only visible effect is on dma-buf where SB_I_NOEXEC silences the warning. - Handle set_blocksize() failures in legacy filesystems (bfs, hpfs, qnx4, jfs, befs, affs, isofs, minix, ntfs3, omfs). Mounting a device with a sector size > PAGE_SIZE crashed roughly half of them; the rest had the same missing error handling pattern. Plus a follow-up releasing the superblock buffer_head when setting the minix v3 block size fails. - mount: honour SB_NOUSER in the new mount API. - fs/fcntl: fix a SOFTIRQ-unsafe lock order in fasync signaling by switching the process-group paths of send_sigio() and send_sigurg() from read_lock(&tasklist_lock) to RCU, matching the single-PID path. - vfs: add an FS_USERNS_DELEGATABLE flag and set it for NFS, fixing delegated NFS mounts (fsopen() in a container with the mount performed by a privileged daemon) that broke when non-init s_user_ns was tied to FS_USERNS_MOUNT. - selftests/namespaces: fix a hang in nsid_test where an unreaped grandchild kept the TAP pipe write-end open, a waitpid(-1) race in listns_efault_test, and a false FAIL on kernels without listns() where the tests should SKIP. - filelock: fix the break_lease() stub signature for CONFIG_FILE_LOCKING=n. - init/initramfs_test: wait for the async initramfs unpacking before running; the test and do_populate_rootfs() share the parser state. - fs/coredump: reduce redundant log noise in validate_coredump_safety(). - iomap: pass the correct length to fserror_report_io() in __iomap_write_begin(). - backing-file: fix the backing_file_open() kerneldoc. Cleanups: - initramfs: refactor the cpio hex header parsing to use hex2bin() instead of the hand-rolled simple_strntoul() which is reverted, and extend the initramfs KUnit tests to cover header fields with 0x prefixes. - Replace __get_free_pages() and friends with kmalloc()/kzalloc() across quota, proc, ocfs2/dlm, nilfs2, nfs, nfsd, libfs, jfs, jbd2, isofs, fuse, select, namespace, configfs, binfmt_misc, bfs, and the do_mounts init code - part of the larger work of replacing page allocator calls with kmalloc(). - Use clear_and_wake_up_bit() in unlock_buffer() and journal_end_buffer_io_sync() instead of open-coding the sequence. - Drop unused VFS exports: unexport drop_super_exclusive(), remove start_removing_user_path_at(), and fold __start_removing_path() into start_removing_path(). - fs/read_write: narrow the __kernel_write() export with EXPORT_SYMBOL_FOR_MODULES(). - vfs: uapi: retire octal and hex constants in favor of (1 << n) for the O_ flags. Finding a free bit for a new flag across the architectures was needlessly hard with the mixed bases. - dcache: add extra sanity checks of dead dentries in dentry_free() via a new DENTRY_WARN_ONCE() that also prints d_flags. - iov_iter: use kmemdup_array() in dup_iter() to harden the allocation against multiplication overflow. - fs/pipe: write to ->poll_usage only once. - vfs: remove an always-taken if-branch in find_next_fd(). - dcache: use kmalloc_flex() for struct external_name in __d_alloc(). - namei: use QSTR() instead of QSTR_INIT() in path_pts(). - sync_file_range: delete dead S_ISLNK code. - Comment fixes: retire a stale comment in fget_task_next() and fix assorted spelling mistakes" * tag 'vfs-7.2-rc1.misc' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (73 commits) backing-file: fix backing_file_open() kerneldoc parameter iomap: pass the correct len to fserror_report_io in __iomap_write_begin vfs: add FS_USERNS_DELEGATABLE flag and set it for NFS filelock: fix break_lease() stub signature for CONFIG_FILE_LOCKING=n vfs: uapi: retire octal and hex numbers in favor of (1 << n) for O_ flags bpf: add bpf_real_inode() kfunc fs/read_write: Do not export __kernel_write() to the entire world libfs: drop redundant SB_I_NOEXEC/SB_I_NODEV in init_pseudo() callers libfs: set SB_I_NOEXEC and SB_I_NODEV by default in init_pseudo() mount: honour SB_NOUSER in the new mount API fs/fcntl: fix SOFTIRQ-unsafe lock order in fasync signaling selftests/pipe: add pipe_bench microbenchmark fs/pipe: pre-allocate pages outside pipe->mutex in anon_pipe_write fs: retire stale comment in fget_task_next() fs: fix spelling mistakes in comment bfs: replace get_zeroed_page() with kzalloc() binfmt_misc: replace __get_free_page() with kmalloc() configfs: replace __get_free_pages() with kzalloc() fs/namespace: use __getname() to allocate mntpath buffer fs/select: replace __get_free_page() with kmalloc() ...
2026-06-10ntfs3: reject direct userspace writes to reserved $LX* xattrsKonstantin Komarov1-0/+12
NTFS3 uses $LXUID, $LXGID, $LXMOD and $LXDEV as internal WSL permission metadata and reloads them into i_uid, i_gid and i_mode from ntfs_get_wsl_perm(). Because the empty-prefix xattr handler also lets file owners call setxattr() on these names directly, an unprivileged writer on a writable ntfs3 mount can plant root ownership and S_ISUID on their own file and gain euid 0 after inode reload. Reject direct userspace writes to the reserved $LX* names. Internal ntfs3 metadata updates are unchanged because ntfs_save_wsl_perm() writes them via ntfs_set_ea() directly. Signed-off-by: Zhen Yan <sdjasjbuaa@gmail.com> [almaz.alexandrovich@paragon-software.com: added an additional check for non privileged users] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-10fs/ntfs3: resize log->one_page_buf when adopting on-disk page sizeJamie Nguyen1-0/+19
log_replay() allocates log->one_page_buf using the page size that was chosen from the host PAGE_SIZE: log->one_page_buf = kmalloc(log->page_size, GFP_NOFS); Later, when a restart area is found, the log page size recorded on disk is adopted: t32 = le32_to_cpu(log->rst_info.r_page->sys_page_size); if (log->page_size != t32) { log->l_size = log->orig_file_size; log->page_size = norm_file_page(t32, &log->l_size, t32 == DefaultLogPageSize); } If the on-disk page size is larger than the size used for the initial allocation, log->page_size grows but one_page_buf is left at its original, smaller size. A subsequent unaligned read_log_page() then reads log->page_size bytes into the undersized scratch buffer: page_buf = page_off ? log->one_page_buf : *buffer; err = ntfs_read_run_nb_ra(ni->mi.sbi, &ni->file.run, page_vbo, page_buf, log->page_size, NULL, &log->read_ahead); overflowing the allocation. This is reachable when mounting a dirty NTFS volume whose log was formatted with a page size larger than the buffer initially allocated on the mounting host (for example a 64K-log volume mounted on a host that allocated a 4K scratch buffer). Grow one_page_buf when the adopted on-disk page size exceeds the size used for the initial allocation. On krealloc() failure the original buffer is left intact and freed by the existing error path. Fixes: b46acd6a6a627 ("fs/ntfs3: Add NTFS journal") Reported-by: Carol L Soto <csoto@nvidia.com> Signed-off-by: Jamie Nguyen <jamien@nvidia.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-10fs/ntfs3: prevent potential lcn remains uninitializedEdward Adam Davis1-4/+5
The target VCN being sought was not found within runs[0], causing run_lookup() to return false. This causes run_lookup_entry() to return false, which in turn results in a len value of 0, and the new parameter passed to attr_data_get_block() is NULL. Collectively, these factors ultimately cause attr_data_get_block_locked() to exit prematurely without initializing lcn, thereby triggering [1]. To prevent [1], the clen check within ni_seek_data_or_hole() has been moved to occur before the lcn check. [1] BUG: KMSAN: uninit-value in ni_seek_data_or_hole+0x24f/0x5f0 fs/ntfs3/frecord.c:2862 ni_seek_data_or_hole+0x24f/0x5f0 fs/ntfs3/frecord.c:2862 ntfs_llseek+0x22a/0x4a0 fs/ntfs3/file.c:1530 vfs_llseek fs/read_write.c:391 [inline] Fixes: c61326967728 ("fs/ntfs3: implement llseek SEEK_DATA/SEEK_HOLE by scanning data runs") Reported-by: syzbot+c2cfe997245202e46f10@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=c2cfe997245202e46f10 Signed-off-by: Edward Adam Davis <eadavis@qq.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-09ntfs3: cap RESTART_TABLE free-chain walker at rt->usedMichael Bommarito1-1/+12
A crafted NTFS3 disk image triggers an in-kernel infinite loop at mount time, hanging the mounting thread and firing the soft-lockup watchdog within ~22s on multi-CPU hosts (panic with kernel.softlockup_panic=1). The bug is reachable from desktop USB auto-mount on distributions where udisks2 routes the NTFS signature to the in-tree ntfs3 driver (Arch family and an increasing fraction of Fedora / openSUSE / RHEL deployments); CAP_SYS_ADMIN-class manual mount elsewhere. check_rstbl()'s second walker iterates the free-entry singly-linked list headed by rt->first_free with no upper bound on iteration count: for (off = ff; off;) { if (off == RESTART_ENTRY_ALLOCATED) return false; off = le32_to_cpu(*(__le32 *)Add2Ptr(rt, off)); if (off > ts - sizeof(__le32)) return false; } The existing guards cover three exits: end-of-list (off == 0), the in-use marker (off == RESTART_ENTRY_ALLOCATED), and out-of-bounds (off > ts - sizeof(__le32)). None of the three prevents an in-bounds cycle. A crafted on-disk RESTART_TABLE whose free chain contains a self-loop or A->B->A cycle whose offsets satisfy: - in range [sizeof(struct RESTART_TABLE), ts - sizeof(__le32)] - (off - sizeof(struct RESTART_TABLE)) % rsize == 0 passes all existing guards and spins the mount-time thread forever. Reproduced in UML by hand-forging a 2 MB NTFS3 image whose journal RESTART_TABLE first_free = 0x18 and whose entry at offset 0x18 stores 0x18 as its next pointer; mount of the forged image with the in-tree ntfs3 driver never returns. Bound the walker by rt->used. Each entry on a legitimate free chain is unique, and the total slot count is ne = le16_to_cpu (rt->used). A traversal that visits more than ne slots is by construction malformed; reject it as a corrupt RESTART_TABLE. After this patch, mount of the forged image returns with -EINVAL and a log_replay failure message, and mkntfs-produced legitimate images mount cleanly (verified in the same UML harness). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-09fs/ntfs3: bound NTFS_DE view.data_off in UpdateRecordData{Root,Allocation}Michael Bommarito1-0/+18
In do_action()'s UpdateRecordDataRoot (fslog.c:3489) and UpdateRecordDataAllocation (fslog.c:3697) cases, the memmove destination is `Add2Ptr(e, le16_to_cpu(e->view.data_off))`, where e->view.data_off comes from an on-disk NTFS_DE inside an INDEX_ROOT or INDEX_BUFFER. Neither case validates view.data_off + dlen against e->size; the existing check_if_index_root / check_if_alloc_index helpers walk the entry chain and validate the entry's offset, but not its internal view fields. The neighbouring read sites (e.g., fs/ntfs3/index.c when iterating view entries) check view.data_off + view.data_size <= e->size. Apply the same bound at the two memmove sites. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe instrumentation: with view.data_off forced to 0xFFFC, the memmove writes 32 bytes past the end of the NTFS_DE. This is similar in shape to Pavitra Jha's 2026-05-02 patch "fs/ntfs3: prevent oob in case UpdateRecordDataRoot" (<20260502105008.21827-1-jhapavitra98@gmail.com>) which proposes calling ntfs3_bad_de_range(); that helper does not exist in mainline. This patch uses inline checks. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Reported-by: Pavitra Jha <jhapavitra98@gmail.com> Closes: https://lore.kernel.org/ntfs3/20260502105008.21827-1-jhapavitra98@gmail.com/ Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-03fs/ntfs3: validate lcns_follow in log_replay conversionKonstantin Komarov1-3/+16
log_replay() converts DIR_PAGE_ENTRY_32 records into DIR_PAGE_ENTRY records when replaying version 0 restart tables. During this conversion, the memmove() length is derived directly from the on-disk lcns_follow field: memmove(&dp->vcn, &dp0->vcn_low, 2 * sizeof(u64) + le32_to_cpu(dp->lcns_follow) * sizeof(u64)); check_rstbl() validates restart table structure, but does not constrain per-entry lcns_follow values relative to the entry size. A malformed filesystem image can provide an oversized lcns_follow value, causing the conversion memmove() to access memory beyond the bounds of the allocated restart table buffer. The same field is later used to bound iteration over page_lcns[], so validating lcns_follow during conversion also prevents downstream out-of-bounds access from the same malformed metadata. Compute the maximum valid lcns_follow from the already-validated restart table entry size and reject entries that exceed this bound. Reuse the existing t16/t32 scratch variables already declared in log_replay() to avoid introducing new declarations. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Pavitra Jha <jhapavitra98@gmail.com> [almaz.alexandrovich@paragon-software.com: fixed the conflicts] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-03fs/ntfs3: bound copy_lcns dp->page_lcns[] index in analysis passMichael Bommarito1-17/+29
In log_replay()'s analysis pass, after find_dp() returns a valid DIR_PAGE_ENTRY for the (target_attr, target_vcn) tuple, the copy_lcns block walks lrh->lcns_follow further entries: t16 = le16_to_cpu(lrh->lcns_follow); for (i = 0; i < t16; i++) { size_t j = (size_t)(le64_to_cpu(lrh->target_vcn) - le64_to_cpu(dp->vcn)); dp->page_lcns[j + i] = lrh->page_lcns[i]; } find_dp() only validates that target_vcn falls within [dp->vcn, dp->vcn + dp->lcns_follow), i.e., that the FIRST cluster is covered. The walk through the further entries is not bounded against dp->lcns_follow. For a malformed LRH where target_vcn = dp->vcn + dp->lcns_follow - 1 and lrh->lcns_follow > 1, the i > 0 writes overflow the dp's allocated page_lcns[] array. Add the missing j + lrh->lcns_follow <= dp->lcns_follow guard. Reproduced under UML+KASAN on mainline 8d90b09e6741 as a slab-out-of-bounds write of size 8 from log_replay+0x68d4 on the mount path. This is distinct from Pavitra Jha's 2026-05-02 patch ("fs/ntfs3: validate lcns_follow in log_replay conversion", <20260502154252.164586-1-jhapavitra98@gmail.com>) which addresses the separate version-0 dirty-page-table conversion path's memmove(&dp->vcn, ...) call. The two fixes are complementary; both should land. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> [almaz.alexandrovich@paragon-software.com: clang-formatted the changes, fixed conflicts] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-03fs/ntfs3: bound DeleteIndexEntryAllocation memmove lengthKonstantin Komarov1-1/+15
In do_action()'s DeleteIndexEntryAllocation case, e->size comes from an on-disk INDEX_BUFFER entry. When e->size makes e + e->size point past hdr + hdr->used, PtrOffset(e1, Add2Ptr(hdr, used)) returns a negative ptrdiff_t that is silently cast to a quasi-infinite size_t when passed to memmove(). The memmove then walks past the destination buffer. The sibling DeleteIndexEntryRoot case at fslog.c:3540-3543 already carries the corresponding guard: if (PtrOffset(e1, Add2Ptr(hdr, used)) < esize || Add2Ptr(e, esize) > Add2Ptr(lrh, rec_len) || used + esize > le32_to_cpu(hdr->total)) { goto dirty_vol; } Apply the same shape to the allocation-path case. Also reject esize == 0: memmove(e, e, ...) is a no-op and leaves hdr->used unchanged, hiding a malformed entry from the existing check_index_header() walk. Reproduced under UML+KASAN on mainline 8d90b09e6741 by mounting a crafted NTFS image: the unguarded memmove takes a length of 0xffffffffffffff00 and the kernel oopses in memmove+0x81/0x1a0 on the do_action+0x36a2 frame. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-03fs/ntfs3: bound attr_off in UpdateResidentValue against data_offKonstantin Komarov1-0/+11
In do_action()'s UpdateResidentValue case (fslog.c:3307), lrh->attr_off and lrh->redo_len come from the on-disk LRH. When they satisfy aoff + dlen < attr->res.data_off, the assignment attr->res.data_size = cpu_to_le32(aoff + dlen - data_off); underflows to ~4 GiB (e.g. 0xFFFFFFF9 when aoff=0x10, dlen=1, data_off=0x18). Subsequent code that reads attr->res.data_size to walk the resident attribute payload would then read up to 4 GiB past the 1024-byte MFT record allocation. The existing mi_enum_attr() defense in fs/ntfs3/record.c:287 catches the corrupted data_size on the next attribute walk and fails the mount, but only on the path that walks all attributes. A read site that picks an attribute by name and reads its data_size without re-validating is not covered. Validate aoff against data_off and asize at the source. Reproduced under UML+KASAN on mainline 8d90b09e6741 via pr_warn-only probe: with aoff=0x10 and data_off=0x18, the post-assignment data_size is 0xfffffff9 (mount then fails at -22 from mi_enum_attr). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> [almaz.alexandrovich@paragon-software.com: clang-formatted the changes] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02ntfs3: fix out-of-bounds read in ntfs_dir_emit() and hdr_find_e()Alessandro Schino2-1/+7
The bounds check in ntfs_dir_emit() compares fname->name_len (a character count) against e->size (a byte count) without accounting for the 2-byte-per-character UTF-16LE encoding or the ATTR_FILE_NAME header size: if (fname->name_len + sizeof(struct NTFS_DE) > le16_to_cpu(e->size)) This computes: name_len + 16 > e_size The correct check must account for the ATTR_FILE_NAME header (66 bytes before the name) and the UTF-16LE character size (2 bytes each): sizeof(NTFS_DE) + offsetof(ATTR_FILE_NAME, name) + name_len * sizeof(short) > e_size Which computes: 16 + 66 + name_len * 2 > e_size The correct calculation already exists as fname_full_size() in ntfs.h and is used in cmp_fnames(), namei.c, and fslog.c, but was not used in the readdir path. A crafted NTFS image with an index entry containing a small e->size but large fname->name_len bypasses the current check, causing ntfs_utf16_to_nls() to read past the entry boundary. Additionally, add a key_size validation in hdr_find_e() to ensure the declared key_size does not exceed the available entry data, preventing comparison functions from reading past entry boundaries on the lookup path. Signed-off-by: Alessandro Schino <7991aleschino@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: fix mount failure on 64K page-size kernelsJamie Nguyen1-5/+1
On 64K page-size kernels, mounting NTFS volumes smaller than ~650 MB fails with EINVAL. The issue is in log_replay(): the initial log page size probe uses PAGE_SIZE (65536) instead of DefaultLogPageSize (4096) when PAGE_SIZE exceeds DefaultLogPageSize * 2. This makes norm_file_page() require the $LogFile to be at least 50 * 65536 = 3.2 MB, but mkfs.ntfs creates a $LogFile of only ~1.5 MB for a typical 300 MB volume. norm_file_page() returns 0 and the mount is rejected with EINVAL. On 4K kernels the #if guard evaluates to true, so use_default=true is passed and DefaultLogPageSize (4096) is used, requiring only ~200 KB. This path works fine. Fix this by always passing use_default=true, which forces the initial probe to use DefaultLogPageSize regardless of the kernel's PAGE_SIZE. This is safe because, after reading the on-disk restart area, log_replay() already re-adjusts log->page_size to match the volume's actual sys_page_size. Also fix read_log_page() to pass log->page_size instead of PAGE_SIZE to ntfs_fix_post_read(), matching the actual buffer size. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Tested-by: Matthew R. Ochs <mochs@nvidia.com> Signed-off-by: Jamie Nguyen <jamien@nvidia.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02ntfs3: avoid another -Wmaybe-uninitialized warningArnd Bergmann1-2/+2
The ntfs3 specific -Wmaybe-uninitialized flag found one more false-postive, this time with gcc-10 on s390: fs/ntfs3/frecord.c: In function 'ni_expand_list': fs/ntfs3/frecord.c:1370:16: error: 'ins_attr' may be used uninitialized in this function [-Werror=maybe-uninitialized] Add an explicit NULL pointer check before using the pointer, and initialize it to NULL. Fixes: 48d9b57b169f ("fs/ntfs3: add a subset of W=1 warnings for stricter checks") Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02ntfs3: Allocate iomap inline_data using alloc_pageMihai Brodschi2-5/+9
This fixes a BUG reported in iomap_write_end_inline: iomap_inline_data_valid checks that the inline_data fits within a page. If the inline_data is allocated with kmemdup there's no guarantee that it's page-aligned, so the check sometimes fails. Allocate it with alloc_page to ensure it's page-aligned. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221446 Fixes: 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations") Signed-off-by: Mihai Brodschi <m.brodschi@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: format code, deal with commentsKonstantin Komarov5-25/+23
format code according to .clang-format, add useful comments and remove non-useful comments. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: reject SEEK_DATA and SEEK_HOLE past EOF earlyKonstantin Komarov2-10/+21
Handle non-data/hole seeks through generic_file_llseek_size() and return -ENXIO immediately when SEEK_DATA or SEEK_HOLE is requested at or past EOF. Handle compressed files in such cases properly as well. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: fold file size handling into ntfs_set_size()Konstantin Komarov2-148/+51
Remove the separate ntfs_extend() and ntfs_truncate() helpers and route file size changes through ntfs_set_size(). This consolidates ntfs3 size updates in one place and lets the write, fallocate, and setattr paths share the same logic for updating i_size, valid data length, and preallocated extents. This patch fixes a few issues found during internal tests. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: force waiting for direct I/O completionKonstantin Komarov1-1/+2
It makes ntfs3 wait for direct I/O completion before returning to the caller, instead of allowing the write path to complete asynchronously. The issue was discovered during internal tests. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: fold resident writeback into writepages loopKonstantin Komarov1-27/+15
Remove the separate ntfs_resident_writepage() helper and handle resident writeback directly from ntfs_writepages(). This simplifies the resident writeback path and keeps the folio handling local to ntfs_writepages(). Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: handle delayed allocation overlap in run lookupKonstantin Komarov3-12/+69
Introduce run_lookup_entry_da() to look up data runs while taking delayed allocation into account. ntfs3 may have both committed extents and delayed allocation extents for the same VCN range. The new helper checks delayed allocation first and falls back to the real run, then corrects the returned range when a real run overlaps with a delayed allocation run. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: zero stale pagecache beyond valid data lengthKonstantin Komarov1-2/+28
Zero cached folios beyond the valid data length when closing a writable mapping. This keeps cached data beyond initialized file contents zeroed and prevents stale pagecache exposure after mmap-based writes. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: add fileattr supportKonstantin Komarov4-0/+91
Implement fileattr_get() and fileattr_set() to fix a problem found during the internal testing. This allows ntfs3 to expose and modify inode flags through the generic file attribute interface used by FS_IOC_GETFLAGS and FS_IOC_SETFLAGS. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: call _ntfs_bad_inode() when failing to renameHelen Koike1-2/+2
It is safe to call _ntfs_bad_inode on live inodes since: commit 519b078998ce ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") The WARN_ON was added when it wasn't safe by: commit d99208b91933 ("fs/ntfs3: cancle set bad inode after removing name fails") Replace the WARN_ON with a call to _ntfs_bad_inode() to prevent further operations on the inconsistent inode. Reported-by: syzbot+4d8e30dbafb5c1260479@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=4d8e30dbafb5c1260479 Fixes: 519b078998ce ("fs/ntfs3: Exclude call make_bad_inode for live nodes.") Signed-off-by: Helen Koike <koike@igalia.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: fix wrong LCN in run_remove_range() when splitting a runZhan Xusheng1-1/+4
When run_remove_range() removes a middle portion of a non-sparse run, it splits the run into head and tail parts. The tail is inserted via run_add_entry() but uses the original r->lcn as its starting LCN instead of advancing it by the split offset. For example, removing VCN range [10, 20) from a run {vcn=0, lcn=100, len=30} should produce: {vcn=0, lcn=100, len=10} (head) {vcn=20, lcn=120, len=10} (tail, lcn advanced by 20) But the current code produces: {vcn=0, lcn=100, len=10} {vcn=20, lcn=100, len=10} (wrong: points to same physical clusters) This creates overlapping physical mappings in the in-memory run tree, which can corrupt cluster allocation decisions and lead to data corruption. The correct pattern is already used in run_insert_range(): CLST lcn2 = r->lcn == SPARSE_LCN ? SPARSE_LCN : (r->lcn + len1); Apply the same logic in run_remove_range(). Fixes: 10d7c95af043 ("fs/ntfs3: add delayed-allocation (delalloc) support") Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: validate Dirty Page Table capacity in log_replay copy_lcnsYunpeng Tian1-5/+15
In the analysis pass of $LogFile journal replay, log_replay() copies LCNs from each action log record into an existing Dirty Page Table (DPT) entry without bounding the destination index. A crafted NTFS image with DPT entry lcns_follow=1 and an action log record with lcns_follow=2 produces a kernel slab out-of-bounds write at mount time: BUG: KASAN: slab-out-of-bounds in log_replay+0x654c/0xdb60 Write of size 8 at addr ffff8880095e1040 by task mount Two attacker-controlled fields can drive j+i past the allocated page_lcns[] array: 1. dp->lcns_follow (capacity) can be smaller than lrh->lcns_follow. 2. lrh->target_vcn may be smaller than dp->vcn, making the u64 subtraction wrap to a huge size_t. Validate target VCN delta and per-record LCN count against the DPT entry capacity, bail via the existing out: cleanup label with -EINVAL. This mirrors the bounds-check pattern added in commit b2bc7c44ed17 ("fs/ntfs3: Fix slab-out-of-bounds read in DeleteIndexEntryRoot") and commit 0ca0485e4b2e ("fs/ntfs3: validate rec->used in journal-replay file record check"). Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Reported-by: Yunpeng Tian <shionthanatos@gmail.com> Reported-by: Mingda Zhang <npczmd@qq.com> Reported-by: Gongming Wang <gmwgg05@gmail.com> Reported-by: Peiyuan Xu <paulbucket12@gmail.com> Reported-by: Qinrun Dai <jupmouse@gmail.com> Cc: stable@vger.kernel.org Signed-off-by: Yunpeng Tian <shionthanatos@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: fix syncing wrong inode on DIRSYNC cross-directory renameZhan Xusheng1-1/+1
In ntfs3_rename(), when IS_DIRSYNC(new_dir) is true, the code syncs the renamed file inode instead of the target directory new_dir: if (IS_DIRSYNC(new_dir)) ntfs_sync_inode(inode); /* should be new_dir */ DIRSYNC requires that directory metadata changes are written to disk synchronously. Since new_dir was modified (a new directory entry was added), it is new_dir that must be synced to satisfy the guarantee, not the renamed file itself. This bug has existed since the initial ntfs3 implementation and was carried through the refactoring in commit 78ab59fee07f ("fs/ntfs3: Rework file operations"). Fix by syncing new_dir instead of inode. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Cc: stable@vger.kernel.org Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: validate index entry key boundsZhengYuan Huang2-7/+56
[BUG] A malformed NTFS directory index entry can advertise a key_size larger than the bytes actually present in its NTFS_DE payload. Directory lookup then passes that malformed key to cmp_fnames(), which can read past the end of the kmalloc'ed index buffer. BUG: KASAN: slab-out-of-bounds in fname_full_size fs/ntfs3/ntfs.h:590 [inline] BUG: KASAN: slab-out-of-bounds in cmp_fnames+0x1ea/0x230 fs/ntfs3/index.c:46 Read of size 1 at addr ffff88801c313018 by task syz.6.3365/9279 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xbe/0x130 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xd1/0x650 mm/kasan/report.c:482 kasan_report+0xfb/0x140 mm/kasan/report.c:595 __asan_report_load1_noabort+0x14/0x30 mm/kasan/report_generic.c:378 fname_full_size fs/ntfs3/ntfs.h:590 [inline] cmp_fnames+0x1ea/0x230 fs/ntfs3/index.c:46 hdr_find_e.isra.0+0x3ed/0x670 fs/ntfs3/index.c:762 indx_find+0x4b5/0x900 fs/ntfs3/index.c:1186 dir_search_u+0x2c0/0x460 fs/ntfs3/dir.c:254 ntfs_lookup+0x1cc/0x2a0 fs/ntfs3/namei.c:85 __lookup_slow+0x241/0x450 fs/namei.c:1816 lookup_slow fs/namei.c:1833 [inline] walk_component+0x31c/0x570 fs/namei.c:2151 link_path_walk+0x592/0xd60 fs/namei.c:2519 path_lookupat+0x138/0x660 fs/namei.c:2675 filename_lookup+0x1f3/0x560 fs/namei.c:2705 filename_setxattr+0xad/0x1c0 fs/xattr.c:660 path_setxattrat+0x1d8/0x280 fs/xattr.c:713 __do_sys_lsetxattr fs/xattr.c:754 [inline] __se_sys_lsetxattr fs/xattr.c:750 [inline] __x64_sys_lsetxattr+0xd0/0x150 fs/xattr.c:750 ... Allocated by task 9279: kasan_save_stack+0x39/0x70 mm/kasan/common.c:56 kasan_save_track+0x14/0x40 mm/kasan/common.c:77 kasan_save_alloc_info+0x37/0x60 mm/kasan/generic.c:573 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0xc3/0xd0 mm/kasan/common.c:417 kasan_kmalloc include/linux/kasan.h:262 [inline] __do_kmalloc_node mm/slub.c:5650 [inline] __kmalloc_noprof+0x2bd/0x900 mm/slub.c:5662 kmalloc_noprof include/linux/slab.h:961 [inline] indx_read+0x41d/0xad0 fs/ntfs3/index.c:1059 indx_find+0x447/0x900 fs/ntfs3/index.c:1179 dir_search_u+0x2c0/0x460 fs/ntfs3/dir.c:254 ntfs_lookup+0x1cc/0x2a0 fs/ntfs3/namei.c:85 __lookup_slow+0x241/0x450 fs/namei.c:1816 lookup_slow fs/namei.c:1833 [inline] walk_component+0x31c/0x570 fs/namei.c:2151 link_path_walk+0x592/0xd60 fs/namei.c:2519 path_lookupat+0x138/0x660 fs/namei.c:2675 filename_lookup+0x1f3/0x560 fs/namei.c:2705 filename_setxattr+0xad/0x1c0 fs/xattr.c:660 path_setxattrat+0x1d8/0x280 fs/xattr.c:713 __do_sys_lsetxattr fs/xattr.c:754 [inline] __se_sys_lsetxattr fs/xattr.c:750 [inline] __x64_sys_lsetxattr+0xd0/0x150 fs/xattr.c:750 ... [CAUSE] The index-header validators only validated INDEX_HDR-level geometry. They did not walk each NTFS_DE to verify entry alignment, subnode layout, or that key_size fit inside the entry payload. They also allowed a last sentinel entry to carry a non-zero key_size. [FIX] Walk every NTFS_DE in ntfs3's index-header validators and reject entries with invalid layout, mismatched subnode state, oversized key_size, or non-zero sentinel keys before lookup or log replay can consume them. Signed-off-by: ZhengYuan Huang <gality369@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: preserve non-DOS attribute bits in system.dos_attribZhengYuan Huang1-1/+3
[BUG] A corrupted ntfs3 image can hit a NULL function pointer call in generic_perform_write() after toggling system.ntfs_attrib and then overwriting system.dos_attrib on the same file. BUG: kernel NULL pointer dereference, address: 0000000000000000 \#PF: supervisor instruction fetch in kernel mode \#PF: error_code(0x0010) - not-present page PGD bed5067 P4D bed5067 PUD 0 Oops: Oops: 0010 [#1] SMP KASAN NOPTI RIP: 0010:0x0 Code: Unable to access opcode bytes at 0xffffffffffffffd6. RSP: 0018:ffff88801025f988 EFLAGS: 00010246 Call Trace: generic_perform_write+0x409/0x8c0 mm/filemap.c:4255 __generic_file_write_iter+0x1bb/0x200 mm/filemap.c:4372 ntfs_file_write_iter+0xcd9/0x1c20 fs/ntfs3/file.c:1253 new_sync_write fs/read_write.c:593 [inline] vfs_write+0x63b/0xf70 fs/read_write.c:686 ksys_write+0x133/0x250 fs/read_write.c:738 __do_sys_write fs/read_write.c:749 [inline] __se_sys_write fs/read_write.c:746 [inline] __x64_sys_write+0x77/0xc0 fs/read_write.c:746 ... [CAUSE] system.ntfs_attrib updates ATTR_DATA flags via ni_new_attr_flags() and switches i_mapping->a_ops to ntfs_aops_cmpr when FILE_ATTRIBUTE_COMPRESSED is set. system.dos_attrib then overwrites ni->std_fa from a one-byte DOS attribute value, clearing the compression bit without updating ATTR_DATA or the mapping operations. Old buffered writes use is_compressed(ni) to choose __generic_file_write_iter(). That leaves generic_perform_write() calling a NULL write_begin callback from ntfs_aops_cmpr. [FIX] Treat system.dos_attrib as a low-byte DOS attribute update and preserve the existing non-DOS attribute bits in ni->std_fa. This keeps compressed and sparse state consistent with ATTR_DATA and the mapping operations while keeping the existing DOS attribute semantics intact. Signed-off-by: ZhengYuan Huang <gality369@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: hold ni_lock across readdir metadata walkZhengYuan Huang1-6/+16
[BUG] KASAN reports a slab-use-after-free during getdents(2): BUG: KASAN: slab-use-after-free in ntfs_read_mft fs/ntfs3/inode.c:79 [inline] BUG: KASAN: slab-use-after-free in ntfs_iget5+0x59b/0x3450 fs/ntfs3/inode.c:541 Read of size 2 at addr ffff88800b7a5a4e by task syz.0.1061/2354 Call Trace: __dump_stack lib/dump_stack.c:94 [inline] dump_stack_lvl+0xbe/0x130 lib/dump_stack.c:120 print_address_description mm/kasan/report.c:378 [inline] print_report+0xd1/0x650 mm/kasan/report.c:482 kasan_report+0xfb/0x140 mm/kasan/report.c:595 __asan_report_load2_noabort+0x14/0x30 mm/kasan/report_generic.c:379 ntfs_read_mft fs/ntfs3/inode.c:79 [inline] ntfs_iget5+0x59b/0x3450 fs/ntfs3/inode.c:541 ntfs_dir_emit fs/ntfs3/dir.c:337 [inline] ntfs_read_hdr+0x714/0x930 fs/ntfs3/dir.c:385 ntfs_readdir+0xaad/0x1010 fs/ntfs3/dir.c:458 iterate_dir+0x276/0x9e0 fs/readdir.c:108 __do_sys_getdents fs/readdir.c:326 [inline] __se_sys_getdents fs/readdir.c:312 [inline] __x64_sys_getdents+0x143/0x290 fs/readdir.c:312 ... Allocated by task 2160: kasan_save_stack+0x39/0x70 mm/kasan/common.c:56 kasan_save_track+0x14/0x40 mm/kasan/common.c:77 kasan_save_alloc_info+0x37/0x60 mm/kasan/generic.c:573 poison_kmalloc_redzone mm/kasan/common.c:400 [inline] __kasan_kmalloc+0xc3/0xd0 mm/kasan/common.c:417 kasan_kmalloc include/linux/kasan.h:262 [inline] __do_kmalloc_node mm/slub.c:5650 [inline] __kmalloc_noprof+0x2bd/0x900 mm/slub.c:5662 kmalloc_noprof include/linux/slab.h:961 [inline] mi_init+0x9d/0x110 fs/ntfs3/record.c:105 mi_format_new+0x6b/0x500 fs/ntfs3/record.c:422 ni_add_subrecord+0x129/0x540 fs/ntfs3/frecord.c:321 ntfs_look_free_mft+0x238/0xd90 fs/ntfs3/fsntfs.c:715 ni_create_attr_list+0x8e6/0x1690 fs/ntfs3/frecord.c:826 ni_ins_attr_ext+0x5ec/0x9d0 fs/ntfs3/frecord.c:924 ni_insert_attr+0x2bf/0x830 fs/ntfs3/frecord.c:1091 ni_insert_resident+0xec/0x3d0 fs/ntfs3/frecord.c:1475 ni_add_name+0x4b2/0x8a0 fs/ntfs3/frecord.c:2987 ni_rename+0xa6/0x160 fs/ntfs3/frecord.c:3026 ntfs_rename+0xa19/0xe00 fs/ntfs3/namei.c:332 vfs_rename+0xd42/0x1d50 fs/namei.c:5216 do_renameat2+0x715/0xb60 fs/namei.c:5364 __do_sys_rename fs/namei.c:5411 [inline] __se_sys_rename fs/namei.c:5409 [inline] __x64_sys_rename+0x83/0xb0 fs/namei.c:5409 x64_sys_call+0x8c4/0x26a0 arch/x86/include/generated/asm/syscalls_64.h:83 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline] do_syscall_64+0x93/0xf80 arch/x86/entry/syscall_64.c:94 entry_SYSCALL_64_after_hwframe+0x76/0x7e Freed by task 85: kasan_save_stack+0x39/0x70 mm/kasan/common.c:56 kasan_save_track+0x14/0x40 mm/kasan/common.c:77 __kasan_save_free_info+0x3b/0x60 mm/kasan/generic.c:587 kasan_save_free_info mm/kasan/kasan.h:406 [inline] poison_slab_object mm/kasan/common.c:252 [inline] __kasan_slab_free+0x6f/0xa0 mm/kasan/common.c:284 kasan_slab_free include/linux/kasan.h:234 [inline] slab_free_hook mm/slub.c:2543 [inline] slab_free mm/slub.c:6642 [inline] kfree+0x2bf/0x6b0 mm/slub.c:6849 mi_clear fs/ntfs3/ntfs_fs.h:1107 [inline] mi_put+0x10e/0x1a0 fs/ntfs3/record.c:97 ni_write_inode+0x479/0x2a00 fs/ntfs3/frecord.c:3320 ntfs3_write_inode+0x51/0x70 fs/ntfs3/inode.c:1042 write_inode fs/fs-writeback.c:1564 [inline] __writeback_single_inode+0x8c9/0xc30 fs/fs-writeback.c:1784 writeback_sb_inodes+0x5e6/0xf60 fs/fs-writeback.c:2015 __writeback_inodes_wb+0x10c/0x2d0 fs/fs-writeback.c:2086 wb_writeback+0x63f/0x900 fs/fs-writeback.c:2197 wb_check_old_data_flush fs/fs-writeback.c:2301 [inline] wb_do_writeback fs/fs-writeback.c:2354 [inline] wb_workfn+0x8cc/0xd60 fs/fs-writeback.c:2382 process_one_work+0x8e0/0x1980 kernel/workqueue.c:3263 process_scheduled_works kernel/workqueue.c:3346 [inline] worker_thread+0x683/0xf80 kernel/workqueue.c:3427 kthread+0x3f0/0x850 kernel/kthread.c:463 ret_from_fork+0x50f/0x610 arch/x86/kernel/process.c:158 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245 The faulting address sits 590 bytes inside a freed kmalloc-1k object allocated by ni_add_subrecord() and freed from ni_write_inode() writeback. [CAUSE] ntfs_readdir() loads all subrecords once, but then drops ni_lock() before it starts walking the directory metadata through ntfs_read_hdr(). That leaves the current NTFS_DE pointer backed by parent-directory subrecord memory that concurrent writeback is still allowed to compact and free. The later ntfs_dir_emit() -> ntfs_iget5() call exposes the stale e->ref, but the lifetime bug starts earlier: readdir is still consuming parent-directory metadata after releasing the lock that protects it. [FIX] Keep ni_lock() held from the point where ntfs_readdir() starts consuming the directory metadata until the walk over root/index entries is finished. This closes the parent-directory lifetime hole directly and keeps the existing readdir d_type behaviour unchanged. Signed-off-by: ZhengYuan Huang <gality369@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02ntfs3: avoid -Wmaybe-uninitialized warningArnd Bergmann1-0/+1
This warning shows up with gcc-10 now: In file included from fs/ntfs3/index.c:15: fs/ntfs3/index.c: In function 'indx_add_allocate': fs/ntfs3/ntfs_fs.h:463:9: error: 'bmp_size' may be used uninitialized in this function [-Werror=maybe-uninitialized] 463 | return attr_set_size_ex(ni, type, name, name_len, run, new_size, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 464 | new_valid, keep_prealloc, NULL, false); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ fs/ntfs3/index.c:1498:6: note: 'bmp_size' was declared here 1498 | u64 bmp_size, bmp_size_v; | ^~~~~~~~ The warning does look correct, as the 'out2' label can be reached without initializing bmp_size and bmp_size_v. Initialize these at the same place as bmp. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-06-02fs/ntfs3: add bounds check to run_get_highest_vcn()Konstantin Komarov3