]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
xprtrdma: Sanitize the reply credit grant after parsing
authorChuck Lever <chuck.lever@oracle.com>
Thu, 4 Jun 2026 17:06:38 +0000 (13:06 -0400)
committerAnna Schumaker <anna.schumaker@hammerspace.com>
Wed, 10 Jun 2026 19:47:06 +0000 (15:47 -0400)
The out_norqst exit in rpcrdma_reply_handler() branches away before
the credit clamp, so a reply that matches no pending request reaches
out_post carrying the raw credit value parsed from the wire.
rpcrdma_post_recvs() does not bound its @needed argument: the refill
loop allocates and chains Receive WRs until the count is satisfied or
allocation fails. A peer that sends a well-formed reply carrying an
unknown XID and an inflated credit grant therefore drives rep
allocation and Receive posting past re_max_requests on every such
reply.

Move the clamp to immediately after the credit field is parsed,
ahead of the first branch that can reach out_post, so every later
consumer sees a sanitized value. The cwnd update stays on the
matched-request path.

Fixes: 704f3f640f72 ("xprtrdma: Post receive buffers after RPC completion")
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>
net/sunrpc/xprtrdma/rpc_rdma.c

index 63e64d53e2892edd6ba3d38bcb04d9eab9909817..5ff086ccd2592bac4d7ddf7c52a6ac2a7738ac81 100644 (file)
@@ -1472,6 +1472,14 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
        credits = be32_to_cpu(*p++);
        rep->rr_proc = *p++;
 
+       /* The credit grant from the wire is not trustworthy;
+        * sanitize it before any code path consumes it.
+        */
+       if (credits == 0)
+               credits = 1;    /* don't deadlock */
+       else if (credits > r_xprt->rx_ep->re_max_requests)
+               credits = r_xprt->rx_ep->re_max_requests;
+
        if (rep->rr_vers != rpcrdma_version)
                goto out_badversion;
 
@@ -1488,10 +1496,6 @@ void rpcrdma_reply_handler(struct rpcrdma_rep *rep)
        xprt_pin_rqst(rqst);
        spin_unlock(&xprt->queue_lock);
 
-       if (credits == 0)
-               credits = 1;    /* don't deadlock */
-       else if (credits > r_xprt->rx_ep->re_max_requests)
-               credits = r_xprt->rx_ep->re_max_requests;
        if (buf->rb_credits != credits)
                rpcrdma_update_cwnd(r_xprt, credits);