summaryrefslogtreecommitdiff
path: root/fs/ntfs3
AgeCommit message (Collapse)Author
26 hoursMerge branch 'vfs.all' of ↵Mark Brown
https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
26 hoursMerge branch 'master' of ↵Mark Brown
https://github.com/Paragon-Software-Group/linux-ntfs3.git
40 hoursMerge branch 'vfs-7.4.idmap' into vfs.allChristian Brauner
42 hoursntfs3: free replaced mount options after a grace periodChristian Brauner
ntfs_fs_reconfigure() replaces the old mount options with new mount options in sbi->options. The old mount options land in fs_context and are freed as soon as remount returns. With a pathwalk held up in ntfs_nls_to_utf16() between loading sbi->options and dereferencing it while a remount replaces them: BUG: KASAN: slab-use-after-free in ntfs_nls_to_utf16+0x7ae/0x7c0 Read of size 8 at addr ff110001044fd448 by task walker/98 Call Trace: <TASK> ntfs_nls_to_utf16+0x7ae/0x7c0 ntfs_d_hash+0x22b/0x500 link_path_walk+0x11ad/0x1d30 path_lookupat+0x63/0xb00 filename_lookup+0x1f1/0x5b0 vfs_statx+0xd7/0x3b0 [...] Allocated by task 95: __kmalloc_node_track_caller_noprof+0x2a2/0x730 kmemdup_noprof+0x25/0x50 ntfs_fill_super+0x1e5/0x4550 [...] Freed by task 97: kfree+0x1db/0x5b0 put_fs_context+0x13c/0x7d0 path_mount+0x54a/0x2130 __x64_sys_mount+0x20c/0x270 That info is needed in rcu lookup in the dentry hash and dentry compary methods for ntfs3 and causes a UAF today. Delay freeing with an rcu grace period. ntfs_nls_to_utf16() reads ->nls and ->ads once under rcu_read_lock() so the hooks pair with the release in ntfs_fs_reconfigure(). That also covers callers outside of rcu pathwalk such as ntfs_lookup(). They don't hold rcu_read_lock() themselves and wouldn't be protected by the grace period otherwise. Remount refuses to change the iocharset so ->nls stays valid after rcu_read_unlock(). Fixes: a3a956c78efa ("fs/ntfs3: Add option "nocase"") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20261009-work-fixes-rcu-v1-8-7446d2d335e6@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
42 hoursntfs3: free the sbi after a grace periodChristian Brauner
If the "nocase" mount option ntfs3 uses custom hash and dentry compare functions. Both access ntfs3 superblock private information in rcu pathwalk. ntfs_put_super() and ntfs3_kill_sb() drop the info without any rcu delay. A lookup that's still inside the filesystem when the last reference to the mount is dropped causes a UAF. With a pathwalk held up in ntfs_d_hash() while the last reference to the mount goes away: BUG: KASAN: slab-use-after-free in ntfs_nls_to_utf16+0x752/0x940 Read of size 8 at addr ff1100010453c850 by task walker/99 Call Trace: <TASK> ntfs_nls_to_utf16+0x752/0x940 ntfs_d_hash+0x22b/0x500 link_path_walk+0x11ad/0x1d30 path_lookupat+0x63/0xb00 filename_lookup+0x1f1/0x5b0 vfs_statx+0xd7/0x3b0 [...] Allocated by task 95: __kmalloc_cache_noprof+0x249/0x560 ntfs_init_fs_context+0x2db/0x5a0 alloc_fs_context+0x52e/0xc70 [...] Freed by task 98: kfree+0x1db/0x5b0 deactivate_locked_super+0x9c/0xf0 cleanup_mnt+0x9f/0x130 task_work_run+0x131/0x250 The bug is currently masked by the grace period in namespace_unlock() for umount(2). Keep the necessary information until the filesystem private info is freed and free it with an rcu delay. ntfs3_free_sbi() now runs from an rcu callback and drops the upcase table via ntfs_put_shared(). So switch s_shared_lock over to spin_lock_bh(). Otherwise the callback can deadlock against a task holding the lock on the same cpu. Fixes: a3a956c78efa ("fs/ntfs3: Add option "nocase"") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20261009-work-fixes-rcu-v1-7-7446d2d335e6@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
3 daysfs/ntfs3: do not mark frame pages uptodate on read errorWeiming Shi
ni_read_frame() ends with a loop that marks every page of a compressed frame uptodate regardless of whether the read succeeded: out: for (i = 0; i < pages_per_frame; i++) { pg = pages[i]; SetPageUptodate(pg); } Non-critical frame pages come from __filemap_get_folio(FGP_CREAT) and are not zeroed. On an error path that reaches out: before frame_mem is written, these pages keep uninitialized page-allocator contents but are still marked uptodate. A later read() is then served from the page cache and returns that stale kernel memory to user space. For an unprivileged user mounting a crafted NTFS image this is a repeatable kernel memory disclosure. Mark the pages uptodate only on success; on error leave them !uptodate so the read path re-reads them or reports the error. Fixes: 4342306f0f0d ("fs/ntfs3: Add file operations and implementation") Reported-by: Xiang Mei <xmei5@asu.edu> Signed-off-by: Weiming Shi <bestswngs@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: prevent positive E_NTFS_NONRESIDENT from reaching writeback error ↵Vidhu Sarwal
handling attr_data_write_resident() returns the internal positive status code E_NTFS_NONRESIDENT when the DATA attribute becomes non-resident. ntfs_writepages() propagates this value into generic writeback error handling, which expects a standard negative errno and triggers WARN_ON_ONCE(err > 0). Translate positive E_NTFS_* status codes to -EIO before recording the writeback error. This prevents positive internal status codes from reaching generic writeback code while preserving the existing behaviour of treating this path as a writeback error. Reported-by: syzbot+57f9327d593d301ce2a2@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=57f9327d593d301ce2a2 Signed-off-by: Vidhu Sarwal <vidhu.linux@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: fix ntfs_fnd leak when compare function is missingliyouhong
indx_insert_entry(), indx_delete_entry() and indx_update_dup() take a finder via fnd_get() before calling get_cmp_func(). If no compare function is available they return -EINVAL and never release the finder. Jump to the existing out path so fnd_put() still runs. Fixes: 3a2141b2f1c3 ("fs/ntfs3: resolve compare function in public index APIs") Signed-off-by: liyouhong <liyouhong@kylinos.cn> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: fix stale attr_b in attr_allocate_frame()Konstantin Komarov
ni_insert_nonresident() can create an attribute list and move the base $DATA attribute to another MFT record, which invalidates attr_b and mi_b. attr_allocate_frame() kept using the stale pointers afterwards to update valid_size, writing into a record that no longer holds the attribute. The same situation is already handled in attr_data_get_block_locked(), which re-reads the base attribute after the layout changes. Look up attr_b again after the insertion, and read valid_size from the attribute before the layout can change. While here, flatten the 'if (attr)' block in attr_allocate_frame() by returning early to ins_ext, and simplify an empty-bodied if in attr_data_get_block_locked(). Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: reject unaligned hole punching on compressed filesKonstantin Komarov
attr_punch_hole() handles a range that is not cluster aligned by reporting the frame size back to the caller, which zeroes the partial clusters and retries. Return -EINVAL for an unaligned punch on a compressed attribute; the sparse case is unaffected. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: take ni_lock() once in ntfs_readdir()Konstantin Komarov
ntfs_readdir() took ni_lock() to load subrecords, dropped it, and then took it again for the index walk. The double-checked pattern around ni_load_all_mi() and the separate out_unlock label exist only because of that split. Take the lock once for the whole function and drop the second acquisition, the inner re-check and the duplicate error label. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: reserve a compression unit in maxbytes_sparseKonstantin Komarov
With 32-bit clusters, the maximum size of a sparse file was computed as the largest offset addressable by a 32-bit VCN. Reserve one compression unit below the limit so the rounded-up VCN still fits. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: allow a zero cluster count for resident attributesKonstantin Komarov
ntfs_iomap_begin() rejected a zero cluster count from attr_data_get_block() as corruption. For a resident attribute this is a normal result: the data lives in the MFT record, so no clusters are mapped, and the resident case is handled further down and returns before the cluster count is used. Move the check below the RESIDENT_LCN and EOF_LCN handling, so it only applies to the mapped case where a zero count really does indicate a broken record. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: add 'ugm' mount option to control storing uid/gid/mode in xattrsKonstantin Komarov
On file creation ntfs3 stores the owner, group, mode and device number in the WSL extended attributes $LXUID, $LXGID, $LXMOD and $LXDEV, and may also write a POSIX ACL. These attributes are meaningless to Windows, and the packed size of the EAs is recorded in the directory entry as well, so every created file carries them. Add a 'ugm' mount option controlling this. It is enabled by default, preserving the current behaviour; mount with 'ugm=0' to create files without the WSL attributes. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: do not force allocation for IOMAP_ZERO on a partial clusterKonstantin Komarov
ntfs_iomap_begin() set 'rw' for IOMAP_ZERO whenever the end of the range fell inside a cluster, forcing the sparse path to allocate clusters in order to zero them. Drop the IOMAP_ZERO case and only clamp clen_max when 'rw' is already set. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: minor code cleanupsKonstantin Komarov
Use Add2Ptr() for pointer arithmetic in fill_name_de(), consistent with the rest of the driver, and reflow two statements to match the 80-column style. No functional change. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: drop unused new_size argument from attr_make_nonresident()Konstantin Komarov
attr_make_nonresident() takes new_size but never uses it: the attribute's size is read from the record it is converting, and all three callers pass either the value already stored there or, in attr_set_size_res(), a size that the function ignores. Remove the argument. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: zero newly allocated clusters when the request is smaller than the ↵Konstantin Komarov
frame For compressed attributes, allocation is rounded up to a whole compression frame, so a request covering only part of a frame still allocates clst_per_frame clusters. The clusters outside the requested range are then allocated but never written by the caller. Until now 'zero' was set only when allocation had to start before the requested VCN. When it started at vcn0 but extended past the end of the request, the tail clusters were left holding whatever was previously on disk. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
3 daysfs/ntfs3: show supported options when an unknown one is givenKonstantin Komarov
When an unrecognised mount option is passed, fs_parse() returns -ENOPARAM. Print the list of supported options alongside the key, so the correct spelling can be found directly. Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
9 daysfs/ntfs3: check return values in indx_delete_entry()Dmitry Morgun
When the index tree collapses down to an empty root, indx_delete_entry() calls attr_set_size() and ni_remove_attr() for both ATTR_ALLOC and ATTR_BITMAP, but their return values are overwritten by the following call without being checked. In most other call sites of these functions the return value is checked, so add the same checks here in case an error at this point is meaningful. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Signed-off-by: Dmitry Morgun <d.morgun@ispras.ru> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
9 daysfs/ntfs3: remove redundant if-block in check_attr()Matvey Kovalev
Condition of this if-block repeats another one from if-block above. So, condition is always false. Found by Linux Verification Center (linuxtesting.org) with SVACE. Signed-off-by: Matvey Kovalev <matvey.kovalev@ispras.ru> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
9 daysfs/ntfs3: validate that EA entry size covers its name and valueAldo Ariel Panzardo
ntfs_read_ea() walks the on-disk $EA attribute and checks each EA_FULL entry for consistency. For an entry with a non-zero ef->size (the offset to the next entry) the loop only verifies that this stride fits in the remaining buffer, and then continues: if (ef->size) { ea_size = le32_to_cpu(ef->size); if (ea_size > bytes) goto out1; continue; } It never verifies that the entry's own name (ef->name_len) and value (ef->elength) actually fit inside ef->size. A later reader trusts ef->elength unconditionally; ntfs_get_ea() does: len = le16_to_cpu(ea->elength); /* up to 0xffff */ ... if (len > size) /* size is the user buffer */ return -ERANGE; memcpy(buffer, ea->name + ea->name_len + 1, len); A crafted image with a small but valid ef->size (e.g. 24) and ef->elength == 0xffff therefore passes validation, and a getxattr() with a large enough user buffer copies up to 64 KiB starting just past the short entry -- an out-of-bounds read of the ea_all allocation that leaks adjacent kernel heap memory to userspace. BUG: KASAN: slab-out-of-bounds in ntfs_get_ea+0x2b8/0x3f0 Read of size 65535 ... ntfs_get_ea -> ntfs_getxattr -> vfs_getxattr -> do_getxattr The zero-ef->size branch already computes the same struct_size() to derive the stride; mirror that in the non-zero branch and reject the entry when its declared size cannot hold the name and value. Fixes: 0e8235d28f3a ("fs/ntfs3: Check fields while reading") Cc: stable@vger.kernel.org Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-28fs/ntfs3: return -ERANGE for short xattr buffersBaolin Liu
ntfs3 currently returns -ENODATA when the xattr exists but the user buffer is too small. Return -ERANGE instead. Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-28fs/ntfs3: reject an oversized resident attribute on the inline iomap pathMichael Bommarito
attr_data_get_block_locked() maps a resident attribute as a one-page IOMAP_INLINE extent: it allocates a single page, copies the resident value into it with memcpy(), and hands that page to ntfs_iomap_begin() as the inline data. The copy length is the on-disk resident value length (res.data_size), and mi_enum_attr() only bounds that length against the MFT record size. A corrupted volume with MFT records larger than a page can therefore present a resident $DATA whose length exceeds PAGE_SIZE, and the memcpy() then writes past the single destination page. Impact: reading or writing a resident file on a mounted crafted NTFS volume whose MFT records are larger than a page overflows the one-page inline buffer in attr_data_get_block_locked() with attacker-controlled bytes. Reject such an attribute in attr_data_get_block_locked(), before the page is allocated and the copy is made. Fixes: 099ef9ab9203 ("fs/ntfs3: implement iomap-based file operations") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-28fs/ntfs3: fix slab-out-of-bounds write in attr_insert_range()Xiang Mei (Microsoft)
When fallocate(FALLOC_FL_INSERT_RANGE) grows a still-resident ntfs3 attribute, attr_insert_range() shifts the inline data using the insert length 'bytes' as both the destination offset and the copy size: memmove(data + bytes, data, bytes); Both are wrong: the shift must start at the insertion point 'vbo' and move only the data past it (data_size - vbo). When bytes > data_size the memmove runs off the end of the data_size + bytes buffer and overflows the adjacent slab object. Since vbo < data_size is already enforced above, the reworked offsets stay within the data_size + bytes buffer and cannot under/overflow. BUG: KASAN: slab-out-of-bounds in attr_insert_range (fs/ntfs3/attrib.c:2581) Write of size 512 at addr ffff8880135e8b10 by task exploit/142 ... __asan_memmove (mm/kasan/shadow.c:95) attr_insert_range (fs/ntfs3/attrib.c:2581) ntfs_fallocate (fs/ntfs3/file.c:618) vfs_fallocate (fs/open.c:338) __x64_sys_fallocate (fs/open.c:362) ... Fixes: 9256ec35359f ("fs/ntfs3: Refactoring attr_insert_range to restore after errors") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-28fs/ntfs3: fix integer overflow in find_log_rec() causing OOB readIbrahim Hashimov
find_log_rec() validates the on-disk LFS_RECORD_HDR.client_data_len field (`len`, a raw u32 read straight from a mounted $LogFile page) against the log's total available space: len = le32_to_cpu(rh->client_data_len); rec_len = len + log->record_header_len; if (rec_len >= log->total_avail) return -EINVAL; The addition is unchecked. For len in [0xffffffd0, 0xffffffff] (i.e. log->record_header_len away from UINT_MAX), `rec_len` wraps to a small u32 value that trivially clears the `rec_len >= log->total_avail` guard, so a record whose declared length is actually ~4 GiB is reported as valid and in-page. Because find_log_rec() wrongly accepts the record, its caller read_log_rec_lcb() hands it back to log_replay() as good. On the transaction-table replay leg, log_replay() re-derives the record length directly from the same raw, still-unclamped client_data_len field and uses it to size the restart-table blob: rec_len = le32_to_cpu(frh->client_data_len); /* ~0xffffffff */ ... t16 = le16_to_cpu(lrh->redo_off); rt = Add2Ptr(lrh, t16); t32 = rec_len - t16; /* still ~4 GiB */ if (!check_rstbl(rt, t32)) return -EINVAL; check_rstbl()'s own `bytes < ts` bounds guard is defeated because the caller-supplied `bytes` (t32) is itself the poisoned ~4 GiB value, so the guard can never trigger. The subsequent entry-validation loop then walks `rt->used` (also attacker-controlled) entries of sizeof(struct RESTART_TABLE) == 0x18 bytes each straight past the end of the kmalloc(log->page_size) log-page buffer allocated in read_log_page(), producing an out-of-bounds read that is reachable simply by mounting a crafted $LogFile (no genuine dirty-page / crash-recovery state is required). Confirmed via KASAN as a slab-out-of-bounds read in check_rstbl(), called from log_replay() -> ntfs_loadlog_and_replay() -> ntfs_fill_super() -> mount(2). Fix the root cause instead of hardening every downstream consumer of the poisoned length: make the addition in find_log_rec() overflow-safe with check_add_overflow(), mirroring the pattern fs/ntfs3/run.c already uses to validate other attacker-controlled on-disk length/offset arithmetic decoded from MAPPING_PAIRS runs, e.g.: if (check_add_overflow(vcn64, len, &next_vcn)) return -EINVAL; ... if (check_add_overflow(lcn, len, &lcn_end)) return -EINVAL; (fs/ntfs3/run.c, run_unpack()). With the overflow rejected instead of silently wrapped, `rec_len` is only ever a true, non-wrapped sum, so the existing `rec_len >= log->total_avail` check now correctly rejects any oversized record up front. log_replay()'s transaction-table leg then never gets a chance to re-read the poisoned client_data_len, and check_rstbl() is never invoked with an attacker-inflated `bytes` for this path. Verified on a v6.19 KASAN build: mounting a crafted $LogFile whose client_data_len overflows trips a KASAN slab-out-of-bounds-read report in check_rstbl() before this fix; with check_add_overflow() applied, the same image is cleanly rejected during log replay and no KASAN report fires. Fixes: b46acd6a6a62 ("fs/ntfs3: Add NTFS journal") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov <security@auditcode.ai> Assisted-by: AuditCode-AI:2026.07 Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-24ntfs3: fix resident attribute handling in run load pathsYi Xie
Don't dereference nres unless attr->non_res is set. ni_find_attr() can succeed with a resident record on a corrupted image, and run_unpack_ex() would mis-parse it as mapping pairs. Fix attr_load_runs(), attr_load_runs_vcn() and attr_set_size(). Fixes: 5ec75546dc71 ("ntfs: fix WARN_ON for resident attribute in ntfs_map_runlist_nolock()") Signed-off-by: Yi Xie <xieyi@kylinos.cn> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-24Fix OOB write if err == buflen in ntfs_readlink_hlp()Alexandro Calo
ntfs_utf16_to_nls() may return buflen. The caller later uses the returned length as the index for writing the trailing NUL byte, so err == buflen writes one byte past the end of buffer. Fix this by limiting err to the last valid buffer index before writing NUL. ntfs_utf16_to_nls() returning a negative value is already handled by if (err < 0) goto out; As long as buflen is guaranteed to be nonzero the patch is fine. As a defensive fix if(buflen==0) could be added. This heap out-of-bounds write requires a crafted filesystem image, which is not in the kernel threat model, but fixing memory errors would be nice to keep things secure. Signed-off-by: Alexandro Calo <alexandro.calo@nozominetworks.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-24fs/ntfs3: Fix heap overflow after ALIGN in mi_pack_runs()Alexandro Calo
When run_pack() returns a non-aligned byte count, ALIGN(err, 8) inflates it by 1-7 bytes, and since the destination of the memmove() is computed from that inflated value, the entire attribute tail could be copied 1-7 bytes past where the record buffer ends. The memmove() destination is next + new_run_size - run_size. For it to stay within the record, new_run_size must not exceed run_size + dsize, the space actually available. Since new_run_size = ALIGN(err, 8), a fix could be to ensures there is enough room that whatever ALIGN does to err, the result still fits within the available space. Restricting the budget of run_pack() should guarantee that. Something like: u32 avail = (run_size + dsize) & ~7u; err = run_pack(run, svcn, len, Add2Ptr(attr, run_off), avail, &plen); Now, run_pack() should never return 0. As now, the function returns at least 1. So this is fine. Negative values are caught as errors by if(err<0). The rounded-down avail may be smaller than the existing run size, if avail=0 run_pack is called and will write run_buf[0] = 0; that will be OOB, because the buffer size is zero, so imo the caller should avoid calling run_pack() with avail=0. After run_pack(), if plen = 0, then svcn + plen - 1 becomes svcn - 1. So, after run_pack(), it may be worth ensuring if(!plen). Lastly, I'm not sure if -ENOSPC is the right error code here, but it seems reasonable for a space condition. This heap out-of-bounds write requires a crafted filesystem image, which is not in the kernel threat model, but fixing memory errors would be nice to keep things secure. Signed-off-by: Alexandro Calo <alexandro.calo@nozominetworks.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-24fs/ntfs3: Fix OOB write if ret == buf_len in ntfs_utf16_to_nls()Alexandro Calo
'utf16s_to_utf8s()' is documented to return at most 'buf_len', the caller does not check whether 'ret == buf_len' before writing the terminator. The 'buf[ret] = '\0'' could write a NULL terminator one byte past the buffer. Fix this up by limiting ret to the last valid buffer index before writing NULL. As long as 'utf16s_to_utf8s()' never return a negative value and 'buf_len' is guaranteed to be nonzero the patch is fine. Anyway, as defensive fixes, if(ret<0) and if(buf_len==0) could be added. Signed-off-by: Alexandro Calo <alexandro.calo@nozominetworks.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-23fs: port ->setattr() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-24-54ccd48e100b@kernel.org Acked-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->getattr() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-23-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->create() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-22-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->symlink() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-21-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->mkdir() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-20-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->rename() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-19-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->mknod() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-18-54ccd48e100b@kernel.org Acked-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->get_acl() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-16-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->set_acl() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-15-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port ->fileattr_set() to pass const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-14-54ccd48e100b@kernel.org Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port xattr to const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-13-54ccd48e100b@kernel.org Acked-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-23fs: port acl to const mnt_idmapChristian Brauner
Convert to const struct mnt_idmap. A mount's idmapping is immutable. The only thing that is allowed to be modified afterwards is the reference count and that is hidden behind mnt_idmap_get() and mnt_idmap_put(). Everything else only ever reads from the idmapping. This is the same model that struct cred uses and the idmapping is also rather sensitive. So make the idmap argument const wherever we can. The conversion is done from the bottom up so callers can continue to pass a non-const pointer to a const parameter until the conversion is finished. No functional changes. Link: https://patch.msgid.link/20260901-work-idmap-const-v1-11-54ccd48e100b@kernel.org Acked-by: Paul Moore <paul@paul-moore.com> Reviewed-by: Jan Kara <jack@suse.cz> Reviewed-by: Seth Forshee <sforshee@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
2026-09-21fs/ntfs3: validate $SDH entry size in ntfs_insert_securityXiang Mei
ntfs_insert_security() walks the $Secure::$SDH index and reads e->sec_hdr.size for every entry returned by indx_find()/indx_find_sort(). Those only guarantee the NTFS_DE envelope (e->size), so a crafted $SDH entry shorter than a full NTFS_DE_SDH at the tail of an index buffer makes the read of e->sec_hdr.size run past the allocation. Triggerable by an unprivileged setxattr(2) on a crafted NTFS image. BUG: KASAN: slab-out-of-bounds in ntfs_insert_security (fs/ntfs3/fsntfs.c:2148) Read of size 4 at addr ffff88800fa1de00 by task exploit/142 ntfs_insert_security (fs/ntfs3/fsntfs.c:2148) ntfs_setxattr (fs/ntfs3/xattr.c:947) __vfs_setxattr (fs/xattr.c:223) __vfs_setxattr_noperm (fs/xattr.c:257) vfs_setxattr (fs/xattr.c:344) filename_setxattr (fs/xattr.c:690) path_setxattrat (fs/xattr.c:734) __x64_sys_setxattr (fs/xattr.c:764) [...] The buggy address is located 0 bytes to the right of allocated 512-byte region [ffff88800fa1dc00, ffff88800fa1de00) Stop the scan when an entry is too small to hold a SECURITY_HDR. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Reported-by: Weiming Shi <bestswngs@gmail.com> Signed-off-by: Xiang Mei <xmei5@asu.edu> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-21fs/ntfs3: validate EA entry layout against ef->size in ntfs_read_eaKonstantin Komarov
In ntfs_read_ea(), the ef->size branch only checks that the declared entry size fits the remaining buffer; unlike the zero-size branch it never verifies that the entry's own name and value fit inside ef->size. A crafted image can thus present an in-bounds ef->size with an arbitrary elength, which find_ea()/ntfs_get_ea() later use as the memcpy length, reading past the allocation and leaking adjacent heap memory: BUG: KASAN: slab-out-of-bounds in ntfs_get_ea (fs/ntfs3/xattr.c:302) Read of size 65535 at addr ffff888010770b0f by task exploit/142 ... __asan_memcpy (mm/kasan/shadow.c:105) ntfs_get_ea (fs/ntfs3/xattr.c:302) ntfs_getxattr (fs/ntfs3/xattr.c:848) __vfs_getxattr (fs/xattr.c:446) vfs_getxattr (fs/xattr.c:479) do_getxattr (fs/xattr.c:808) filename_getxattr (fs/xattr.c:837) path_getxattrat (fs/xattr.c:876) do_syscall_64 (arch/x86/entry/syscall_64.c:94) ... The buggy address is located 15 bytes inside of allocated 20-byte region, cache kmalloc-32 Validate the name/value layout in the ef->size branch too, bounding it against ef->size, as the zero-size branch validates it against the buffer. Fixes: be71b5cba2e6 ("fs/ntfs3: Add attrib operations") Reported-by: Weiming Shi <bestswngs@gmail.com> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Xiang Mei <xmei5@asu.edu> [almaz.alexandrovich@paragon-software.com: remove duplicate check of need > ea_size] Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-21fs/ntfs3: fix valid_size not being updated in attr_allocate_frame()Zhan Xusheng
At the end of attr_allocate_frame() the on-disk valid_size is meant to be updated to the value passed in via @new_valid: valid_size = le64_to_cpu(attr_b->nres.valid_size); if (new_valid != valid_size) { attr_b->nres.valid_size = cpu_to_le64(valid_size); mi_b->dirty = true; } but it writes back the old valid_size that was just read instead of new_valid, so the field is never actually changed even though the record is marked dirty -- the intended update is a no-op. ni_update_parent() spells the same read-compare-write out correctly a few hundred lines away, storing the newly derived value: valid_le = cpu_to_le64(new_valid); if (valid_le != attr->nres.valid_size) { attr->nres.valid_size = valid_le; In practice that path, reached from ni_write_inode(), re-derives nres.valid_size from ni->i_valid on inode write-back, which normally masks this, so the visible impact is limited. The assignment here is still wrong though; store @new_valid as intended. Fixes: be71b5cba2e6 ("fs/ntfs3: Add attrib operations") Signed-off-by: Zhan Xusheng <zhanxusheng@xiaomi.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-21fs/ntfs3: check INDEX_ROOT resident size before use in ntfs_read_mft()Xiang Mei (Microsoft)
When ntfs_read_mft() parses an ATTR_ROOT attribute it computes root = Add2Ptr(attr, roff); using the on-disk resident data offset (roff = attr->res.data_off) and then dereferences root->type and root->rule, and later passes root on to indx_init(). Unlike the ATTR_STD and ATTR_NAME cases, which validate that the resident data is large enough (asize >= sizeof(struct X) + roff and rsize >= sizeof(struct X)) before accessing it, the ATTR_ROOT case has no such check. mi_enum_attr() only guarantees data_off + data_size <= attribute size, so a crafted image can set data_off such that root lands at (or past) the end of the MFT record's slab allocation while still passing that check (e.g. data_size == 0). The subsequent read of root->type is then a slab-out-of-bounds read, reported by KASAN when mounting the image: BUG: KASAN: slab-out-of-bounds in ntfs_iget5 (fs/ntfs3/inode.c:283) Read of size 4 at addr ffff88800c06a400 by task exploit/142 Call Trace: ... [KASAN report internals] ntfs_iget5 (fs/ntfs3/inode.c:283 fs/ntfs3/inode.c:542) ntfs_fill_super (fs/ntfs3/super.c:1690) get_tree_bdev_flags (fs/super.c:1634) vfs_get_tree (fs/super.c:1694) fc_mount (fs/namespace.c:1198) path_mount (fs/namespace.c:3765 ...) __x64_sys_mount (fs/namespace.c:4174 ...) do_syscall_64 (arch/x86/entry/syscall_64.c:63) entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:121) The buggy address is located 0 bytes to the right of allocated 1024-byte region [ffff88800c06a000, ffff88800c06a400) which belongs to the cache kmalloc-1k of size 1024 Validate the resident data size in the ATTR_ROOT case the same way the neighbouring cases do, so that a truncated or mis-offset INDEX_ROOT is rejected before root is dereferenced. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-16ntfs3: bound index head insertions by buffer capacityYousef Alhouseen
hdr_insert_head() shifts the existing index entries by ins_bytes without checking that the resulting used size fits within hdr->total. A directory index root larger than the newly allocated index buffer can therefore make the memmove write beyond the buffer. Reject insertions whose size would exceed the index header capacity and make both split callers unwind the new node when that happens. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Reported-by: syzbot+45ef5a8d661162757547@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=45ef5a8d661162757547 Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-16fs/ntfs3: reject FS_IOC_SETFSLABEL on readonly mountsBaolin Liu
FS_IOC_SETFSLABEL does not check for readonly mounts and can report success without persisting the label change. Return -EROFS on readonly mounts. Fixes: 21dc07ac9c25 ("ntfs3: add FS_IOC_SETFSLABEL ioctl") Signed-off-by: Baolin Liu <liubaolin@kylinos.cn> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-16ntfs3: reject empty iomap before reading its LCNYousef Alhouseen
attr_data_get_block() can return success with a zero run length when no cached or on-disk mapping covers a read VCN. In that case it does not initialize the LCN output. Check the returned length before comparing the LCN against the special mapping values. This preserves the existing -EINVAL result for an empty mapping without passing an uninitialized LCN to the comparisons. Fixes: ecbb433f9a8e ("fs/ntfs3: fold file size handling into ntfs_set_size()") Reported-by: syzbot+4b4ec878e25fafefa70f@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=4b4ec878e25fafefa70f Cc: stable@vger.kernel.org Signed-off-by: Yousef Alhouseen <alhouseenyousef@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>
2026-09-16ntfs3: fix out-of-bounds read in ntfs_read_mft()Weiming Shi
ntfs_read_mft() walks the attributes of an MFT record and, for each FILE_NAME attribute, compares the resident on-disk name with the name being looked up. The only size check before the compare is rsize >= SIZEOF_ATTRIBUTE_FILENAME, which covers the fixed header up to and including name_len, but not the variable-length name that follows it. name_len comes straight from disk and is not validated against the resident data_size. A crafted image can set name_len large while keeping data_size at the header size, so ntfs_cmp_names_cpu() reads name_len le16 characters past the end of the MFT record buffer: BUG: KASAN: slab-out-of-bounds in ntfs_cmp_names_cpu (fs/ntfs3/upcase.c:84) Read of size 2 at addr ffff888031879c00 by task a.out ntfs_cmp_names_cpu (fs/ntfs3/upcase.c:84) ntfs_iget5 (fs/ntfs3/inode.c:209) dir_search_u (fs/ntfs3/dir.c:265) ntfs_lookup (fs/ntfs3/namei.c:85) __lookup_slow (fs/namei.c:1915) lookup_slow (fs/namei.c:1932) path_lookupat (fs/namei.c:2278) vfs_statx (fs/stat.c) vfs_fstatat (fs/stat.c) __do_sys_newfstatat (fs/stat.c) Allocated by task: mi_init (fs/ntfs3/record.c) ntfs_iget5 (fs/ntfs3/inode.c) dir_search_u (fs/ntfs3/dir.c) ntfs_lookup (fs/ntfs3/namei.c) Check that the name fits in the resident data before using it. Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") Reported-by: Xiang Mei <xmei5@asu.edu> Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Weiming Shi <bestswngs@gmail.com> Signed-off-by: Konstantin Komarov <almaz.alexandrovich@paragon-software.com>