Commit 0d27eb5c authored by Chuck Lever's avatar Chuck Lever Committed by Li Lingfeng
Browse files

NFSD: Limit the number of concurrent async COPY operations

mainline inclusion
from mainline-v6.12-rc1
commit aadc3bbea163b6caaaebfdd2b6c4667fbc726752
category: bugfix
bugzilla: https://gitee.com/src-openeuler/kernel/issues/IAYR9C
CVE: CVE-2024-49974

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=aadc3bbea163b6caaaebfdd2b6c4667fbc726752



----------------------------------------------------------------------

Nothing appears to limit the number of concurrent async COPY
operations that clients can start. In addition, AFAICT each async
COPY can copy an unlimited number of 4MB chunks, so can run for a
long time. Thus IMO async COPY can become a DoS vector.

Add a restriction mechanism that bounds the number of concurrent
background COPY operations. Start simple and try to be fair -- this
patch implements a per-namespace limit.

An async COPY request that occurs while this limit is exceeded gets
NFS4ERR_DELAY. The requesting client can choose to send the request
again after a delay or fall back to a traditional read/write style
copy.

If there is need to make the mechanism more sophisticated, we can
visit that in future patches.

Cc: stable@vger.kernel.org
Reviewed-by: default avatarJeff Layton <jlayton@kernel.org>
Signed-off-by: default avatarChuck Lever <chuck.lever@oracle.com>

Conflicts:
  fs/nfsd/nfs4proc.c
[Commit 9ed666eba4e0("NFSD: Async COPY result needs to return a write
verifier") move net_generic to the begining of nfsd4_copy]
Signed-off-by: default avatarLi Lingfeng <lilingfeng3@huawei.com>
parent f34aecb9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -153,6 +153,7 @@ struct nfsd_net {
	u32		s2s_cp_cl_id;
	struct idr	s2s_cp_stateids;
	spinlock_t	s2s_cp_lock;
	atomic_t	pending_async_copies;

	/*
	 * Version information
+9 −2
Original line number Diff line number Diff line
@@ -1282,6 +1282,7 @@ static void nfs4_put_copy(struct nfsd4_copy *copy)
{
	if (!refcount_dec_and_test(&copy->refcount))
		return;
	atomic_dec(&copy->cp_nn->pending_async_copies);
	kfree(copy->cp_src);
	kfree(copy);
}
@@ -1818,10 +1819,16 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	if (nfsd4_copy_is_async(copy)) {
		struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);

		status = nfserrno(-ENOMEM);
		async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
		if (!async_copy)
			goto out_err;
		async_copy->cp_nn = nn;
		/* Arbitrary cap on number of pending async copy operations */
		if (atomic_inc_return(&nn->pending_async_copies) >
			(int)rqstp->rq_pool->sp_nrthreads) {
			atomic_dec(&nn->pending_async_copies);
			goto out_err;
		}
		INIT_LIST_HEAD(&async_copy->copies);
		refcount_set(&async_copy->refcount, 1);
		async_copy->cp_src = kmalloc(sizeof(*async_copy->cp_src), GFP_KERNEL);
@@ -1860,7 +1867,7 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
	}
	if (async_copy)
		cleanup_async_copy(async_copy);
	status = nfserrno(-ENOMEM);
	status = nfserr_jukebox;
	goto out;
}

+1 −0
Original line number Diff line number Diff line
@@ -8141,6 +8141,7 @@ static int nfs4_state_create_net(struct net *net)
	spin_lock_init(&nn->client_lock);
	spin_lock_init(&nn->s2s_cp_lock);
	idr_init(&nn->s2s_cp_stateids);
	atomic_set(&nn->pending_async_copies, 0);

	spin_lock_init(&nn->blocked_locks_lock);
	INIT_LIST_HEAD(&nn->blocked_locks_lru);
+1 −0
Original line number Diff line number Diff line
@@ -574,6 +574,7 @@ struct nfsd4_copy {
	struct nfsd4_ssc_umount_item *ss_nsui;
	struct nfs_fh		c_fh;
	nfs4_stateid		stateid;
	struct nfsd_net		*cp_nn;
};

static inline void nfsd4_copy_set_sync(struct nfsd4_copy *copy, bool sync)