| Age | Commit message (Collapse) | Author |
|
https://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs.git
|
|
https://github.com/Paragon-Software-Group/linux-ntfs3.git
|
|
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
'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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|
|
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>
|