]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFSD: Limit the number of concurrent async COPY operations
authorChuck Lever <chuck.lever@oracle.com>
Tue, 19 Nov 2024 00:47:30 +0000 (19:47 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 18:47:39 +0000 (19:47 +0100)
[ Upstream commit 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: Jeff Layton <jlayton@kernel.org>
Link: https://nvd.nist.gov/vuln/detail/CVE-2024-49974
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
fs/nfsd/netns.h
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/xdr4.h

index 548422b24a7d785785f5b75d92e4ce3dee118cb7..41c750f3447375e0cae4fe2a3afbb3aaca4c0fd4 100644 (file)
@@ -152,6 +152,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
index 3a445b78edd5905473e9ae685d6dd78533f3249d..9718af3c261152cc30a47cad4d4c90909f5e9aab 100644 (file)
@@ -1244,6 +1244,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);
 }
@@ -1782,10 +1783,16 @@ nfsd4_copy(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        memcpy(&copy->fh, &cstate->current_fh.fh_handle,
                sizeof(struct knfsd_fh));
        if (nfsd4_copy_is_async(copy)) {
-               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);
@@ -1824,7 +1831,7 @@ out_err:
        }
        if (async_copy)
                cleanup_async_copy(async_copy);
-       status = nfserrno(-ENOMEM);
+       status = nfserr_jukebox;
        goto out;
 }
 
index 18d64a9312a7a16d95407a49eecf9631d8852aed..5547db4db8e67041277250d48294c582d45c80a2 100644 (file)
@@ -8088,6 +8088,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);
index a034b9b62137c6ea64386eaff6304c4a5e6fa56c..15a617bece00a5c3790c09802ef23b39e76763a1 100644 (file)
@@ -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)