]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
NFSD: Limit the number of concurrent async COPY operations
authorChuck Lever <chuck.lever@oracle.com>
Mon, 18 Nov 2024 21:23:41 +0000 (16:23 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sat, 14 Dec 2024 18:50:37 +0000 (19:50 +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 08d90e0e8faecf9ff991255ef33247d4ed6c4de9..54f43501fed98b7646ed9d241d6863ba5f82fab5 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 5ab3045c649fb40bf6dbe1b572196ebd1eb293f6..a7016f738647e29535b4406471c20ebae6f9abaf 100644 (file)
@@ -8079,6 +8079,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)