summaryrefslogtreecommitdiff
path: root/fs
AgeCommit message (Collapse)AuthorFilesLines
3 dayscifs: consolidate time_last_write stamp into _cifsFileInfo_put()Frank Sorenson2-66/+22
commit 0e3ea5445c228048f937ad5a944c27859a78f971 upstream. The time_last_write stamp was scattered across cifs_close(), smb2_deferred_work_close(), and the three drain functions in misc.c. This missed the case where background I/O holds the final reference after userspace close() returns, and required explicit maintenance at each close-path site. Move the smp_store_release() into _cifsFileInfo_put(), immediately before releasing open_file_lock. This single location covers all close paths unconditionally: normal close, background I/O dropping the final reference, deferred close via timer or external drain. The spinlock's store-release/load-acquire pairing with is_inode_writable() already provides the ordering guarantee documented in is_size_safe_to_change(). Remove the now-redundant stamps from cifs_close(), smb2_deferred_work_close(), and all six stamp sites in the misc.c deferred-close drain functions. Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata") Signed-off-by: Frank Sorenson <sorenson@redhat.com> Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Signed-off-by: Steve French <stfrench@microsoft.com> Cc: Jiri Slaby <jirislaby@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 dayscifs: fix time_last_write stamp placement in setattr/truncate pathsFrank Sorenson1-4/+22
commit ecababf08905958ba8c125979c4e39fc2f1a8a05 upstream. cifs_file_set_size() calls cifs_setsize() on success, which calls i_size_write(), updating i_size to the new value. The subsequent check attrs->ia_size != i_size_read() in both cifs_setattr_unix() and cifs_setattr_nounix() therefore always evaluates false after a successful cifs_file_set_size(), making the smp_store_release() of time_last_write dead code. The truncate path was unprotected against stale readdir size updates. Move the stamp to before the cifs_file_set_size() RPC call, guarded by attrs->ia_size != i_size_read() to exclude no-op same-size ftruncate(2) calls from stamping time_last_write unnecessarily. On the error path the stamp remains rather than being restored: restoring a stale snapshot (prev_tlw) could silently erase a concurrent _cifsFileInfo_put() close stamp if that close arrived between the READ_ONCE and the smp_store_release. readdir is suppressed until the stamp expires, which extends beyond one acregmax if the caller retries failed truncations. stat() is unaffected: the cifs_revalidate_dentry_attr() path calls cifs_fattr_to_inode() with from_readdir=false, which bypasses the time_last_write check in is_size_safe_to_change() entirely and always writes the authoritative QUERY_INFO result to i_size. Remove the now-unreachable stamp from the dead block in both functions. Fixes: e8a8d54c2d50 ("cifs: prevent readdir from changing file size due to stale directory metadata") Signed-off-by: Frank Sorenson <sorenson@redhat.com> Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Signed-off-by: Steve French <stfrench@microsoft.com> Cc: Jiri Slaby <jirislaby@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysSUNRPC: Return an error from xdr_buf_to_bvec() on overflowChuck Lever1-1/+5
[ Upstream commit 18c1cc69886192e33536498289d26dba6894e3d5 ] xdr_buf_to_bvec() returns a slot count even when the caller's bvec budget is exhausted partway through the xdr_buf. Callers feed that count into iov_iter_bvec() and continue as if the conversion had succeeded, silently sending or writing fewer bytes than the data length declares. For an NFS WRITE the server reports the truncated transfer to the client as full success. The overflow represents an internal invariant violation: a higher layer reserved a bvec budget too small for the xdr_buf it then asked the encoder to convert. That is a server-side fault, not a media I/O failure and not a malformed client argument. Change xdr_buf_to_bvec() to return a signed int and have the overflow label return -ESERVERFAULT. Update the three callers to detect the negative return and fail the request: nfsd_vfs_write() folds the error into host_err, which nfserrno() translates to nfserr_serverfault for the WRITE reply; svc_udp_sendto() and svc_tcp_sendmsg() propagate the error out of the send path. Reported-by: Chris Mason <clm@meta.com> Fixes: 2eb2b9358181 ("SUNRPC: Convert svc_tcp_sendmsg to use bio_vecs directly") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysxfs: don't replace the wrong part of the cow forkDarrick J. Wong2-83/+148
[ Upstream commit a1caeeadbf57ff86dfc3454398c46de86056a74e ] LOLLM points out that xfs_iext_lookup_extent can return a @got where got->br_startoff < startoff. In this case, xrep_cow_replace_range replaces the entire mapping instead of just the part that had been marked bad in the bitmap, but advances the bitmap cursor in xrep_cow_replace by the amount replaced. As a result, we fail to replace the end of the bad range, and replace part of the good range. Fix this by rewriting the replace method to handle replacing the middle of a cow fork mapping. This we do by returning both the current mapping as @got, and the subset of the mapping that we want to replace as @rep, using @rep to store the results of the new allocation, and comparing @rep to @got to figure out the exact transformations needed. Cc: stable@vger.kernel.org # v6.8 Fixes: dbbdbd0086320a ("xfs: repair problems in CoW forks") Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Assisted-by: LOLLM # finding obvious bugs Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Carlos Maiolino <cem@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysfuse-uring: fix race between registration and connection abortionJoanne Koong1-6/+16
[ Upstream commit 952b5d36f6a298f57c52a59e72076c69386a8aaf ] This fixes this race: - thread a: io_uring_enter -> register sqe -> fuse_uring_create_ring_ent -> allocate ent but doesn't grab queue_ref yet - thread b: fuse_conn_destroy() -> fuse_chan_abort() -> fuse_uring_abort() is a no-op due to queue ref being 0 - thread a: grabs the queue_ref, queue_ref is now 1, rest of fuse_uring_do_register() logic executes - thread b: fuse_chan_abort() returns, fuse_chan_wait_aborted() now runs and calls "wait_event(ring->stop_waitq, atomic_read(&ring->queue_refs) == 0);" The abort/unmount thread will hang indefinitely in unkillable state as nothing will decrement queue_refs or wake stop_waitq, and the ring, queue, and ent are leaked. Fix this by checking fch->connected under fch->lock after the created ent has grabbed a ref count on the queue. This ensures that in the scenario above, it is guaranteed that we either release the queue ref and wake up stop_waitq (in case fuse_chan_wait_aborted() is already waiting) in fuse_uring_do_register() when we detect !fch->connected, or if the connection is aborted after the check, it is guaranteed that the async teardown worker will be running in the background cleaning up ents and decrementing the ent's ref on the queue, which will unblock the eventual queue and ring teardown. Fixes: 24fe962c86f5 ("fuse: {io-uring} Handle SQEs - register commands") Cc: stable@vger.kernel.org Reviewed-by: Bernd Schubert <bernd@bsbernd.com> Signed-off-by: Joanne Koong <joannelkoong@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> [ changed fch->lock/fch->connected references to fc->lock/fc->connected since struct fuse_chan does not exist in this tree ] Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysksmbd: validate ACE size against SID sub-authoritiesNamjae Jeon1-3/+10
commit 5152c6d49e3fd4e9f2e857c57527aead752f1f87 upstream. set_ntacl_dacl() validates sid.num_subauth before copying an ACE, but does not verify that the declared ACE size contains all sub-authorities described by that field. An undersized ACE can therefore be copied and later make the POSIX ACL deduplication walk inspect data beyond the copied ACE boundary. The existing initial bound check is also too small. It only ensures that the ACE size field is accessible before set_ntacl_dacl() reads sid.num_subauth farther into the input buffer. Require enough input for the fixed SID header before accessing num_subauth, reject ACEs smaller than that header, and skip ACEs whose declared size cannot contain the complete SID. This makes the validation consistent with the other ACE walk paths. Reported-by: LocalHost <localhost.detect@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysksmbd: bound DACL dedup walk to copied ACEsNamjae Jeon1-6/+10
commit 58d97fcd0bf1aee694e244cc28635b9df95b543b upstream. set_ntacl_dacl() can stop copying ACEs before consuming the full input DACL when size accounting overflows. When that happens, num_aces reflects only the ACEs that were actually copied into the output DACL, but set_posix_acl_entries_dacl() still receives nt_num_aces and uses it to walk the existing ACE array during dedup. That makes the dedup walk scan past the copied ACE array and inspect buffer tail that does not contain valid ACEs. Split the two meanings currently carried by the NT ACE count. Pass the number of copied NT ACEs to bound the dedup walk, and preserve the original "input DACL had NT ACEs" state separately for the Everyone/default ACL fallback. This keeps the dedup walk aligned with the ACEs that are actually present in the rebuilt DACL. Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysksmbd: restore DACL size on check_add_overflow() to avoid malformed ACLWentao Guan1-1/+6
commit bbf0a8e931204ecdab494a88d43b0a24a04285c5 upstream. check_add_overflow() unconditionally writes the truncated sum into *d even on overflow, per its contract in include/linux/overflow.h. The four check_add_overflow() guards in set_posix_acl_entries_dacl() and set_ntacl_dacl() break out of the ACE-building loops on overflow, but the truncated *size is then consumed downstream at the end of set_ntacl_dacl(): pndacl->size = cpu_to_le16(le16_to_cpu(pndacl->size) + size); This produces an on-wire NT ACL whose pndacl->size under-reports the bytes actually written by the preceding fill_ace_for_sid()/memcpy() calls, yielding a malformed ACL that can trigger out-of-bounds reads when re-parsed by clients or ksmbd itself. Restore *size to its pre-addition value on each overflow branch (via `*size -= ace_sz` / `size -= nt_ace_size`) so that after the break, *size once again holds the cumulative size of the successfully-written ACEs. The committed ACL is then truncated-but-self-consistent rather than malformed. The ksmbd DACL builders are the only check_add_overflow() sites found where an overflow path breaks out of a loop and the destination value is consumed afterward. The other nearby break-style cases either return -EINVAL on overflow (transport_ipc.c) or break without consuming the overflowed destination value afterward (buildid.c). Fixes: 299f962c0b02 ("ksmbd: use check_add_overflow() to prevent u16 DACL size overflow") Assisted-by: atomcode:glm-5.2 Assisted-by: Codex:gpt-5.5 Cc: stable@vger.kernel.org Signed-off-by: Wentao Guan <guanwentao@uniontech.com> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysksmbd: validate num_subauth when copying ACE in set_ntacl_daclHaofeng Li1-1/+7
commit 47f0b34f6bc98ed85bfdc293e8f3e432ec24958d upstream. set_ntacl_dacl() copies each ACE from the attacker-controlled stored security descriptor verbatim into the response DACL without checking sid.num_subauth. The ACE bytes (including an unchecked num_subauth) originate from an authenticated SMB2_SET_INFO(SecInfo=DACL) that is stored raw via ksmbd_vfs_set_sd_xattr(); parse_dacl() rejects a bad ACE with `break` rather than an error, so parse_sec_desc() still returns success and the malformed SD reaches the xattr intact. On a subsequent SMB2_QUERY_INFO(SecInfo=DACL) for an inode carrying a POSIX access ACL, build_sec_desc() -> set_ntacl_dacl() -> set_posix_acl_entries_dacl() walks the copied ACEs and reads ntace->sid.sub_auth[ntace->sid.num_subauth - 1] with num_subauth taken straight from the stored SD. Since sub_auth[] is fixed at SID_MAX_SUB_AUTHORITIES (15), a crafted num_subauth (e.g. 255) drives an out-of-bounds heap read of ~1 KB with an offset fully controlled by an authenticated client. The sibling functions already gate this field: parse_dacl() -- num_subauth == 0 || > SID_MAX_SUB_AUTHORITIES parse_sid() -- num_subauth > SID_MAX_SUB_AUTHORITIES smb_copy_sid() -- min_t(u8, num_subauth, SID_MAX_SUB_AUTHORITIES) set_ntacl_dacl() is the lone inconsistent path that omits the check. Add the same num_subauth validation in set_ntacl_dacl() before copying the ACE, matching the gate already enforced by parse_dacl(). Signed-off-by: Haofeng Li <lihaofeng@kylinos.cn> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> Suggested-by: Namjae Jeon <linkinjeon@kernel.org> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Wentao Guan <guanwentao@uniontech.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysksmbd: validate minimum PDU size for transform requestsNamjae Jeon1-5/+11
commit cfc0b8e5080aec87700774e8568765eaa4b7b92b upstream. The receive path applies the minimum SMB2 PDU size check only when ProtocolId is SMB2_PROTO_NUMBER. A packet carrying SMB2_TRANSFORM_PROTO_NUM bypasses the check even when the negotiated dialect does not provide transform handling. On an SMB 2.1 connection, a short transform packet therefore reaches init_smb2_rsp_hdr(), which interprets the request as a full SMB2 header and reads beyond the request allocation. The copied fields can then be returned to the unauthenticated client. Compression transforms are converted to ordinary SMB2 messages before protocol validation. After that conversion, validate ordinary SMB2 requests against SMB2_MIN_SUPPORTED_PDU_SIZE and require encryption transform requests to contain both a transform header and an SMB2 header. This rejects truncated requests before work allocation. Fixes: 368ba06881c3 ("ksmbd: check the validation of pdu_size in ksmbd_conn_handler_loop") Cc: stable@vger.kernel.org Reported-by: zdi-disclosures@trendmicro.com # ZDI-CAN-31063 Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysksmbd: defer destroy_previous_session() until after NTLM authenticationJames Montgomery1-5/+4
commit c74801ee524f477c174a1899782b6c3b6918d407 upstream. In ntlm_authenticate(), destroy_previous_session() is called using a user pointer resolved from the client-supplied NTLM blob username field before the NTLMv2 response is validated. An authenticated attacker can set the NTLM blob username to match a victim account and set PreviousSessionId to the victim's session ID; destroy_previous_session() destroys the victim's session while ksmbd_decode_ntlmssp_auth_blob() subsequently rejects the request with -EPERM. Move destroy_previous_session() and the prev_id assignment to after ksmbd_decode_ntlmssp_auth_blob() returns success and use sess->user rather than the pre-authentication lookup result. This matches the ordering already used by krb5_authenticate(), where destroy_previous_session() is called only after ksmbd_krb5_authenticate() returns success. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/linux-cifs/20260702155449.3639773-1-james_montgomery@disroot.org/ Signed-off-by: James Montgomery <james_montgomery@disroot.org> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 dayssmb: client: handle STATUS_STOPPED_ON_SYMLINK responses without a symlink targetCarl Johnson2-3/+41
commit 2eb74eef4b7eda8df593d22fb48e94ef959ec8a5 upstream. The macOS built-in SMB server returns STATUS_STOPPED_ON_SYMLINK for a CREATE on a path whose final component is a symlink, but it does not include a Symbolic Link Error Response in the error data: both ErrorContextCount and ByteCount are zero, so the symlink target is not present in the response at all. Per [MS-SMB2] section 2.2.2 such a response should carry a valid Symbolic Link Error Response, so this is a server bug, but the target can still be retrieved with FSCTL_GET_REPARSE_POINT. Frame from a capture against macOS 26.5.2 (build 25F84): SMB2 hdr : Status=0x8000002d STATUS_STOPPED_ON_SYMLINK, Cmd=Create Error Rsp: StructureSize=0x0009 Error Context Count: 0 Byte Count: 0 Error Data: 00 symlink_data() cannot find a struct smb2_symlink_err_rsp in such a response and returns -EINVAL, which parse_create_response() propagates, so smb2_query_path_info() bails out at if (rc || !data->reparse_point) goto out; before it can retry with SMB2_OP_GET_REPARSE. stat(), readlink() and ls of any server-side symlink then fail with -EINVAL: $ ls -la Config l????????? ? ? ? ? ? Config.json $ stat Config/Config.json stat: cannot statx 'Config/Config.json': Invalid argument A 5.10 client resolves these symlinks correctly against the same server and share, so this is a regression for Apple SMB servers. Handle it in several places: - symlink_data() detects the empty response (ErrorContextCount and ByteCount both zero) and returns a distinct -ENODATA, so that "server did not send the target" can be told apart from a genuinely malformed response and only this case is worked around. - parse_create_response() treats -ENODATA like STATUS_IO_REPARSE_TAG_NOT_HANDLED, which does not carry the target either: leave the reparse tag unset and clear rc, so the existing SMB2_OP_GET_REPARSE path retrieves the target. - smb2_query_path_info() only fixes up the symlink target type when the target is already known. SMB2_OP_GET_REPARSE sets data->reparse.tag but does not parse the target out of the reparse buffer; that happens later, in reparse_info_to_fattr(). Without this check smb2_fix_symlink_target_type() is called with a NULL target and returns -EIO. This could not happen with servers that send the target inline and therefore skip SMB2_OP_GET_REPARSE. - smb2_open_file() maps -ENODATA to -EIO, matching STATUS_IO_REPARSE_TAG_NOT_HANDLED, so its callers retrieve the target with SMB2_OP_GET_REPARSE as well. Tested on Debian 13, kernel 6.18.38 (armv7), against macOS 26.5.2: symlinks now resolve, including relative, parent-traversing and directory symlinks, and reads through symlinks succeed. Cc: stable@vger.kernel.org Co-developed-by: Pali Rohár <pali@kernel.org> Signed-off-by: Pali Rohár <pali@kernel.org> Signed-off-by: Carl Johnson <carl@jpartners.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 dayssuper: fix emergency thaw deadlock on frozen block devicesChristian Brauner1-13/+16
commit 749d7aa0377aae32af8c0a4ad43371e7bf830ab5 upstream. do_thaw_all_callback() calls bdev_thaw() while holding sb->s_umount exclusively. If the block device was frozen via bdev_freeze() dropping the last block layer freeze reference calls fs_bdev_thaw() which reacquires s_umount: do_thaw_all_callback(sb) super_lock_excl(sb) # holds sb->s_umount bdev_thaw(sb->s_bdev) mutex_lock(&bdev->bd_fsfreeze_mutex) # bd_fsfreeze_count drops 1 -> 0 bd_holder_ops->thaw == fs_bdev_thaw get_bdev_super(bdev) bdev_super_lock(bdev, true) super_lock(sb, true) down_write(&sb->s_umount) # same task: deadlock The emergency thaw worker deadlocks against itself holding both s_umount and bd_fsfreeze_mutex. That fscks any subsequent unmount, freeze, or thaw of that filesystem and block device. [ 81.878470] sysrq: Show Blocked State [ 81.880140] task:kworker/0:1 state:D stack:0 pid:11 tgid:11 ppid:2 task_flags:0x4208060 flags:0x00080000 [ 81.884876] Workqueue: events do_thaw_all [ 81.886656] Call Trace: [ 81.887759] <TASK> [ 81.888763] __schedule+0x579/0x1420 [ 81.890372] schedule+0x3a/0x100 [ 81.891794] schedule_preempt_disabled+0x15/0x30 [ 81.893848] rwsem_down_write_slowpath+0x1ea/0x900 [ 81.895191] ? __pfx_do_thaw_all_callback+0x10/0x10 [ 81.896528] down_write+0xbd/0xc0 [ 81.897505] super_lock+0x91/0x180 [ 81.898457] ? __mutex_lock+0xa99/0x1140 [ 81.900748] ? __mutex_unlock_slowpath+0x1f/0x400 [ 81.902069] bdev_super_lock+0x5b/0x150 [ 81.903132] get_bdev_super+0x10/0x60 [ 81.904042] fs_bdev_thaw+0x23/0xf0 [ 81.904755] bdev_thaw+0x82/0x100 [ 81.905484] do_thaw_all_callback+0x2c/0x50 [ 81.906298] __iterate_supers+0x5d/0x130 [ 81.907067] do_thaw_all+0x20/0x40 [ 81.907739] process_one_work+0x206/0x5e0 [ 81.908545] worker_thread+0x1e2/0x3c0 [ 81.909339] ? __pfx_worker_thread+0x10/0x10 [ 81.910171] kthread+0xf4/0x130 [ 81.910799] ? __pfx_kthread+0x10/0x10 [ 81.911528] ret_from_fork+0x2e2/0x3b0 [ 81.912259] ? __pfx_kthread+0x10/0x10 [ 81.913010] ret_from_fork_asm+0x1a/0x30 [ 81.913806] </TASK> bdev_super_lock() even documents the violated requirement with lockdep_assert_not_held(&sb->s_umount). Acquiring bd_fsfreeze_mutex under s_umount also inverts the bd_fsfreeze_mutex vs. s_umount ordering established by bdev_{freeze,thaw}() and can thus ABBA against a concurrent block-layer freeze even when the recursive path isn't hit. Fix this by not holding s_umount around the bdev_thaw() loop at all. Pin the superblock with an active reference instead as filesystems_freeze_callback() does. The active reference keeps the superblock from being shut down and so ->s_bdev stays valid without holding s_umount. The block-layer-held freeze is dropped by fs_bdev_thaw() with FREEZE_MAY_NEST | FREEZE_HOLDER_USERSPACE exactly as a regular unfreeze would and thaw_super_locked() handles filesystem-level freezes as before. The emergency thaw path has deadlocked like this in one form or another for a long long time but the current exclusively-held shape dates back to commit [1] where thaw_bdev() already ended in thaw_super() with s_umount held by do_thaw_all_callback(). Fixes: 08fdc8a0138a ("buffer.c: call thaw_super during emergency thaw") [1] Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260723-work-super-emergency_thaw-v1-1-7c315c600245@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysproc: Fix broken error paths for namespace linksJann Horn1-1/+3
commit 425224c2d700391729be7fe6929a88ef4e2d7a4e upstream. Don't return the return value of down_read_killable() (0) when a ptrace access check fails, return -EACCES as intended. Reported-by: Magnus Lindholm <linmag7@gmail.com> Closes: https://lore.kernel.org/r/20260706170735.2941493-1-linmag7@gmail.com Fixes: 6650527444da ("proc: protect ptrace_may_access() with exec_update_lock (part 1)") Cc: stable@vger.kernel.org Signed-off-by: Jann Horn <jannh@google.com> Link: https://patch.msgid.link/20260706-procfs-ns-eacces-fix-v1-1-a69ab14c02e6@google.com Tested-by: Magnus Lindholm <linmag7@gmail.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysiomap: fix out-of-bounds bitmap_set() with zero-length rangeZhang Yi1-8/+14
commit 9c7d8f7c8994c790fca501dc45ce66e7356cbe05 upstream. ifs_set_range_dirty() and ifs_set_range_uptodate() compute last_blk as (off + len - 1) >> i_blkbits. When off is 0 and len is 0, the unsigned subtraction underflows to SIZE_MAX, producing a huge last_blk and nr_blks value that causes bitmap_set() to write far beyond the ifs->state allocation. Regarding ifs_set_range_uptodate(), it is temporarily safe because len cannot be passed in as 0. However, for ifs_set_range_dirty() this is reachable from __iomap_write_end(): when copy_folio_from_iter_atomic() returns 0 (e.g. user buffer fault) and the folio is already uptodate, the guard at the top of __iomap_write_end() does not trigger because !folio_test_uptodate() is false, and iomap_set_range_dirty() is called with copied == 0. Add a !len guard to both functions before the computation, so that a zero-length range is a no-op. Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance") Cc: stable@vger.kernel.org # v6.6 Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Link: https://patch.msgid.link/20260714082325.325163-5-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysfscrypt: Avoid dynamic allocation in fscrypt_get_devices()Eric Biggers2-48/+34
commit 6fe4e4b8259e1330945b5f3c9476e08473b8e0e8 upstream. When a blk_crypto_key starts being used or is evicted, fs/crypto/ calls fscrypt_get_devices() to get the filesystem's list of block devices, then iterates over them and calls blk_crypto_config_supported(), blk_crypto_start_using_key(), or blk_crypto_evict_key() on each one. Currently, the block device pointers are placed in a dynamically allocated array. This dynamic allocation is problematic because: - It can fail, especially at the fscrypt_destroy_inline_crypt_key() call site when it's invoked for inode eviction under direct reclaim. - fscrypt_destroy_inline_crypt_key() doesn't handle the failure. It just zeroizes and frees the blk_crypto_key without calling blk_crypto_evict_key(). That causes a use-after-free. For now, let's fix this in the straightforward and easily-backportable way by switching to an on-stack array. Currently the fscrypt multi-device functionality is used only by f2fs, which has a hardcoded limit of 8 block devices. An on-stack array works fine for that. (Of course, this solution won't scale up to large number of block devices. For that we'd need a different solution, like moving the block device iteration into the filesystem. Or in the case of btrfs, which will only support blk-crypto-fallback, we should make it just call blk-crypto-fallback directly, so the block devices won't be needed.) Fixes: 22e9947a4b2b ("fscrypt: stop holding extra request_queue references") Cc: stable@vger.kernel.org Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://sashiko.dev/#/patchset/20260713023708.9245-1-ebiggers%40kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260719055602.78828-1-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysfscrypt: Add missing superblock check in find_or_insert_direct_key()Eric Biggers1-1/+7
commit b5fa40226e71c17847b9ff2816c6ca4133d0d994 upstream. The legacy 'fscrypt_direct_keys' table caches master keys that are used by v1 encryption policies that have FSCRYPT_POLICY_FLAG_DIRECT_KEY. It's just a global table for all filesystems (since the keys can be provided by the legacy process-subscribed keyrings mechanism, which makes it difficult to reuse super_block::s_master_keys). The entries in it ('struct fscrypt_direct_key') do contain a super_block pointer, though, for passing to fscrypt_destroy_inline_crypt_key() when the last inode that references the key is evicted. However, when finding the fscrypt_direct_key for an inode, we weren't actually comparing the super_block pointer. As a result, inodes with different super_blocks could point to the same fscrypt_direct_key. That could extend the lifetime of a fscrypt_direct_key beyond the super_block it points to, causing a use-after-free later. Fix this by creating distinct fscrypt_direct_key structs for distinct super_block structs. Note that this problem doesn't exist in the v2 policy equivalent ("per-mode keys"), since the data structures there are per super_block. Fixes: 22e9947a4b2b ("fscrypt: stop holding extra request_queue references") Cc: stable@vger.kernel.org Reported-by: Sashiko <sashiko-bot@kernel.org> Closes: https://sashiko.dev/#/patchset/20260717044303.425265-1-ebiggers%40kernel.org Reviewed-by: Christoph Hellwig <hch@lst.de> Link: https://patch.msgid.link/20260719033120.122120-1-ebiggers@kernel.org Signed-off-by: Eric Biggers <ebiggers@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysfs: preserve ACL_DONT_CACHE state in forget_cached_acl()Amir Goldstein1-0/+7
commit 4b9a5458d02e214ef2b384124ca626e3e381d778 upstream. The ACL_DONT_CACHE state is meant to be a constant state for the inode for filesystems that want to opt out of posix acl caching. Commit facd61053cff1 ("fuse: fixes after adapting to new posix acl api") used this facility to opt out of posix acl caching for fuse inodes with fuse server that does not negotiate FUSE_POSIX_ACL (fc->posix_acl). The commit also takes care to gate the forget_all_cached_acls() call in fuse_set_acl() on fc->posix_acl because there is no need for it, but there are other placed in fuse code which call forget_all_cached_acls() unconditional to fc->posix_acl and those cause the loss of the ACL_DONT_CACHE state. This is not only a functional bug. Properly timed, a get_acl() from this fuse filesystem can return a stale cached value, as was observed in tests, because set_acl() does not invalidate the unintentional acl cache. We could fix this in fuse, but it actually makes no sense for the vfs helper forget_cached_acl() to invalidate the ACL_DONT_CACHE state, so let it not do that to fix fuse and future users of ACL_DONT_CACHE. Fixes: facd61053cff1 ("fuse: fixes after adapting to new posix acl api") Cc: stable@vger.kernel.org Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://patch.msgid.link/20260713220932.413004-2-amir73il@gmail.com Reviewed-by: Luis Henriques <luis@igalia.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysfs/super: fix emergency thaw double-unlock of s_umountChen Changcheng1-2/+5
commit 503d67fbaec6fdeaba391cb497675071db9d16ea upstream. do_thaw_all() iterates over all superblocks via __iterate_supers() with SUPER_ITER_EXCL, which acquires s_umount exclusively before calling the callback and releases it afterwards. However, the callback do_thaw_all_callback() calls thaw_super_locked() which unconditionally releases s_umount on every code path. This results in a second unlock attempt in __iterate_supers() that corrupts the rwsem state, triggering a DEBUG_RWSEMS warning: [ 182.601148] sysrq: Emergency Thaw of all frozen filesystems [ 182.601865] ------------[ cut here ]------------ [ 182.602375] DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) && !rwsem_test_oflags(sem, RWSEM_NONSPINNABLE)): count = 0x0, magic = 0xffff99b1011e5870, owner = 0x0, curr 0xffff99b101b06c80, list not empty [ 182.603817] WARNING: kernel/locking/rwsem.c:1412 at up_write+0xa3/0x170, CPU#2: kworker/2:1/53 [ 182.604578] Modules linked in: [ 182.604864] CPU: 2 UID: 0 PID: 53 Comm: kworker/2:1 Not tainted 7.2.0-rc4-00001-gbd3bd93ea98a-dirty #4 PREEMPT(lazy) [ 182.605711] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.13.0-1kylin1 04/01/2014 [ 182.606417] Workqueue: events do_thaw_all [ 182.606750] RIP: 0010:up_write+0xaf/0x170 [ 182.607076] Code: 19 3a 92 48 0f 44 c2 48 8b 55 08 48 8b 55 00 4c 8b 45 08 48 8b 55 00 48 8d 3d ad 91 e0 01 48 8b 4d 20 50 48 c7 c6 f0 8c 26 92 <67> 48 0f b9 3a e8 d7 93 4e 00 58 eb 81 48 83 7f 18 00 48 c7 c2 8d [ 182.608563] RSP: 0018:ffffb670001d7e08 EFLAGS: 00010246 [ 182.609007] RAX: ffffffff92349e8d RBX: 0000000000000000 RCX: ffff99b1011e5870 [ 182.609595] RDX: 0000000000000000 RSI: ffffffff92268cf0 RDI: ffffffff92914d10 [ 182.610283] RBP: ffff99b1011e5870 R08: 0000000000000000 R09: ffff99b101b06c80 [ 182.610847] R10: ffff99b10139a808 R11: fefefefefefefeff R12: 0000000000000000 [ 182.611414] R13: ffffffff90cf74d0 R14: 0000000000000000 R15: ffff99b1011e5800 [ 182.612009] FS: 0000000000000000(0000) GS:ffff99b1eaaee000(0000) knlGS:0000000000000000 [ 182.612670] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 182.613146] CR2: 00000000005c631c CR3: 00000000013ee000 CR4: 00000000000006f0 [ 182.613722] Call Trace: [ 182.613946] <TASK> [ 182.614130] __iterate_supers+0x128/0x150 [ 182.614463] do_thaw_all+0x1b/0x30 [ 182.614759] process_scheduled_works+0xbb/0x3f0 [ 182.615150] ? __pfx_worker_thread+0x10/0x10 [ 182.615499] worker_thread+0x129/0x270 [ 182.615816] ? __pfx_worker_thread+0x10/0x10 [ 182.616201] kthread+0xe2/0x120 [ 182.616469] ? __pfx_kthread+0x10/0x10 [ 182.616792] ret_from_fork+0x15b/0x240 [ 182.617115] ? __pfx_kthread+0x10/0x10 [ 182.617426] ret_from_fork_asm+0x1a/0x30 [ 182.617761] </TASK> [ 182.617968] ---[ end trace 0000000000000000 ]--- [ 182.618412] Emergency Thaw complete Fix this by switching to SUPER_ITER_UNLOCKED and acquiring s_umount in the callback via super_lock_excl() before calling thaw_super_locked(). This matches the locking pattern expected by thaw_super_locked() and eliminates the double unlock. While at it, remove the dead 'return;' at the end of do_thaw_all_callback(). Fixes: 2992476528ae ("super: use a common iterator (Part 1)") Cc: stable@vger.kernel.org Signed-off-by: Chen Changcheng <chenchangcheng@kylinos.cn> Link: https://patch.msgid.link/20260721064140.152305-1-chenchangcheng@kylinos.cn Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysbinfmt_elf_fdpic: only honour the first PT_INTERPChristian Brauner1-0/+4
commit 3349ef6a366a61d631f6a263d12cea240957719d upstream. The program header scan handles PT_INTERP from a switch nested in the scan loop, so its break leaves the switch and not the loop. A binary carrying more than one PT_INTERP runs the case again and overwrites both interpreter_name and interpreter. The previous name allocation leaks and so does the previous interpreter reference, along with the write denial open_exec() took on it. The denial is never released, so the file stays unwritable for as long as the system runs. An unprivileged caller reaches this with a crafted binary and repeats it at will. binfmt_elf stops at the first PT_INTERP. Do the same here. The flaw dates back to the driver's introduction in the pre-git history tree introduced in v2.6.11 by 91808d6ebe39 ("[PATCH] FRV: Add FDPIC ELF binary format driver"). Link: https://patch.msgid.link/20260721-gezittert-medium-kreide-b41fc1f0277e@brauner Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: Jori Koolstra <jkoolstra@xs4all.nl> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysceph: fix writeback_count leak in write_folio_nounlock()Wentao Liang1-0/+9
commit cbf59617cd715219e84c50d106a3d0e1e8ba054e upstream. write_folio_nounlock() increments fsc->writeback_count to track in-flight writeback operations. On several error paths where the function returns early (folio lookup failure, snapshot context allocation failure, and writepages submission failure), the function returns without calling atomic_long_dec_return() to decrement the counter. Each leaked increment keeps the counter above zero, which can prevent the filesystem from cleanly unmounting or suspending writes. Add atomic_long_dec_return() calls on all error paths that currently return without decrementing the counter. Cc: stable@vger.kernel.org Fixes: d55207717ded ("ceph: add encryption support to writepage and writepages") Signed-off-by: Wentao Liang <vulab@iscas.ac.cn> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysceph: fix refcount leak in ceph_readdir()WenTao Liang1-1/+6
commit c3e64079d8b9663e3998d0caac9aba915b6b93ae upstream. The ceph_readdir() function allocates a ceph_mds_request via ceph_mdsc_create_request() and stores it in dfi->last_readdir. In the directory entry processing loop, if the entry's offset is less than ctx->pos or if the inode pointer is unexpectedly NULL, the function returns -EIO without releasing the reference held by dfi->last_readdir, causing a refcount leak. Fix this by adding ceph_mdsc_put_request(dfi->last_readdir) before returning on these error paths. Also set dfi->last_readdir to NULL for safety, matching the cleanup done at the normal exit. Cc: stable@vger.kernel.org Fixes: af9ffa6df7e3 ("ceph: add support to readdir for encrypted names") Signed-off-by: WenTao Liang <vulab@iscas.ac.cn> Reviewed-by: Viacheslav Dubeyko <slava@dubeyko.com> Reviewed-by: Alex Markuze <amarkuze@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysceph: fix pre-auth out-of-bounds read on snaptrace in ceph_handle_caps()Bryam Vargas1-0/+1
commit 4dbc71bcaf9a30abf3920a4e2cc4ed33bba78c02 upstream. ceph_handle_caps() reads snap_trace_len from the wire-format ceph_mds_caps header and uses it unconditionally to build a fake end pointer (snaptrace + snaptrace_len) that is later handed to ceph_update_snap_trace() in the CEPH_CAP_OP_IMPORT case: snaptrace = h + 1; snaptrace_len = le32_to_cpu(h->snap_trace_len); p = snaptrace + snaptrace_len; ... case CEPH_CAP_OP_IMPORT: if (snaptrace_len) { ... if (ceph_update_snap_trace(mdsc, snaptrace, snaptrace + snaptrace_len, false, &realm)) { ... } ceph_update_snap_trace() then decodes a struct ceph_mds_snap_realm from snaptrace using ceph_decode_need(&p, e, sizeof(*ri), bad) with the attacker-supplied fake end e == snaptrace + snaptrace_len. With snaptrace_len == 0xFFFFFFFF the bound check is trivially satisfied, ri = p reads sizeof(struct ceph_mds_snap_realm) past the legitimate msg->front buffer, and ri->num_snaps / ri->num_prior_parent_snaps then drive further out-of-bounds reads of the encoded snap arrays. The eleven msg_version >= 2 .. msg_version >= 12 decoder blocks above the op switch each catch this OOB through their ceph_decode_*_safe() / ceph_decode_need() helpers, but they sit behind a hdr.version-gated if, so a malicious or compromised MDS that sets msg->hdr.version = 1 reaches the IMPORT path with no version-gated decoder having validated snap_trace_len. The shape has been present since ceph_handle_caps() was introduced. Validate snap_trace_len against the message front buffer before consuming it, using the canonical ceph_decode_need() / ceph_has_room() helper. The helper bounds the length with subtraction (n <= end - p, guarded by end >= p) rather than pointer addition, so it is wrap-safe for the attacker-controlled u32 length on 32-bit builds where p + snap_trace_len could overflow the address space. This matches the rest of the ceph decode path (e.g. the pool_ns_len check a few lines below), and the existing goto bad cleanup already covers this exit path. Cc: stable@vger.kernel.org Fixes: a8599bd821d0 ("ceph: capability management") Signed-off-by: Bryam Vargas <hexlabsecurity@proton.me> Reviewed-by: Viacheslav Dubeyko <Slava.Dubeyko@ibm.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysbtrfs: do not try compression for data reloc inodesQu Wenruo1-0/+2
commit ae4316f332e03e628712e9dfb89f2b7d3c70c21a upstream. [BUG] There is a syzbot report that the check inside get_new_location() triggered: BTRFS info (device loop0): found 31 extents, stage: move data extents BTRFS info (device loop0): leaf 8908800 gen 16 total ptrs 28 free space 1676 owner 18446744073709551607 item 0 key (256 INODE_ITEM 0) itemoff 3835 itemsize 160 inode generation 5 transid 0 size 0 nbytes 0 block group 0 mode 40755 links 1 uid 0 gid 0 rdev 0 sequence 0 flags 0x0 atime 1669132761.0 ctime 1669132761.0 mtime 1669132761.0 otime 0.0 item 1 key (256 INODE_REF 256) itemoff 3823 itemsize 12 index 0 name_len 2 item 2 key (258 INODE_ITEM 0) itemoff 3663 itemsize 160 inode generation 1 transid 16 size 733184 nbytes 106496 block group 0 mode 100600 links 0 uid 0 gid 0 rdev 0 sequence 24 flags 0x18 item 3 key (258 EXTENT_DATA 0) itemoff 3595 itemsize 68 generation 16 type 0 inline extent data size 47 ram_bytes 4096 compression 1 [...] item 27 key (18446744073709551611 ORPHAN_ITEM 258) itemoff 2376 itemsize 0 BTRFS error (device loop0): unexpected non-zero offset in file extent item for data reloc inode 258 key offset 0 offset 9277520992061368337 ------------[ cut here ]------------ btrfs_abort_should_print_stack(__error) [CAUSE] The above dump tree shows the first file extent item is inlined, which should make no sense for data reloc inodes, as such inodes just represent where the data extents are in the relocation destination chunk. However the relocation path preallocates space for each block, then dirties them, cluster by cluster. It's possible to have a single block at the beginning of the block group, and no other block in the same cluster. So relocation will preallocate a file extent for that block and dirty the first block. Then memory pressure forces the data reloc inode to be written back, before any other blocks are dirtied/allocated. Finally commit 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated delalloc helper") changed the sequence of delalloc. Before that commit we always tried NOCOW first, so that dirtied block would be written back into the preallocated space, and appear as a regular extent. But with that commit, we always try inline first, and since compression is forced, we try compressing the first block, and then inline the compressed data, resulting in the above inlined file extent in the data reloc tree. Then the check in get_new_location() will check the file offset, without checking if the file extent is inlined or not, resulting in the above failure. [FIX] Do not allow compression for data reloc inodes. Since data reloc inode sizes are always block aligned, as long as we do not compress, @data_len will always be at least one block, and that will cause can_cow_file_range_inline() to return false, thus no inlined extent will be created. Reported-by: syzbot+d950c6ba09b79f6e1864@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/6a373dc5.764cf64f.168fbe.0001.GAE@google.com/ Fixes: 3eaf5f082c4c ("btrfs: extract inlined creation into a dedicated delalloc helper") CC: stable@vger.kernel.org Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysafs: Fix afs_edit_dir_remove() to get, not find, block 0David Howells1-1/+1
commit 62d9853aa4ce6e9797b6949804891be14b219752 upstream. Fix afs_edit_dir_remove() to use afs_dir_get_block() to get block 0 rather than afs_dir_find_block() as the latter caches the found block in the afs_dir_iter and may[*] switch out the page it's on if another afs_dir_find_block() is done. This parallels what afs_edit_dir_add() does. [*] There's more than one block per page. Fixes: a5b5beebcf96 ("afs: Use the contained hashtable to search a directory") Closes: https://sashiko.dev/#/patchset/20260706153408.1231650-1-dhowells%40redhat.com Signed-off-by: David Howells <dhowells@redhat.com> Link: https://patch.msgid.link/2380759.1783956175@warthog.procyon.org.uk cc: Marc Dionne <marc.dionne@auristor.com> cc: linux-afs@lists.infradead.org cc: linux-fsdevel@vger.kernel.org cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysbinfmt_misc: set have_execfd only once the interpreter is openedChristian Brauner1-3/+2
commit bbf5f639918dc011aaf60aab8480218758ee68c5 upstream. load_misc_binary() raises bprm->have_execfd as soon as it sees the 'O' (or 'C') flag. This happens well before it opens the interpreter. If that open fails the flag stays set on the bprm. binfmt_misc is at the head of the format list so an interpreter open failure that returns -ENOEXEC lets the search fall through to a later format. This means it runs the matched binary directly having never staged an interpreter. So bprm->executable is NULL while have_execfd falsely claims a descriptor is present. Consequently, begin_new_exec() dereferences the missing executable: would_dump(bprm, bprm->executable); and NULL derefs. Had it not, the hand-off later in the same function would have failed anyway. FD_ADD(0, bprm->executable) rejects a NULL file with -ENOMEM. Both sites are past the point of no return so the exec cannot be unwound either way. This can be reached by unprivileged users as binfmt_misc can be mounted in user namespaces. So a user can register an 'O' entry whose interpreter lives on a FUSE mount, have the FUSE server fail the open with -ENOEXEC and execute a native ELF file that matches the entry. have_execfd only means anything alongside the executable it describes which is not set until the interpreter has been opened and staged. So lets raise it there, next to execfd_creds, which is already set at that point. An open failure now leaves it clear, so the fallback format derives credentials from the binary and emits no AT_EXECFD, as it would for any native exec. The argv rewrite load_misc_binary() performs before the open is still not undone. This means the binary sees the interpreter path in argv[0] and its own path in argv[1] but that predates this change and only became observable once the exec stopped faulting. Link: https://patch.msgid.link/20260720-beglichen-kognitiv-organismus-5e1e55326c56@brauner Fixes: bc2bf338d54b ("exec: Remove recursion from search_binary_handler") Cc: stable@vger.kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 daysexec: fix unsigned loop counter wrap in transfer_args_to_stack()Christian Brauner1-1/+1
commit 16cc4f5c1c4b9e45eca7f7deefa5410a292db599 upstream. The stop value is derived from bprm->p >> PAGE_SHIFT. The index variable is an unsigned long. If bprm->p drops below PAGE_SIZE and stop becomes zero the loop condition index >= stop is always true. After the index == 0 iteration the decrement wraps to ULONG_MAX and bprm->page[ULONG_MAX] reads sizeof(void *) bytes in front of the array. The pointer has wrapped to -1. That garbage pointer is then passed to kmap_local_page() and PAGE_SIZE bytes are copied from wherever that lands into the stack of the process being created. And the loop doesn't terminate either... Getting there only requires bprm->p < PAGE_SIZE. On !MMU bprm_set_stack_limit() and bprm_hit_stack_limit() are empty. So the only constraint on how far bprm->p is pushed down is valid_arg_len(), i.e. that each individual string still fits in what is left. bprm->p starts at PAGE_SIZE * MAX_ARG_PAGES - sizeof(void *) so a single argument or environment string of a little over 31 pages leaves it in the first page: Oops - load access fault [#1] CPU: 0 UID: 0 PID: 1 Comm: victim Not tainted 7.2.0-rc4 #1 epc : __memcpy+0xd4/0xf8 ra : transfer_args_to_stack+0xaa/0xae s4 : ffffffffffffffff s2 : 0000000000000000 a1 : ffffffdc98000000 a2 : 0000000000001000 status: 0000000a00001880 badaddr: ffffffdc98000000 cause: 0000000000000005 [<801a5324>] __memcpy+0xd4/0xf8 [<800d5f6a>] load_flat_binary+0x43a/0x65e [<800a2de4>] bprm_execve+0x1d4/0x316 [<800a351a>] do_execveat_common+0x12e/0x138 [<800a3d44>] __riscv_sys_execve+0x38/0x4e Kernel panic - not syncing: Fatal exception in interrupt This is an arcane bug but we should still fix it. Count down from MAX_ARG_PAGES so the loop ends when index reaches stop, stop == 0 included. The iterations performed are unchanged for every other value of stop. Only CONFIG_MMU=n builds are affected, transfer_args_to_stack() is used by binfmt_flat and binfmt_elf_fdpic on nommu only. The loop predates git history. commit 7e7ec6a93434 ("elf_fdpic_transfer_args_to_stack(): make it generic") only moved it from binfmt_elf_fdpic.c into fs/exec.c and narrowed the copy to the used part of the first page. The condition and the decrement are unchanged from 2.6.12-rc2. Link: https://patch.msgid.link/20260721-hochachtung-staumauer-pigmente-15d71f7d7d04@brauner Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Cc: stable@vger.kernel.org Reviewed-by: David Hildenbrand (Arm) <david@kernel.org> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
3 dayspidfs: make pidfs_ino_lock staticMateusz Guzik1-1/+1
[ Upstream commit c1d04c1bce98f9dd984a9c6657278a7761854c9c ] Fixes: 87caaeef7995 ("pidfs: implement ino allocation without the pidmap lock") Reported-by: kernel test robot <lkp@intel.com> Closes: https://lore.kernel.org/oe-kbuild-all/202607231547.ehCQxi0L-lkp@intel.com/ Signed-off-by: Mateusz Guzik <mjguzik@gmail.com> Link: https://patch.msgid.link/20260723160114.291515-1-mjguzik@gmail.com Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysiomap: fix incorrect did_zero setting in iomap_zero_iter()Zhang Yi1-2/+6
[ Upstream commit 7a6fd6b21d7e1737b40de1a210acf9e6a1e4d59e ] The did_zero output parameter was unconditionally set after the loop, which is incorrect. It should only be set when the zeroing operation actually completes, not when IOMAP_F_STALE is set or when IOMAP_F_FOLIO_BATCH is set but !folio causes the loop to break early, or when iomap_iter_advance() returns an error. This causes did_zero to be incorrectly set when zeroing a clean unwritten extent because the loop exits early without actually zeroing any data. Fix it by using a local variable to track whether any folio was actually zeroed, and only set did_zero after the loop if zeroing happened. Fixes: 98eb8d95025b ("iomap: set did_zero to true when zeroing successfully") Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Link: https://patch.msgid.link/20260714082325.325163-4-yi.zhang@huaweicloud.com Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysiomap: correct the range of a partial dirty clearZhang Yi1-4/+8
[ Upstream commit 88c26515313169806a412a362b32a1eca53d21bd ] The block range calculation in ifs_clear_range_dirty() is incorrect when partially clearing a range in a folio. We cannot clear the dirty bit of the first block or the last block if the start or end offset is not blocksize-aligned. This has not yet caused any issues since we always clear a whole folio in iomap_writeback_folio(). Fix this by rounding up the first block to blocksize alignment, and calculate the last block by rounding down (using truncation). Correct the nr_blks calculation accordingly. Fixes: 4ce02c679722 ("iomap: Add per-block dirty state tracking to improve performance") Signed-off-by: Zhang Yi <yi.zhang@huawei.com> Link: https://patch.msgid.link/20260714082325.325163-2-yi.zhang@huaweicloud.com Reviewed-by: Joanne Koong <joannelkoong@gmail.com> Reviewed-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysovl: check access to copy_file_range source with src mounter credsAmir Goldstein1-1/+15
[ Upstream commit a1e0eb8f55cfe09bb31a202a388babc411292656 ] Commit 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") allowed filesystems that implement the copy_file_range() f_op to decide if they want to access cross-sb copy from/to the same fs type. The same commit added checks to verify same sb copy for filesystems that implement ->copy_file_range() and do not support cross-sb copy at the time, namely, to ceph, fuse and nfs. The two remaining fs which implement ->copy_file_range(), cifs and overlayfs started to support cross-sb copy from this time. While overlayfs does support cross-sb copy when the two underlying files are on the same base fs, the copy operation on the two real files from two different overalyfs filesystems is performed with the mounter creds of the destination overlayfs and the read permission access hook for the source file was called with the wrong creds. This could cause either deny of access to copy which would otherwise be allowed (e.g. with splice) or allow read access to file which would otherwise be denied. Fix the latter case by explicitly verifying read access to source file with the source overlayfs mounter creds. The former case remains a quirk of cross-sb overlayfs copy, but userspace could fall back to regular copy so no harm done. Fixes: 5dae222a5ff0c ("vfs: allow copy_file_range to copy across devices") Signed-off-by: Amir Goldstein <amir73il@gmail.com> Link: https://patch.msgid.link/20260712122421.203113-1-amir73il@gmail.com Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysovl: fix trusted xattr escape prefix matchingYichong Chen1-1/+1
[ Upstream commit a8e72879cd0d8422c0b47d6d3c1802274fe73b98 ] In the trusted.* xattr namespace, ovl_is_escaped_xattr() compares one byte less than the escaped overlay xattr prefix length. This makes it match "trusted.overlay.overlay" without requiring the trailing dot. As a result, an xattr such as "trusted.overlay.overlayfoo" is incorrectly treated as an escaped overlay xattr. This can be reproduced by setting "trusted.overlay.overlayfoo" on a lower file and listing xattrs through an overlay mount. listxattr() then exposes it as "trusted.overlay.oo", and a following getxattr() on that listed name fails with ENODATA. Compare the full escaped prefix, including the trailing dot, so similarly-prefixed private xattrs are not misclassified. Fixes: dad02fad84cbc ("ovl: Support escaped overlay.* xattrs") Signed-off-by: Yichong Chen <chenyichong@uniontech.com> Link: https://patch.msgid.link/20260708082221.633602-1-chenyichong@uniontech.com Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 dayscifs: fix cifsFileInfo leak on kmalloc failure in deferred close drain pathsFrank Sorenson1-6/+39
[ Upstream commit c2f2e83e3bbc5483730fd4ee903182761f1ae50f ] In cifs_close_deferred_file(), cifs_close_all_deferred_files(), and cifs_close_deferred_file_under_dentry(), when a pending deferred close is cancelled via cancel_delayed_work(), the subsequent kmalloc_obj() to add the file to the local processing list may fail under memory pressure. The loop breaks immediately, but the cancelled work is no longer pending (it would have called _cifsFileInfo_put()), and the cfile is never added to file_head for processing. The cifsFileInfo reference and the open server handle both leak. Fix by saving the cfile that failed allocation in a local variable, breaking as before, and calling _cifsFileInfo_put() on it after releasing the lock. Any files later in the iteration are unaffected since their deferred work is still pending and will fire normally. Fixes: e3fc065682eb ("cifs: Deferred close performance improvements") Signed-off-by: Frank Sorenson <sorenson@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 dayscifs: prevent readdir from changing file size due to stale directory metadataFrank Sorenson5-8/+96
[ Upstream commit e8a8d54c2d508891c142a928fc7d298c4c8bd0dd ] Windows Server's directory enumeration metadata lags behind the actual file size after a write+close or rename. A concurrent readdir() in the window between close() returning to userspace and stat() being called overwrites the correct cached i_size with the stale server value, causing stat() to return the wrong size. Once _cifsFileInfo_put() removes the last writable handle from openFileList, is_size_safe_to_change() permits readdir to overwrite i_size. smb2_close_getattr() then stamps cifs_i->time = jiffies, making the corrupt cached value appear fresh to the next stat(). The existing check (see Fixes:) only blocked stale size updates while an active RW lease was held, not after the last writable handle closes. Add cifsInodeInfo->time_last_write, written via smp_store_release() at writable close and on setattr/truncate. is_size_safe_to_change() checks is_inode_writable() first (acquiring open_file_lock), then rejects a readdir size update if time_last_write falls within acregmax jiffies. The spinlock release in _cifsFileInfo_put() forms a store-release barrier that pairs with the spin_lock() (load-acquire) in is_inode_writable(), ensuring the subsequent smp_load_acquire() on time_last_write observes any update from a concurrent close(). When a size update is rejected and the server value differs from the cached one, cifs_i->time is cleared to force a fresh QUERY_INFO on the next stat(). readdir is also blocked from changing i_size while writable handles are open or an RW lease is held, even on direct-IO mounts. For deferred close (closetimeo > 0), time_last_write is refreshed at the actual server close in smb2_deferred_work_close() and in the cifs_close_deferred_file*() drain paths invoked by lease/oplock breaks and tcon teardown, anchoring the protection window to the real close time rather than the earlier userspace close. time_last_write == 0 skips the time_before() check to avoid false positives near boot on 32-bit systems where jiffies starts close to INITIAL_JIFFIES. Does not reproduce against Samba or with actimeo=0. Fixes: e4b61f3b1c67 ("cifs: prevent updating file size from server if we have a read/write lease") Signed-off-by: Frank Sorenson <sorenson@redhat.com> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 dayssmb: client: validate DFS referral PathConsumedYichong Chen1-9/+25
[ Upstream commit f6f5ee2aa33b350c671721b965251c42cebb962e ] parse_dfs_referrals() validates that the response contains the fixed referral entry array and, on for-next, the per-referral string offsets. However, the response also contains a PathConsumed value that is later used for DFS path parsing. If a malformed response provides a PathConsumed value larger than the search name, later DFS parsing can advance beyond the end of the path. Validate PathConsumed against the search name length before storing it in the parsed referral. Fixes: 4ecce920e13a ("CIFS: move DFS response parsing out of SMB1 code") Reviewed-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Signed-off-by: Yichong Chen <chenyichong@uniontech.com> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysbtrfs: free mapping node on duplicate reloc root insertGuanghui Yang1-0/+1
[ Upstream commit 6a8269b6459ed870a8156c106a0f597383907872 ] __add_reloc_root() allocates a mapping_node before inserting it into rc->reloc_root_tree. If rb_simple_insert() finds an existing entry, it returns the existing rb_node and leaves the newly allocated node unlinked. The error path then returns -EEXIST without freeing the new node. Since the node was never inserted into reloc_root_tree, the later cleanup in put_reloc_control() cannot find it either. Free the newly allocated node before returning -EEXIST. The callers currently assert that -EEXIST should not happen, so this is a defensive cleanup for an unexpected duplicate insert path. If the path is ever reached, the local allocation should still be released. Fixes: 57a304cfd43b ("btrfs: do not panic in __add_reloc_root") Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Guanghui Yang <3497809730@qq.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysbtrfs: don't propagate EXTENT_FLAG_LOGGING to split extent mapsLeo Martins1-2/+2
[ Upstream commit 5eff4d5b17fa1950e80bfd1ba43dc0699e61a644 ] When btrfs_drop_extent_map_range() splits an extent map, the new split maps inherit the original map's flags through a local 'flags' variable. Commit f86f7a75e2fb ("btrfs: use the flags of an extent map to identify the compression type") changed the EXTENT_FLAG_LOGGING clearing to operate on em->flags instead of that local 'flags' copy, so a split of an extent map that is currently being logged wrongly inherits EXTENT_FLAG_LOGGING. The flag is then never cleared on the split, and when it is freed while still on the inode's modified_extents list (for example by the extent map shrinker) it trips the WARN_ON(!list_empty(&em->list)) in btrfs_free_extent_map() and leads to a use-after-free. Clear EXTENT_FLAG_LOGGING from the local 'flags' copy used for the splits and only clear EXTENT_FLAG_PINNED from em->flags, restoring the behaviour prior to f86f7a75e2fb. CC: Jeff Layton <jlayton@kernel.org> Link: https://lore.kernel.org/all/20260629-btrfs-skip-logging-v1-1-4e3a28c1acaf@kernel.org/ Fixes: f86f7a75e2fb ("btrfs: use the flags of an extent map to identify the compression type") Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Leo Martins <loemra.dev@gmail.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysbtrfs: fix u32 to s64 type conversion in dirty_metadata_bytes accountingDave Chen1-2/+2
[ Upstream commit 8b5a09ceb61b18b1f0797cd30a549d7dc85d8d50 ] The percpu_counter dirty_metadata_bytes is updated by negating eb->len and passing it to percpu_counter_add_batch(), whose amount parameter is s64. Since commit 84cda1a6087d ("btrfs: cache folio size and shift in extent_buffer"), eb->len is u32. The u32 result of -eb->len, when widened to the s64 parameter, becomes a large positive value instead of the intended negative value. For eb->len == 16384 the counter adds +4294950912 instead of subtracting 16384. The counter therefore grows on every metadata writeback instead of shrinking by the extent buffer size, permanently exceeding BTRFS_DIRTY_METADATA_THRESH and causing __btrfs_btree_balance_dirty() to trigger balance_dirty_pages_ratelimited() unconditionally, adding unnecessary writeback pressure. Cast eb->len to s64 before negation at both call sites so the subtraction is performed in signed 64-bit arithmetic. Reviewed-by: Filipe Manana <fdmanana@suse.com> Fixes: 84cda1a6087d ("btrfs: cache folio size and shift in extent_buffer") Signed-off-by: Dave Chen <davechen@synology.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysksmbd: validate compound request size before reading StructureSize2Xiang Mei (Microsoft)1-0/+5
[ Upstream commit 15b38176fd1530372905c602fde51fe89ec8c877 ] When ksmbd validates a compound (chained) SMB2 request, ksmbd_smb2_check_message() reads pdu->StructureSize2 without first checking that the compound element is large enough to contain it. StructureSize2 is a 2-byte field at offset 64 (__SMB2_HEADER_STRUCTURE_SIZE) from the start of each element. The compound-walking logic only guarantees that a full 64-byte SMB2 header is present for the trailing element: when NextCommand is 0, len is reduced to the number of bytes remaining after next_smb2_rcv_hdr_off. A remote client can craft a compound request whose last element has exactly 64 bytes, so the 2-byte StructureSize2 read at offset 64 extends one byte past the receive buffer, producing a slab-out-of-bounds read. BUG: KASAN: slab-out-of-bounds in ksmbd_smb2_check_message (fs/smb/server/smb2misc.c:402) Read of size 2 at addr ffff888012ae31ac by task kworker/0:1/14 The buggy address is located 172 bytes inside of allocated 173-byte region Workqueue: ksmbd-io handle_ksmbd_work Call Trace: ... kasan_report (mm/kasan/report.c:595) ksmbd_smb2_check_message (fs/smb/server/smb2misc.c:402) handle_ksmbd_work (fs/smb/server/server.c:119) process_one_work (kernel/workqueue.c:3314) worker_thread (kernel/workqueue.c:3397) kthread (kernel/kthread.c:436) ret_from_fork (arch/x86/kernel/process.c:158) ret_from_fork_asm (arch/x86/entry/entry_64.S:245) Reject any compound element that is too small to hold StructureSize2 before dereferencing it. Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3") Reported-by: AutonomousCodeSecurity@microsoft.com Signed-off-by: Xiang Mei (Microsoft) <xmei5@asu.edu> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysksmbd: pin conn during async oplock break notificationQihang1-2/+4
[ Upstream commit aa5d8f3f96aa11a4a54ce993c11ce8af11c546f9 ] smb2_oplock_break_noti() and smb2_lease_break_noti() store a ksmbd_conn pointer in an async ksmbd_work and then queue that work on ksmbd-io. The work only increments conn->r_count, which prevents teardown from passing the pending-request wait after the increment, but it does not pin the struct ksmbd_conn object. If connection teardown races with an oplock break notification, the last conn reference can be dropped before the queued worker finishes. The worker then uses the freed conn in ksmbd_conn_write() and ksmbd_conn_r_count_dec(). Take a real conn reference when publishing the conn pointer to the async work item, and drop it after the notification work has decremented r_count. Apply the same lifetime rule to lease break notification, which uses the same work->conn pattern. Fixes: 3aa660c05924 ("ksmbd: prevent connection release during oplock break notification") Signed-off-by: Qihang <q.h.hack.winter@gmail.com> Acked-by: Namjae Jeon <linkinjeon@kernel.org> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 dayssmb/client: handle overlapping allocated ranges in fallocateHuiwen He1-7/+18
[ Upstream commit b09ae45d85dc816987a71db9eebc54b0ae288e94 ] smb3_simple_fallocate_range() can skip holes when an allocated range returned by the server starts before the current fallocate offset. The skipped hole is not zero-filled, but fallocate still returns success. A later write to that hole may therefore fail with ENOSPC. The function queries allocated ranges so that it can preserve existing contents and write zeroes only into holes. However, the server may return a range that starts before the current fallocate offset. For example, assume the fallocate request is [100, 400) and the only allocated range returned by the server is [0, 200): Request: [100, 400) Server range: [ 0, 200) allocated Correct: [100, 200) allocated data, skip [200, 400) hole, zero-fill Current: [100, 300) skipped [300, 400) zero-filled afterwards The current code adds the full server range length, 200, to the current offset 100 and moves to 300. As a result, the hole in [200, 300) is skipped without being zero-filled. Fix this by advancing only over the part of the allocated range that overlaps the current fallocate offset. Ignore ranges that end before the current offset and reject ranges whose end offset overflows. This also prevents a malformed range length from causing an out-of-bounds zero-buffer read. Fixes: 966a3cb7c7db ("cifs: improve fallocate emulation") Signed-off-by: Huiwen He <hehuiwen@kylinos.cn> Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn> Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 dayserofs: relax sanity check for tail pclusters due to ztailpackingGao Xiang1-1/+2
[ Upstream commit d3386e17393bec1341cfeedb9d08d6846ccd6fb2 ] If the tail data can be inlined into the inode meta block, it should be converted into a regular tail pcluster. In principle, it should be converted into an uncompressed pcluster if there is not enough gain to use compression (map->m_llen < map->m_plen); but since there are various shipped images, relax the condition for ztailpacking tail pcluster fallback instead of reporting corruption incorrectly. Reported-and-tested-by: Yifan Zhao <zhaoyifan28@huawei.com> Reported-by: Alberto Salvia Novella <es20490446e@gmail.com> Closes: https://github.com/erofs/erofs-utils/issues/51 Fixes: a5242d37c83a ("erofs: error out obviously illegal extents in advance") Signed-off-by: Gao Xiang <hsiangkao@linux.alibaba.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysbtrfs: fallback to transaction csum tree on a commit root csum missBoris Burkov1-1/+25
[ Upstream commit 3dcd50730814e5220072d2b26d0587af6bfb6dbe ] We have been running with commit root csums enabled for some time and have noticed a slight uptick in zero csum errors. Investigating those revealed that they were same transaction reads of extents that were just relocated, but the extent map generation was long ago. It turns out that relocation intentionally does not update the extent generation (replace_file_extents()), but must write a new csum since the data has moved, so we must account for this with commit root csum reading. Luckily this is a short lived condition: after the relocation transaction the commit root will once again have the csum. So we can add a generic fallback to the lookup to try again with the transaction csum root. Fixes: f07b855c56b1 ("btrfs: try to search for data csums in commit root") Reviewed-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Boris Burkov <boris@bur.io> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysbtrfs: fix root leak if its reloc root is unexpected in merge_reloc_roots()Filipe Manana1-0/+1
[ Upstream commit ce6050bafb4e33377dc17fcc357736bfc351180c ] If we have an unexpected reloc_root for our root, we jump to the out label but never drop the reference we obtained for root, resulting in a leak. Add a missing btrfs_put_root() call. Fixes: 24213fa46c70 ("btrfs: do proper error handling in merge_reloc_roots") Reviewed-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com> Signed-off-by: Filipe Manana <fdmanana@suse.com> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
3 daysbtrfs: reject free space cache with more entries than pagesXiang Mei1-0/+3
[ Upstream commit a2d8d5647ed854e38f941741aea45b9eb15a6350 ] When loading a v1 free space cache, __load_free_space_cache() takes num_entries and num_bitmaps straight from the on-disk btrfs_free_space_header. That header is stored in the tree_root under a key with type 0, which the tree-checker has no case for, so neither count is validated before the load trusts it. The load loops num_entries times and maps the next page whenever the current one runs out, going through io_ctl_check_crc() -> io_ctl_map_page(), which does io_ctl->pages[io_ctl->index++]. But pages[] is allocated in io_ctl_init() from the cache inode's i_size, not from num_entries: num_pages = DIV_ROUND_UP(i_size_read(inode), PAGE_SIZE); io_ctl->pages = kcalloc(num_pages, sizeof(struct page *), GFP_NOFS); So if num_entries claims more records than the pages can hold, io_ctl->index runs off the end of pages[]. The write side never hits this because io_ctl_add_entry() and io_ctl_add_bitmap() both stop once io_ctl->index >= io_ctl->num_pages; the read side just never had the same check. To trigger it, take a clean cache (num_entries = <N> here), set num_entries in the header to 0x10000, and fix up the leaf checksum so it still passes the tree-checker. The cache inode has i_size = 65536, so num_pages is 16 and pages[] is a 16-pointer (kmalloc-128) array. The load now tries to read 65536 entries, io_ctl->index walks up to 16, and pages[16] is read past the array: BUG: KASAN: slab-out-of-bounds in io_ctl_check_crc (fs/btrfs/free-space-cache.c:420 fs/btrfs/free-space-cache.c:565) Read of size 8 at addr ffff88800c833a80 by task kworker/u8:3/58 io_ctl_check_crc (fs/btrfs/free-space-cache.c:420 fs/btrfs/free-space-cache.c:565) __load_free_space_cache (fs/btrfs/free-space-cache.c:655 fs/btrfs/free-space-cache.c:820) load_free_space_cache (fs/btrfs/free-space-cache.c:1017) caching_thread (fs/btrfs/block-group.c:880) btrfs_work_helper (fs/btrfs/async-thread.c:312) process_one_work worker_thread kthread ret_from_fork free-space-cache.c:420 is io_ctl_map_page(), inlined into io_ctl_check_crc() at line 565, which is why that is the frame KASAN names. The out-of-bounds slot is then treated as a struct page and handed to crc32c(), so the bad read turns into a GP fault. Add the missing check to io_ctl_check_crc(), which is where both the entry loop and the bitmap loop end up. When num_entries is too large the load now fails like any corrupt cache: __load_free_space_cache() drops it and rebuilds the free space from the extent tree, so a valid cache is never rejected. Reported-by: Weiming Shi <bestswngs@gmail.com> Fixes: 5b0e95bf607d ("Btrfs: inline checksums into the disk free space cache") Link: https://lore.kernel.org/linux-btrfs/CAPpSM+RMPByMCKXvM5QFKToxsyNccfuFLWMdD0mfd0wh2Ja62w@mail.gmail.com/ Assisted-by: Claude:claude-opus-4-8 Reviewed-by: Qu Wenruo <wqu@suse.com> Signed-off-by: Xiang Mei <xmei5@asu.edu> Reviewed-by: David Sterba <dsterba@suse.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
13 daysiomap: consolidate bio submissionChristoph Hellwig4-13/+14
[ Upstream commit 044472d5ee7d71f918fa3f61bd65e4933a0c006e ] 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> Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
13 daysexfat: fix implicit declaration of brelse()Namjae Jeon1-0/+1
[ Upstream commit f0d1b2e1e02c11e29c953f22a485449e3da2a575 ] exfat_cluster_walk() calls brelse(bh) without including the header that declares the function, causing the following build error: fs/exfat/exfat_fs.h:542:9: error: implicit declaration of function ‘brelse’ [-Werror=implicit-function-declaration] Fix this by adding the missing buffer_head.h in exfat_fs.h. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
13 daysexfat: add data_start_bytes and exfat_cluster_to_phys_bytes() helperNamjae Jeon2-0/+9
[ Upstream commit 3b9450beeb961c7ddf4cccddba37fcfa6d1fcb7a ] This caches the data area start offset in bytes (data_start_bytes) and introduces a helper function exfat_cluster_to_phys_bytes() to compute the physical byte position of a given cluster. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
13 daysexfat: add balloc parameter to exfat_map_cluster() for iomap supportNamjae Jeon1-2/+5
[ Upstream commit 3474416bb9abafca2e79b7ff700a100b7e8bc766 ] In preparation for supporting the iomap infrastructure, we need to know whether a new cluster was allocated or not in exfat_map_cluster(). Add an optional 'bool *balloc' output parameter. When a new cluster is allocated, *balloc is set to true. Pass NULL from exfat_get_block() to preserve the existing behavior. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
13 daysexfat: replace unsafe macros with static inline functionsNamjae Jeon8-86/+147
[ Upstream commit f07db38084dc45162d5fe2871ed72674ddc0f9ae ] The current exFAT driver relies on various macros for unit conversions between clusters, blocks, sectors, and directory entries. These macros are structurally unsafe as they lack type enforcement and are prone to potential integer overflows during bit-shift operations, especially on 64-bit architectures. Replace all arithmetic macros with static inline functions to provide strict type checking and explicit casting. Acked-by: Christoph Hellwig <hch@lst.de> Acked-by: "Darrick J. Wong" <djwong@kernel.org> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org> Stable-dep-of: 044472d5ee7d ("iomap: consolidate bio submission") Signed-off-by: Sasha Levin <sashal@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>