[SRU][F][PATCH 1/1] NFSD: Limit the number of concurrent async COPY operations
Massimiliano Pellizzer
massimiliano.pellizzer at canonical.com
Tue Jan 28 10:28:44 UTC 2025
From: Chuck Lever <chuck.lever at oracle.com>
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 at vger.kernel.org
Reviewed-by: Jeff Layton <jlayton at kernel.org>
Signed-off-by: Chuck Lever <chuck.lever at oracle.com>
(backported from commit aadc3bbea163b6caaaebfdd2b6c4667fbc726752)
[mpellizzer: backported considering huge changes in functionality
between Focal nfsd implementation and upstream]
CVE-2024-49974
Signed-off-by: Massimiliano Pellizzer <massimiliano.pellizzer at canonical.com>
---
fs/nfsd/netns.h | 1 +
fs/nfsd/nfs4proc.c | 9 +++++++++
fs/nfsd/nfs4state.c | 1 +
fs/nfsd/xdr4.h | 1 +
4 files changed, 12 insertions(+)
diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h
index 82329b5102c69..105e09dd8c68b 100644
--- a/fs/nfsd/netns.h
+++ b/fs/nfsd/netns.h
@@ -128,6 +128,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
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index f6abe41b58e20..55eb3551d2a5a 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -1093,6 +1093,7 @@ void nfs4_put_copy(struct nfsd4_copy *copy)
{
if (!refcount_dec_and_test(©->refcount))
return;
+ atomic_dec(©->cp_nn->pending_async_copies);
kfree(copy);
}
@@ -1288,6 +1289,14 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
async_copy = kzalloc(sizeof(struct nfsd4_copy), GFP_KERNEL);
if (!async_copy)
goto out;
+ 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);
+ kfree(async_copy);
+ goto out;
+ }
if (!nfs4_init_cp_state(nn, copy)) {
kfree(async_copy);
goto out;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index a3f8660803c73..0b4fc42598f39 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -7713,6 +7713,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);
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
index f4737d66ee984..256ed4c34e69c 100644
--- a/fs/nfsd/xdr4.h
+++ b/fs/nfsd/xdr4.h
@@ -546,6 +546,7 @@ struct nfsd4_copy {
struct task_struct *copy_task;
refcount_t refcount;
bool stopped;
+ struct nfsd_net *cp_nn;
};
struct nfsd4_seek {
--
2.43.0
More information about the kernel-team
mailing list