Two early return paths in nfs_local_doio() fail to release the localio (nfsd_file) reference passed in by the caller: - When hdr->args.count is zero, the function returns 0 without calling nfs_local_file_put(). - When nfs_local_iocb_init() fails (e.g. -ENOMEM from allocation or -EOPNOTSUPP if the file lacks read_iter/write_iter), the function returns the error without releasing localio or completing the hdr lifecycle. A leaked nfsd_file pins the associated net namespace reference, blocking network namespace teardown, and holds a reference on the exported filesystem, preventing unmount. Fix the zero-count path by adding the missing nfs_local_file_put() call. Fix the iocb init failure path by jumping to a new cleanup label that releases localio, sets hdr->task.tk_status, and calls nfs_local_hdr_release() -- matching the existing error handling pattern for the post-iocb error path. Fixes: e77c464c31b3 ("nfs/nfsd: add "local io" support") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Jeff Layton --- fs/nfs/localio.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/fs/nfs/localio.c b/fs/nfs/localio.c index e55c5977fcc3..63cf6e2cc745 100644 --- a/fs/nfs/localio.c +++ b/fs/nfs/localio.c @@ -970,12 +970,16 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, struct nfs_local_kiocb *iocb; int status = 0; - if (!hdr->args.count) + if (!hdr->args.count) { + nfs_local_file_put(localio); return 0; + } iocb = nfs_local_iocb_init(hdr, localio); - if (IS_ERR(iocb)) - return PTR_ERR(iocb); + if (IS_ERR(iocb)) { + status = PTR_ERR(iocb); + goto out_put_localio; + } switch (hdr->rw_mode) { case FMODE_READ: @@ -996,6 +1000,12 @@ int nfs_local_doio(struct nfs_client *clp, struct nfsd_file *localio, nfs_local_hdr_release(hdr, call_ops); } return status; + +out_put_localio: + nfs_local_file_put(localio); + hdr->task.tk_status = status; + nfs_local_hdr_release(hdr, call_ops); + return status; } static void -- 2.54.0