Re: [PATCH v2 03/10] nfsd: fix stale s2s_cp_stateids IDR entry for async COPY

"Chuck Lever" <[email protected]>
Newsgroups gmane.linux.kernel,gmane.linux.nfs
Message-ID <[email protected]>

On Thu, Jul 9, 2026, at 2:47 PM, Jeff Layton wrote:
> 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")
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Jeff Layton <[email protected]>
> ---
>  fs/nfsd/nfs4proc.c | 16 +++++++++++++---
>  1 file changed, 13 insertions(+), 3 deletions(-)
>
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index fad01d67bf3f..1c674479d4ca 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -2272,11 +2272,21 @@ nfsd4_copy(struct svc_rqst *rqstp, struct 
> nfsd4_compound_state *cstate,
>  		async_copy->cp_src = kmalloc_obj(*async_copy->cp_src);
>  		if (!async_copy->cp_src)
>  			goto out_dec_async_copy_err;
> -		if (!nfs4_init_copy_state(nn, copy))
> +		dup_copy_fields(copy, async_copy);
> +		/*
> +		 * Register the copy stateid on the long-lived async_copy
> +		 * rather than on the transient COMPOUND argument buffer
> +		 * (&u->copy). nfs4_init_copy_state() installs a pointer to
> +		 * the copy_stateid_t in nn->s2s_cp_stateids, and that pointer
> +		 * outlives this call (it is removed only when the background
> +		 * copy finishes). Pointing it at &u->copy would leave a stale
> +		 * pointer into reused request memory that the laundromat and
> +		 * OFFLOAD_CANCEL later dereference.
> +		 */
> +		if (!nfs4_init_copy_state(nn, async_copy))
>  			goto out_dec_async_copy_err;
> -		memcpy(&result->cb_stateid, &copy->cp_stateid.cs_stid,
> +		memcpy(&result->cb_stateid, &async_copy->cp_stateid.cs_stid,
>  			sizeof(result->cb_stateid));
> -		dup_copy_fields(copy, async_copy);
>  		if ((READ_ONCE(copy->nf_dst->nf_file->f_mode) &
>  			       FMODE_NOCMTIME) != 0)
>  			async_copy->attr_update = true;
>
> -- 
> 2.55.0

Sashiko spotted some severe issues:

- [Critical] Async copy cancellation paths remove the copy from `clp->async_copies` and free the `async_copy` object, but fail to call `nfs4_free_copy_state()`. This leaks the IDR entry in `nn->s2s_cp_stateids` and leaves it pointing to freed memory, causing a remote Use-After-Free (UAF) DoS.
- [High] Reordering `dup_copy_fields()` before `nfs4_init_copy_state()` leaves `async_copy->cp_res.cb_stateid` uninitialized, causing the server to send an invalid (all-zero) stateid in the `CB_OFFLOAD` callback.

The second one was also spotted by gpt-5.6-sol.

The new code comment here is a design breadcrumb. Probably not useful
to carry it as part of the code, but YMMV.


-- 
Chuck Lever
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.