]> git.hungrycats.org Git - linux/commitdiff
NFSD: Limit the number of concurrent async COPY operations
authorChuck Lever <chuck.lever@oracle.com>
Mon, 18 Nov 2024 21:18:58 +0000 (16:18 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 22 Nov 2024 14:37:32 +0000 (15:37 +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 b8fb37cc59c43040d82476cff5f2c59b66ab3ac7..6637a7ef974b06883d502868e925d5592baf0d03 100644 (file)
@@ -1243,6 +1243,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);
 }
@@ -1781,10 +1782,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);
@@ -1823,7 +1830,7 @@ out_err:
        }
        if (async_copy)
                cleanup_async_copy(async_copy);
-       status = nfserrno(-ENOMEM);
+       status = nfserr_jukebox;
        goto out;
 }
 
index 893d099e009933a06b329ce0296b0350bd0ab6e3..e195012db48ec6204761ca4f4198ed02a6f2e7b9 100644 (file)
@@ -8076,6 +8076,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 510978e602da628c19d1c4736a41ba59139547a4..9bd1ade6ba54f94f3fa6a466205140447d1769f2 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)