summaryrefslogtreecommitdiff
path: root/fs
AgeCommit message (Collapse)AuthorFilesLines
2026-08-12ubifs: fix out-of-bounds read in signature length checkIbrahim Hashimov1-1/+1
ubifs_sb_verify_signature() bounds the on-disk ubifs_sig_node->len field before handing the signature payload to verify_pkcs7_signature(), but the check has the wrong sign: if (le32_to_cpu(signode->len) > snod->len + sizeof(struct ubifs_sig_node)) The signature bytes start sizeof(struct ubifs_sig_node) (UBIFS_SIG_NODE_SZ, 64 bytes) into the node, so the payload is at most snod->len - sizeof(struct ubifs_sig_node) bytes long. Adding the header size instead of subtracting it accepts a declared length up to 2 * UBIFS_SIG_NODE_SZ larger than the node actually holds -- past the end of c->sbuf, which is vmalloc(c->leb_size). verify_pkcs7_signature() -> pkcs7_parse_message() -> asn1_ber_decoder() is then handed that inflated length and reads beyond the allocation while walking the DER headers. The node length comes straight from the mounted image, so a crafted signed UBIFS image reaches this via ubifs_read_superblock() before the signature is cryptographically checked. snod->len is guaranteed to be >= UBIFS_SIG_NODE_SZ by the node scanner (c->ranges[UBIFS_SIG_NODE].min_len == UBIFS_SIG_NODE_SZ), so the corrected subtraction cannot underflow. Legitimately signed images are unaffected: a correct superblock never declares a signature longer than the node it is embedded in. Fixes: 817aa094842d ("ubifs: support offline signed images") Cc: stable@vger.kernel.org Signed-off-by: Ibrahim Hashimov <security@auditcode.ai> Assisted-by: AuditCode-AI:2026.07 Reviewed-by: Richard Weinberger <richard@nod.at> Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com> Signed-off-by: Richard Weinberger <richard@nod.at>
2026-08-11ubifs: ubifs.h: clean up kernel-doc commentsRandy Dunlap1-8/+12
- use the struct keyword when describing a struct in kernel-doc format. - add or correct missing struct members @leaf, @eof, and @superblock_need_write. - add Returns: clauses for 4 function descriptions. to prevent kernel-doc warnings: Warning: fs/ubifs/ubifs.h:755 struct member 'leaf' not described in 'ubifs_zbranch' Warning: fs/ubifs/ubifs.h:814 struct member 'eof' not described in 'bu_info' Warning: fs/ubifs/ubifs.h:814 Excess struct member 'oef' description in 'bu_info' Warning: fs/ubifs/ubifs.h:992 cannot understand function prototype: 'struct ubifs_stats_info' Warning: fs/ubifs/ubifs.h:1513 struct member 'superblock_need_write' not described in 'ubifs_info' Warning: fs/ubifs/ubifs.h:1597 No description found for return value of 'ubifs_check_hash' Warning: fs/ubifs/ubifs.h:1612 No description found for return value of 'ubifs_check_hmac' Warning: fs/ubifs/ubifs.h:1653 No description found for return value of 'ubifs_branch_hash' Warning: fs/ubifs/ubifs.h:1703 No description found for return value of 'ubifs_auth_node_sz' Signed-off-by: Randy Dunlap <rdunlap@infradead.org> Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com> Signed-off-by: Richard Weinberger <richard@nod.at>
2026-08-11ubifs: key.h: use correct function parameter nameRandy Dunlap1-1/+1
Use @hash as the function parameter name to prevent kernel-doc warnings: Warning: fs/ubifs/key.h:42 function parameter 'hash' not described in 'key_mask_hash' Warning: fs/ubifs/key.h:42 Excess function parameter 'val' description in 'key_mask_hash' Signed-off-by: Randy Dunlap <rdunlap@infradead.org> Reviewed-by: Zhihao Cheng <chengzhihao1@huawei.com> Signed-off-by: Richard Weinberger <richard@nod.at>
2026-08-11ubifs: debug.h: fix kernel-doc struct prototypesRandy Dunlap1-2/+2
Use the "struct" keyword to describe structs in kernel-doc format. This avoids kernel-doc warnings: Warning: fs/ubifs/debug.h:75 cannot understand function prototype: 'struct ubifs_debug_info' Warning: fs/ubifs/debug.h:130 cannot understand function prototype: 'struct ubifs_global_debug_info' Signed-off-by: Randy Dunlap <rdunlap@infradead.org> Signed-off-by: Richard Weinberger <richard@nod.at>
2026-08-11orangefs: skip leading spaces before parsing client debug masksZhiling Zou1-0/+1
orangefs_prepare_cdm_array() sizes each client debug keyword buffer with strcspn(cds_head, " "), but then parses the keyword with %s. The %s conversion skips leading whitespace, while strcspn() does not. If a client debug entry starts with a space, the allocation can be sized for an empty keyword while sscanf() copies the following non-empty token. This can write past the end of the allocated keyword buffer. Skip leading spaces before computing the keyword length so the allocation matches the string parsed by sscanf(). Fixes: f7be4ee07fb7 ("Orangefs: kernel client part 4") Cc: stable@vger.kernel.org Reported-by: Vega <vega@nebusec.ai> Assisted-by: Codex:gpt-5.4 Signed-off-by: Zhiling Zou <zhilinz@nebusec.ai> Signed-off-by: Ren Wei <enjou1224z@gmail.com> Signed-off-by: Mike Marshall <hubcap@omnibond.com>
2026-08-11orangefs: Remove commented out code in find_cached_xattrThorsten Blum1-5/+0
The code has been commented out since 2017 - remove it. Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev> Signed-off-by: Mike Marshall <hubcap@omnibond.com>
2026-08-11orangefs: use folio_pos() and folio_size() in orangefs_page_mkwrite()Tal Zussman1-4/+4
orangefs_page_mkwrite() records the faulted range with page_offset(vmf->page) and PAGE_SIZE, although the write range it sets is attached to the folio and the rest of the function already operates on folios. Use folio_pos() and folio_size() instead. This gets rid of two calls to page_offset(), removing two calls to compound_head(). No functional change. orangefs folios are always order-0, so the values are identical. However, if orangefs ever enables large folios, this change is necessary for correctness with the current write range tracking scheme. Tracking only a single page of a larger folio would leave the rest of the folio's dirty data outside the range that gets written back, leading to data loss. Signed-off-by: Tal Zussman <tz2294@columbia.edu> Signed-off-by: Mike Marshall <hubcap@omnibond.com>
2026-08-11orangefs: fix double-free of trailer_buf on readdir copy failureYifei Gao1-0/+1
On a readdir downcall, orangefs_devreq_write_iter() frees op->downcall.trailer_buf with vfree() when copy_from_iter_full() fails, but does not clear the pointer before goto Efault. The waiter in do_readdir() is then woken with a negative status and frees the same pointer again on its r < 0 path, causing a deterministic double-free. A client holding /dev/pvfs2-req triggers it by sending a readdir downcall whose declared trailer_size exceeds the bytes it supplies. Clear the pointer after freeing so the readdir-side vfree() becomes a no-op. Fixes: 382f4581e67f ("orangefs: rewrite readdir to fix several bugs") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Yifei Gao <gyf161023@gmail.com> Signed-off-by: Mike Marshall <hubcap@omnibond.com>
2026-08-11f2fs: call __add_ino_entry out of the eviction pathJaegeuk Kim5-7/+122
The f2fs_evict_inode() can be called during the direct reclaim path, but __add_ino_entry requires allocating some memory. Since we don't need to do that in that context, let's migrate it in other workqueue context. Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2026-08-11erofs: use dedicated meta inodes for file-backed mountsGao Xiang5-44/+65
Currently, metadata access for file-backed mounts reuses the page cache of backing inodes directly. Switch to per-sb dedicated pseudo inodes ("managed cache") for metadata access on file-backed mounts (although I still don't think it is necessary due to the EROFS immutable model). As the result, metadata cache won't use the page cache of backing inodes anymore. The "managed cache" was originally used to cache physical compressed data according to the current cache strategy and I/O patterns; since file-backed mounts also need to access physical data for metadata access, it's natural to reuse the managed cache for this too, providing a unique inode for all physical data access. Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Gao Xiang <xiang@kernel.org>
2026-08-11erofs: accept source file descriptor via fsconfigGiuseppe Scrivano1-11/+62
Allow userspace to pass an already-opened file descriptor as the mount source instead of a path string. This is useful for tools that already hold an fd to the image, such as composefs reusing an existing erofs backing file. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com> Reviewed-by: Gao Xiang <xiang@kernel.org> Reviewed-by: Christian Brauner (Amutable) <brauner@kernel.org> Acked-by: Jan Kara <jack@suse.cz> Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Gao Xiang <xiang@kernel.org>
2026-08-11exfat: fix truncated volume labels returned by FS_IOC_GETFSLABELYang Wen1-1/+1
exfat_ioctl_get_volume_label() passes uniname.name_len to exfat_utf16_to_nls() as the output buffer length. However, name_len is the number of UTF-16 code units, while exfat_utf16_to_nls() expects the buffer size in bytes. As a result, volume labels that expand during charset conversion are truncated.The destination buffer is FSLABEL_MAX bytes long, so pass its actual size to the conversion helper. Signed-off-by: Yang Wen <anmuxixixi@gmail.com> Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
2026-08-11f2fs: refactor f2fs_evict_inode having three major partsJaegeuk Kim1-53/+79
1. f2fs_pre_evict_inode() : drop all in-memory structures 2. f2fs_delete_inode() : truncate inode blocks, if it was unlinked. 3. f2fs_post_evict_inode() : update inode records for future access Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2026-08-11f2fs: support dynamic reserve/release for device aliasingDaeho Jeong9-68/+515
This patch adds a dynamic management feature to the existing device aliasing functionality. It allows users to dynamically reserve or release specific devices from the filesystem's free pool at runtime through new ioctls. To support this, three new ioctls are introduced: - F2FS_IOC_RESERVE_DEV_ALIAS: This reclaims the space occupied by a device aliasing file. It first performs a capacity check, resets GC victim information for the target range, marks the segments as in-use to prevent new allocations, and then triggers GC to migrate existing valid data out of the range. Finally, it reserves these blocks in the SIT to effectively exclude the device from the usable capacity. - F2FS_IOC_RELEASE_DEV_ALIAS: This releases the reserved space of a previously reserved device aliasing file. It truncates the blocks associated with the file, which makes them available for general filesystem allocation again. - F2FS_IOC_GET_DEV_ALIAS_STATUS: This retrieves the current aliasing status of a device aliasing file, returning whether the file is released (inactive alias) or reserved (active alias, with blocks fully allocated on the device). Signed-off-by: Daeho Jeong <daehojeong@google.com> Reviewed-by: Chao Yu <chao@kernel.org> Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
2026-08-10nilfs2: standardize the inode number type to u64Ryusuke Konishi10-50/+46
Variables handling inode numbers - such as the 'i_ino' member of the inode structure - have been converted to 'u64' within the kernel to ensure consistency. However, some parts of the nilfs2 implementation still use 'ino_t' or 'unsigned long' - both of which are architecture-dependent types - to handle inode numbers. Replace those remaining instances of 'ino_t' or 'unsigned long' with 'u64'. Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
2026-08-10nilfs2: enhance btree node keys checkWang Jianjian1-3/+16
syzbot reported a warning on nilfs_btree_assign: WARNING: fs/nilfs2/btree.c:2302 at nilfs_btree_assign+0x983/0xbe0 fs/nilfs2/btree.c:2302, Analysis found that a corrupted file has the following btree layout: Level2(key/ptr): [ 256/15 ] Level1(key/ptr): [ 0/8, 1/9, 0/10, 3/11, 4/12, 5/13, 6/14, 139637976727559/16, 0/17 ] The test truncated the file to 2 bytes, which partially zeroes the first block and adds the file to the dirty list. When the segment constructor writes it and assigns a new blocknr for the index block, it searches the btree with key=0 and min level=2, and apparently returns -ENOENT. Therefore, we should perform more checks on the btree nodes and return early. [ryusuke: split long lines in btree.c to satisfy checkpatch and improved the error message format for clarity] Reported-by: syzbot+158be45e4d99232e1900@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=158be45e4d99232e1900 Signed-off-by: Wang Jianjian <wangjianjian3@huawei.com> Fixes: 17c76b0104e4 ("nilfs2: B-tree based block mapping") Cc: stable+noautosel@kernel.org # Warning suppression primarily Signed-off-by: Ryusuke Konishi <konishi.ryusuke@gmail.com> Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
2026-08-10NFS: Return a delegation the client fails to recordChuck Lever1-4/+14
When an NFS server grants a delegation in an OPEN reply, nfs_inode_set_delegation() records it on the client. However, three of its error flows return without sending DELEGRETURN. A delegation can be relinquished only by DELEGRETURN (RFC 8881 Section 20.2.4), so dropping one silently leaves the server believing the client still holds it. If the server happens to recall that delegation, the client answers CB_RECALL with NFS4ERR_BADHANDLE because it has no record of the stateid. The server revokes the delegation and moves it onto its cl_revoked list, because the client never sends the FREE_STATEID that would drain it. Every subsequent SEQUENCE reply then carries SEQ4_STATUS_RECALLABLE_STATE_REVOKED, and the client's state manager loops issuing TEST_STATEID across its delegations without ever clearing the condition. The window is easy to reach now that a server offers a write delegation on any write OPEN: a delegation recalled for one opener races a re-open that the server answers with a fresh write delegation. Instead of dropping it, hand the delegation back during these error flows. Fixes: ade04647dd56 ("NFSv4: Ensure we honour NFS_DELEGATION_RETURNING in nfs_inode_set_delegation()") Signed-off-by: Chuck Lever <cel@kernel.org> Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
2026-08-10nfsd: export NFSv4 callback op stats via netlinkJeff Layton1-0/+27
Add a proc4cb-ops nested attribute to the server-stats netlink dump, reusing the existing server-proc-entry (op/count) layout. The dump gains a callback section that emits one entry per callback opcode (OP_CB_GETATTR..OP_CB_OFFLOAD) from the per-netns callback counters, paged across messages like the other per-operation sections. This lets nfsstat report NFSv4 backchannel operation counts over netlink, including CB_GETATTR which corresponds to the procfs wdeleg_getattr line. Assisted-by: LLM Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260717-exportd-netlink-v7-6-b7ce17b83b60@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: count NFSv4 callback operations per netnsJeff Layton6-7/+50
The NFS server tracks per-operation call counts for the forward channel (proc4ops) but keeps no statistics for the NFSv4 backchannel (callback) operations it sends to clients. Add a per-netns array of percpu counters for callback operations, indexed by RFC 8881 callback opcode (OP_CB_GETATTR..OP_CB_OFFLOAD), and bump the relevant counter in nfsd4_run_cb(), which is hit exactly once per callback that is actually queued. CB_GETATTR is sent when a GETATTR conflicts with an outstanding write delegation, which is roughly what the dedicated wdeleg_getattr counter tracked. The two are not identical: the old counter incremented on every such conflict, whereas the CB_GETATTR counter only counts callbacks that are actually queued, so concurrent conflicts that coalesce onto an already in-flight CB_GETATTR are now counted once rather than once per conflict. Report the procfs "wdeleg_getattr" line from the CB_GETATTR counter and drop the now-redundant NFSD_STATS_WDELEG_GETATTR counter, its helper, and its increment site. Assisted-by: LLM Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260717-exportd-netlink-v7-5-b7ce17b83b60@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10sunrpc: remove unused svc_version vs_count fieldJeff Layton9-35/+0
Now that svc_seq_show() and the nfsd netlink stats handler both use the per-netns svc_stat vs_count arrays, the global per-version vs_count percpu counters are no longer read by anything. Remove the vs_count field from struct svc_version and all the associated DEFINE_PER_CPU_ALIGNED arrays and initializers across nfsd, lockd, and the NFS client callback service. Assisted-by: LLM Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260717-exportd-netlink-v7-4-b7ce17b83b60@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: implement server-stats-get netlink handlerJeff Layton3-0/+229
Implement nfsd_nl_server_stats_get_dumpit() which exposes the NFS server statistics currently available via /proc/net/rpc/nfsd through the nfsd generic netlink family. The handler uses a dump operation to stream statistics across one or more netlink messages. The reply is divided into sections that are emitted in order: - scalar stats (reply cache, filehandle, IO, network, RPC), emitted once in the first message, then - per-version procedure counts (proc2/3/4-ops) and the NFSv4 per-operation counts (proc4ops-ops), using the per-netns vs_count arrays. cb->args[0] tracks the current section and cb->args[1] the entry index within it, so a section that does not fit in the current message is closed and resumed in the next one. This matters because the first dump message is allocated at NLMSG_GOODSIZE (a single page on most architectures) regardless of the client's receive buffer; packing every counter into one message would overflow it and fail the dump with -EMSGSIZE. Userspace merges the attributes from every message. This allows nfsstat to retrieve server statistics via netlink with a procfs fallback for older kernels. Assisted-by: LLM Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260717-exportd-netlink-v7-3-b7ce17b83b60@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10sunrpc: add per-netns per-procedure call counts to svc_statJeff Layton1-1/+7
The existing per-procedure call counts live in global svc_version->vs_count[] arrays which are not network-namespace-aware. Add per-netns equivalents in struct svc_stat so the upcoming netlink stats interface can return namespace-scoped statistics. Add a vs_count pointer array to struct svc_stat, along with svc_stat_alloc_counts() and svc_stat_free_counts() helpers to manage per-version percpu call count arrays. Increment the per-net counter alongside the global one in svc_generic_init_request(). Call the alloc/free helpers from nfsd_net_init() and nfsd_net_exit(). Assisted-by: LLM Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260717-exportd-netlink-v7-1-b7ce17b83b60@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Eliminate percpu counter contention in IO byte accountingChuck Lever1-4/+26
nfsd_stats_io_write_add() and nfsd_stats_io_read_add() accumulate byte counts in per-net-namespace and per-export percpu_counters using percpu_counter_add(), which applies the default batch threshold of max(32, 2*nr_cpus). For a 4 KB NFS WRITE, the amount (4096) always exceeds this threshold, so percpu_counter_add_batch() acquires the counter's global spinlock on every update. Each WRITE RPC updates two counters (per-net and per-export), producing two global lock acquisitions per operation. Profiling on a 10-CPU RDMA NFS server shows 0.44% of total CPU cycles spent contending on these locks during a small random write workload. Switch to percpu_counter_add_local(), which batches with INT_MAX so that updates always remain on the per-CPU fast path regardless of the amount. All readers of these counters already use percpu_counter_sum_positive(), which sums the per-CPU deltas under the global lock, so read accuracy is unaffected. Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260717001232.438792-5-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Eliminate percpu counter contention in reply cache statisticsChuck Lever1-3/+23
Each RPC passes through nfsd_cache_lookup(), which increments one of nfsd_stats_rc_hits_inc(), nfsd_stats_rc_misses_inc(), or nfsd_stats_rc_nocache_inc(). These helpers update per-net-namespace percpu_counters with percpu_counter_inc(), which applies the default batch threshold of max(32, 2*nr_cpus). Once a CPU's local delta reaches that threshold, the update folds into the shared counter under its global spinlock. On a busy multi-CPU server this produces lock traffic on a counter cacheline shared across all CPUs, growing with the request rate. Switch to percpu_counter_add_local(fbc, 1), which batches with INT_MAX so that increments always remain on the per-CPU fast path. This matches the treatment already applied to the IO byte and DRC memory counters. All readers of these counters use percpu_counter_sum_positive(), which sums the per-CPU deltas under the global lock, so read accuracy is unaffected. Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260717001232.438792-4-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Eliminate percpu counter contention in DRC memory accountingChuck Lever1-2/+20
The DRC memory usage counter (NFSD_STATS_DRC_MEM_USAGE) tracks bytes, but percpu_counter_add() uses the global percpu_counter_batch threshold of max(32, 2*nr_cpus). Each DRC entry add or removal updates the counter by sizeof(struct nfsd_cacherep) (~144 bytes), which always exceeds the batch threshold. percpu_counter_add() then acquires the counter's global spinlock on every update, serializing all nfsd threads. On a 10-CPU NFS server handling a high rate of non-idempotent NFSv3 operations, this lock accounts for a measurable fraction of total spin lock overhead because nfsd_cache_lookup() both inserts a new entry and prunes up to three old entries per RPC, producing 4-7 global lock acquisitions per operation. Switch to percpu_counter_add_local() and percpu_counter_sub_local(), which batch with INT_MAX so that updates always remain on the per-CPU fast path regardless of the amount. The only reader of this counter uses percpu_counter_sum_positive(), which sums the per-CPU deltas under the global lock, so read accuracy is unaffected. Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260717001232.438792-3-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Fix off-by-one in DRC bucket pruning limitChuck Lever1-1/+1
nfsd_prune_bucket_locked() evicts an entry before checking the freed count against @max. The check uses "++freed > max", which does not break until freed exceeds max, resulting in max + 1 evictions. Use ">=" so the limit stated in the function comment is honored. Fixes: a9507f6af145 ("NFSD: Replace nfsd_prune_bucket()") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: NeilBrown <neil@brown.name> Link: https://patch.msgid.link/20260717001232.438792-2-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Relocate NFSv4 "supported attributes" to new headerChuck Lever4-150/+164
These NFSv4 attribute bitmask definitions live in nfsd.h, which nearly every nfsd source file includes, yet only nfs4proc.c and nfs4xdr.c reference them. Move them to a dedicated header so only those two consumers pull them in. While moving the block, correct the stale QUOTA_* annotation: the promised support never materialized, so these attributes are unlikely to be supported any time soon rather than forthcoming. Link: https://patch.msgid.link/20260712204554.125308-10-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Relocate nfsd4_set_netaddr()Chuck Lever2-33/+31
Clean up: Common practice in the Linux kernel is to avoid the use of static inline functions when there is only a single call site. The 30-line helper function is removed from a header pulled into ~25 .c files, removing <linux/sunrpc/addr.h> from that header's transitive include surface, dropping a now-redundant <linux/sunrpc/msg_prot.h> include, and reducing the function's visibility to the one translation unit that uses it. Link: https://patch.msgid.link/20260712204554.125308-9-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Relocate nfsd_user_namespace()Chuck Lever5-6/+26
Refactor: nfsd_user_namespace() currently lives in nfsd.h, so every caller must pull in nfsd.h -- directly or transitively via state.h -- and with it the NFS protocol definitions from uapi/linux/nfs.h and friends, even when the caller uses nothing else from nfsd.h. Since nfsd_user_namespace() is an auth-related function, move it to fs/nfsd/auth.c in preparation for removing '#include "nfsd.h"' from a few places. Link: https://patch.msgid.link/20260712204554.125308-8-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Move struct readdir_cdChuck Lever5-6/+7
struct readdir_cd is part of the VFS readdir API, but it lives in nfsd.h, the subsystem's catch-all header, rather than alongside that API. That forces vfs.h to include nfsd.h solely to declare readdir_cd for its nfsd_readdir() prototype, a layering inversion since vfs.h is the lower-level shim. Relocate readdir_cd to vfs.h, just below the nfsd_filldir_t callback typedef. vfs.h then defines the struct itself and no longer includes nfsd.h. The xdr headers that embed readdir_cd by value include vfs.h to obtain the definition. This prepares the ground for dropping nfsd.h from more files. Link: https://patch.msgid.link/20260712204554.125308-7-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Move the export.h include from nfsd.h to auth.cChuck Lever2-2/+1
Nothing declared in fs/nfsd/nfsd.h references a type, macro, or function that export.h defines. The include is present only so that source files including nfsd.h pick up export.h's definitions transitively. Of the twenty source files that include nfsd.h, only auth.c relies on that side effect: it names struct svc_export and the NFSEXP_* flags yet includes no header that supplies them. Add the export.h include directly to auth.c, then drop it from nfsd.h so the header carries only the dependencies its own declarations require. Link: https://patch.msgid.link/20260712204554.125308-6-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Remove '#include "nfsd.h"' from fs/nfsd/cache.hChuck Lever1-1/+2
Clean up: cache.h does not need the full definition of struct nfsd_net. A forward declaration suffices. Link: https://patch.msgid.link/20260712204554.125308-5-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: include "netns.h"Chuck Lever9-4/+13
Nothing in fs/nfsd/nfsd.h needs the contents of "netns.h"; the prototypes there that take a struct nfsd_net pointer need only a forward declaration of that type. Relocate the existing forward declaration ahead of the first such prototype, drop the "netns.h" include from nfsd.h, and include it directly in the translation units that operate on struct nfsd_net. "netns.h" had also been the path by which <linux/filelock.h> reached nfsxdr.c and state.h. Both now include <linux/filelock.h> themselves. Link: https://patch.msgid.link/20260712204554.125308-4-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Explicitly include "stats.h"Chuck Lever7-1/+6
Nothing in fs/nfsd/nfsd.h needs what is defined in "stats.h", so that header can be moved out to the six translation units that actually need it. Link: https://patch.msgid.link/20260712204554.125308-3-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Make "stats.h" self-containedChuck Lever1-0/+3
The inline helpers in fs/nfsd/stats.h dereference struct nfsd_net and struct svc_export, yet the header includes neither "netns.h" nor "export.h", where those types are defined. Each helper therefore compiles only when its translation unit has already pulled in both headers ahead of "stats.h" -- a hidden ordering requirement that has to be honored at every include site. Include "netns.h" and "export.h" from "stats.h" directly so the header stands on its own, and no consumer has to order its includes to satisfy it. Link: https://patch.msgid.link/20260712204554.125308-2-cel@kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10xdrgen: Share void RPC procedure handlers across programsChuck Lever6-100/+40
The generated server-side decoder and encoder for a void procedure argument or result are named after the RPC program (for example, nfs_svc_decode_void). xdrgen derives that prefix from the program name alone, not the version, so two versions of one program built into the same module emit the identical symbol. NFSv2 and NFSv3 both declare program NFS_PROGRAM; once both are converted, fs/nfsd fails to link with multiple definitions of nfs_svc_decode_void and nfs_svc_encode_void. A void handler carries no program- or version-specific behavior: each merely forwards to xdrgen_decode_void() or xdrgen_encode_void(). Define one shared pair, xdrgen_svc_decode_void() and xdrgen_svc_encode_void(), in the xdrgen builtins, and stop the program generator from emitting a per-program void handler. lockd is the one in-tree consumer that already emits per-program void handlers, so regenerate the NLMv3 and NLMv4 XDR code to drop nlm_svc_{decode,encode}_void() and nlm4_svc_{decode,encode}_void() and point both procedure tables at the shared handlers. The shared handlers are identical to the generated ones they replace, so no wire behavior changes. Only the server (svc) handlers are affected. The client-side void stubs remain static and per-program, so they do not collide. Link: https://patch.msgid.link/20260712193122.116845-3-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10xdrgen: Emit a blank line ahead of enum declarationsChuck Lever6-6/+9
Clean up. The declaration templates for structs, pointers, and typedefs each begin with a blank line, which keeps successive declarations and the include block above them visually separated. The enum declaration template omits that blank line. trim_blocks collapses the template's lone comment line to nothing, so the omission stayed invisible as long as every generated header happened to lead with a non-enum declaration. Fixes: 4329010ad9c3 ("xdrgen: Address some checkpatch whitespace complaints") Link: https://patch.msgid.link/20260712193122.116845-2-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Encode only the status in NFS-ACL v2 GETACL error repliesChuck Lever1-16/+15
The NFSv2 ACL GETACL reply is a union that carries file attributes and ACL data only when the status is NFS_OK. All error cases are void results. However, currently the NFSv2 ACL GETACL result encoder decides whether to append the "OK" body by testing only whether the file handle resolved to a positive dentry, not the actual reply status. A GETACL request that resolves its file handle but then fails for another reason (an unsupported mask value, a getattr failure, or an ACL retrieval error) therefore appends file attributes and ACL data after the error status on the wire. Worse, when the mask is rejected, fh_getattr() hasn't been called at all, so those attributes are serialized from a zero-filled kstat and are junk. The logic before the xdr_stream conversion used the reply status. Revert to that approach (but keep the xdr_stream conversion in place). Fixes: f8cba47344f7 ("NFSD: Update the NFSv2 GETACL result encoder to use struct xdr_stream") Cc: stable@vger.kernel.org Link: https://patch.msgid.link/20260712150911.48461-1-cel@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: drop dead COPY-vs-COPYNOTIFY type handling from s2s stateid IDRJeff Layton2-19/+12
Now that the COPY offload stateid is a first-class nfs4_stid, nn->s2s_cp_stateids holds COPY_NOTIFY stateids exclusively (its only inserter, nfs4_init_cp_state(), runs only from nfs4_alloc_init_cpntf_state()). The type-distinguishing machinery is dead: - remove the unreferenced NFS4_COPY_STID definition; - drop nfs4_init_cp_state()'s cs_type argument (hardcode NFS4_COPYNOTIFY_STID) and its now-always-true "if (p_stid)" guard; - remove the cs_type == NFS4_COPYNOTIFY_STID gates in manage_cpntf_state() and the laundromat, which can no longer be false. copy_stateid_t.cs_type is retained for the WARN_ON_ONCE() sanity checks on the free paths. No functional change. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-10-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: make the copy offload stateid a first-class nfs4_stidJeff Layton4-39/+61
The async COPY offload stateid was a copy_stateid_t in the per-net nn->s2s_cp_stateids IDR, sharing that table with COPY_NOTIFY stateids even though every reader (laundromat, manage_cpntf_state()) accepts only NFS4_COPYNOTIFY_STID. It was inserted there only to mint a unique so_id; OFFLOAD_CANCEL and OFFLOAD_STATUS find the copy by walking clp->async_copies. Building on the nfsd4_async_copy split, promote it to a first-class nfs4_stid (SC_TYPE_COPY) embedded at the head of nfsd4_async_copy and allocated from the client's cl_stateids via nfs4_alloc_stid(). This: - makes the stateid per-client by construction rather than relying on a guessable cyclic id in a global table; - reuses the common id allocation, refcounting, and teardown (nfs4_put_stid() + sc_free), removing the bespoke nfs4_init_copy_state()/nfs4_free_copy_state(); and - leaves nn->s2s_cp_stateids exclusively for COPY_NOTIFY stateids. The async-copy lifetime model is unchanged; nf4_put_copy() now drops the stid's single reference, which removes it from cl_stateids and frees the slab. Per RFC 7862 Section 4.8 a copy offload stateid is valid only for COPY/OFFLOAD_CANCEL/OFFLOAD_STATUS/CB_OFFLOAD, not FREE_STATEID or TEST_STATEID, so find_stateid_locked() hides SC_TYPE_COPY and those paths keep returning bad_stateid as before. Its seqid MUST NOT be zero, so set si_generation to 1 (nfs4_alloc_stid() leaves it zero). Follow-ups (not done here): NFS4_COPY_STID, the now-always-COPYNOTIFY branch in nfs4_init_cp_state(), and the redundant cs_type checks in the laundromat and manage_cpntf_state() are vestigial. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-9-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: split nfsd4_copy into transient and durable async copy objectsJeff Layton4-80/+98
struct nfsd4_copy served two roles: as &u->copy it is a transient per-COMPOUND argument in the request buffer; as the heap async_copy it is a durable object (worker kthread, reaper linkage, CB_OFFLOAD callback, IDR stateid) that outlives the COMPOUND, with dup_copy_fields() shuttling state between them. That dual identity was the root of the recent lifetime bugs. Introduce struct nfsd4_async_copy for the durable object. It embeds a struct nfsd4_copy (cp_copy) for the operation parameters/result and adds the durable-only fields: async_copies linkage, task_struct, refcount, reaper TTL, copy stateid, and CB_OFFLOAD callback. The durable object therefore never points into the request buffer. cp_clp stays in nfsd4_copy -- it is a request property read by the sync-copy tracepoints on the transient object. Mechanical split, no intended behavioral change; a step toward folding the copy stateids into the common nfs4_stid infrastructure. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-8-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: return NFS4ERR_NOTSUPP for unsupported netloc4 typesJeff Layton1-0/+13
nfsd4_decode_nl4_server() handled only NL4_NETADDR and returned nfserr_bad_xdr for NL4_NAME and NL4_URL. Those forms are well-formed XDR, so BADXDR is misleading -- the request is unsupported, not malformed. Decode and discard the utf8str_cis for NL4_NAME and NL4_URL to keep the stream consistent, and return nfserr_notsupp. nfsd4_proc_compound() honors a decode-time op->status, so the op fails without executing. Fixes: 84e1b21d5ec4 ("NFSD add ca_source_server<> to COPY") Cc: stable@vger.kernel.org Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-7-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: revoke copy-notify stateids before dropping their referenceJeff Layton1-16/+62
Copy-notify stateids live in the s2s_cp_stateids IDR and on their parent stid's sc_cp_list, pinned by a single membership reference. _free_cpntf_state_locked() only unlinks an entry once its refcount reaches zero, so any revoke path that runs while a concurrent find_cpntf_state()/manage_cpntf_state() holder has elevated cs_count drops the reference without unlinking, leaving the entry discoverable with its membership reference already consumed. A second revoke or a laundromat tick then frees it while the reader still holds the pointer -- a KASAN-detectable use-after-free at the reader's nfs4_put_cpntf_state(). This affected all three revoke paths: - The parent-stid drain (nfs4_free_cpntf_statelist()) repeatedly called _free_cpntf_state_locked() on the first list entry; a holder that had bumped cs_count made it return early, so the next iteration re-decremented and burned the holder's reference. - OFFLOAD_CANCEL (manage_cpntf_state()) and laundromat expiry likewise used _free_cpntf_state_locked() and could drop 2->1 without unlinking. Add revoke_cpntf_state_locked(), which unhashes the entry from the IDR and sc_cp_list first (deferring the final free to any holder), and use it from all three revoke paths. The drain now walks with list_for_each_entry_safe() and revokes each entry unconditionally, so it terminates in one pass per entry regardless of cs_count. The unhash is gated on !list_empty(&cps->cp_list); the idr_remove() gate matters because idr_alloc_cyclic() may have recycled the so_id by then. Keep _free_cpntf_state_locked() for the reference-holder put path only, where a concurrent revoke may already have unlinked the entry (its list_del_init() then a no-op). Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-7 Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-6-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: check client ownership when cancelling a copy-notify stateidJeff Layton1-2/+12
On the OFFLOAD_CANCEL path (clp != NULL), manage_cpntf_state() freed the target cpntf state without checking ownership. The lookup key st->si_opaque.so_id is allocated cyclically (guessable) and the embedded clientid is the fixed per-net nn->s2s_cp_cl_id, so any authenticated NFSv4.2 client could cancel and free another client's copy-notify stateid. Compare the creating clientid recorded in state->cp_p_clid against the requesting client's cl_clientid and return nfserr_bad_stateid on a mismatch instead of freeing the entry. Fixes: ce0887ac96d3 ("NFSD add nfs4 inter ssc to nfsd4_copy") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-5-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: initialize copy-notify stateid before publishing itJeff Layton2-8/+18
nfsd4_copy_notify() finished initializing the cpntf state after nfs4_alloc_init_cpntf_state() had already linked it into the s2s_cp_stateids IDR and the parent's sc_cp_list, with cs_count == 1 (the membership reference) and none held for the caller. A racing OFFLOAD_CANCEL (crafted cl_id == nn->s2s_cp_cl_id plus the guessable so_id) could reach manage_cpntf_state() and free the entry, turning the caller's subsequent cpn_cnr_stateid read and cp_p_stateid/cp_p_clid writes into use-after-free. The owning clientid was also only recorded after publication, so it could not gate an ownership check in that window. Record cp_p_stateid and cp_p_clid inside nfs4_alloc_init_cpntf_state() before nfs4_init_cp_state() publishes the entry, and return it with an extra reference. The caller reads the stateid under that reference and drops it with nfs4_put_cpntf_state(); on a late error the laundromat reaps the entry. Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-4-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: fix stale s2s_cp_stateids IDR entry for async COPYJeff Layton1-3/+4
For an async COPY, nfsd4_copy() called nfs4_init_copy_state() before dup_copy_fields(), so the s2s_cp_stateids IDR was pointed at &u->copy->cp_stateid -- memory in the per-rqstp COMPOUND buffer that is reused by the next request. dup_copy_fields() copies only the value into async_copy, so the IDR slot dangled at the transient buffer for the whole background copy. Any IDR walker then dereferences reused request memory: the laundromat reads cs_type from it and, if the bytes look like an expired NFS4_COPYNOTIFY_STID, follows into refcount_dec()/idr_remove()/kfree() on garbage; manage_cpntf_state() has the same exposure via idr_find(). Duplicate the fields first, then register the stateid on the stable async_copy. result->cb_stateid is unchanged. Fixes: e0639dc5805a ("NFSD introduce async copy feature") Cc: stable@vger.kernel.org Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-3-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: fix UAF in async copy cancel and shutdownJeff Layton1-35/+85
An async copy could be freed or used after free while a teardown caller (OFFLOAD_CANCEL, nfsd4_shutdown_copy, nfsd4_cancel_copy_by_sb) raced the copy kthread: - find_async_copy() bumped copy->refcount but left the copy on clp->async_copies, so the reaper's cleanup_async_copy() could run release_copy_files() concurrently with a cancel/shutdown caller. Both put and NULL nf_src/nf_dst without a common lock, double-putting the nfsd_file and freeing it early. - nfsd4_do_async_copy() set NFSD4_COPY_F_STOPPED before its final uses of the copy (nfsd_update_cmtime_attr() on copy->nf_dst, nfsd4_send_cb_offload()). nfsd4_stop_copy() treats a set STOPPED bit as "kthread done, skip kthread_stop()", so a teardown caller ran release_copy_files() -- which puts and NULLs nf_dst -- while the kthread still dereferenced it (NULL/UAF). - copy->copy_task was never pinned. The one-shot kthread self-reaps on return, so kthread_stop()'s get_task_struct() could touch a freed task_struct. - co_cb is embedded in the copy, but nfsd4_send_cb_offload() held a reference only on the client, so a concurrent teardown could free the copy while the CB_OFFLOAD callback was in flight. Fix the teardown lifetime as a whole: - find_async_copy() unlinks the copy (clear cp_clp, list_del_init) under async_lock; the cancel, shutdown, and sb-cancel paths drop the list-membership reference via nfs4_put_copy() after nfsd4_stop_copy(). Drop the now-redundant list_del fixup from cleanup_async_copy(). - Because unlinking hides the copy from the reaper, its cleanup_async_copy() can no longer remove the copy's s2s_cp_stateids entry; the cancel/shutdown/sb-cancel paths now call nfs4_free_copy_state() themselves (while cp_clp is still valid) so the entry does not dangle at freed memory for the laundromat and manage_cpntf_state() to dereference. - Give the kthread its own reference, taken in nfsd4_copy() before wake_up_process() and dropped at the end of nfsd4_do_async_copy(); call wake_up_process() before list_add(). - Pin the task_struct with get_task_struct() in nfsd4_copy(), released in nfs4_put_copy(), so kthread_stop() is safe whenever the kthread exits. Set NFSD4_COPY_F_STOPPED only in nfsd4_stop_copy(), which now always kthread_stop()s before release_copy_files(); completion is still reported via NFSD4_COPY_F_COMPLETED, so nfsd4_has_active_async_copies() is unaffected. Each teardown caller removes the copy from clp->async_copies first, so kthread_stop() runs exactly once. - Take a copy reference in nfsd4_send_cb_offload(), dropped in nfsd4_cb_offload_release(). The kthread still holds its own reference there, so the refcount_inc() cannot race the final free. - Read cp_clp with smp_load_acquire() to pair with the unordered set_bit()/clear_bit() writers (Documentation/atomic_bitops.rst). Fixes: e0639dc5805a ("NFSD introduce async copy feature") Cc: stable@vger.kernel.org Fixes: ac0514f4d198 ("NFSD: Add a laundromat reaper for async copy state") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-2-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10nfsd: fix cpntf publish race in nfs4_init_cp_stateChris Mason1-10/+25
nfs4_alloc_init_cpntf_state() published the new cpntf entry into the s2s_cp_stateids IDR (with cs_type set) in one s2s_cp_lock section, then took the lock again to list_add() it onto p_stid->sc_cp_list. In the gap the entry is reachable by so_id but cp_list is still {NULL,NULL} from kzalloc. A racing OFFLOAD_CANCEL (so_id is echoed to the client as cnr_stateid, so any NFSv4.2 client can drive it) reaches manage_cpntf_state() -> _free_cpntf_state_locked() and does list_del() on the zeroed list_head, oopsing the server. Fold the cs_type assignment and the list_add() into the same critical section as idr_alloc_cyclic(), so a concurrent lookup either misses the entry or sees a fully linked cp_list. INIT_LIST_HEAD() the entry after allocation and switch _free_cpntf_state_locked() to list_del_init() so a stale unlink is a no-op. nfs4_init_copy_state() passes NULL p_stid and skips the list_add, preserving NFS4_COPY_STID semantics. Fixes: 624322f1adc5 ("NFSD add COPY_NOTIFY operation") Cc: stable@vger.kernel.org Assisted-by: kres:claude-opus-4-7 Signed-off-by: Chris Mason <clm@meta.com> Link: https://patch.msgid.link/20260710-nfsd-testing-v3-1-a0ff7db6aa3e@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Release the export reference when reaping open stateidsChuck Lever1-1/+7
nfs4_put_stid() releases the svc_export tracked in nfs4_stid.sc_export, but free_ol_stateid_reaplist() frees open and lock stateids by calling ->sc_free() directly, bypassing that path. An open stateid takes an sc_export reference in nfs4_open() and a lock stateid takes its own in init_lock_stateid(); both reach free_ol_stateid_reaplist() through their normal teardown, the open stateid via release_open_stateid() and the lock stateid via nfsd4_release_lockowner(), each through put_ol_stateid_locked(). The reference is therefore never dropped, pinning the export and blocking unmount for the lifetime of the stateid. Release sc_export in free_ol_stateid_reaplist() the way nfs4_put_stid() does. ->sc_free() runs once per stateid, and a stateid reaches free_ol_stateid_reaplist() or nfs4_put_stid() but never both, so the reference is dropped exactly once. Revoked stateids reach this path with sc_export already cleared by drop_stid_export(), so they are skipped rather than double-freed. nfs4_put_stid() itself read sc_export before acquiring cl_lock. drop_stid_export() clears that field and releases the reference under cl_lock, so a concurrent revocation could drop the export in the window between the read and the final put, releasing the same reference twice. Read sc_export while cl_lock is held so the two paths serialize and the reference is released exactly once. Fixes: ba0cde5dc81d ("NFSD: Track svc_export in nfs4_stid") Reported-by: sashiko-bot <sashiko-bot@kernel.org> Closes: https://sashiko.dev/#/patchset/20260707-cel-v3-0-7c0cc16fd54f@kernel.org?part=9 Reviewed-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260709-cel-v4-9-1d519d9be0cb@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>
2026-08-10NFSD: Prevent client use-after-free during close_lru reapingChuck Lever1-0/+5
An nfs4_openowner left on nn->close_lru after its final CLOSE keeps its last closed stateid in oo_last_closed_stid, holding only a raw pointer to its nfs4_client. The laundromat reaps timed-out entries, drops nn->client_lock, and calls nfs4_put_stid(), which dereferences the client through cl_lock. Nothing pins the client across that window, so a concurrent force_expire_client() can free it and nfs4_put_stid() reads freed memory. __destroy_client() hits the same race, walking clp->cl_openowners without cl_lock. Pin the client with cl_rpc_users before dropping client_lock, and skip clients already expiring. __destroy_client() then cleans up its own close_lru entries through release_last_closed_stateid(), so teardown no longer races the laundromat. Fixes: 217526e7ecc9 ("nfsd: protect the close_lru list and oo_last_closed_stid with client_lock") Cc: stable@vger.kernel.org Reviewed-by: Jeff Layton <jlayton@kernel.org> Link: https://patch.msgid.link/20260709-cel-v4-8-1d519d9be0cb@kernel.org Signed-off-by: Chuck Lever <cel@kernel.org>