nfsd4: adjust buflen to session channel limit
authorJ. Bruce Fields <bfields@redhat.com>
Thu, 13 Mar 2014 01:39:35 +0000 (21:39 -0400)
committerJ. Bruce Fields <bfields@redhat.com>
Fri, 30 May 2014 21:32:02 +0000 (17:32 -0400)
We can simplify session limit enforcement by restricting the xdr buflen
to the session size.

Also fix a preexisting bug: we should really have been taking into
account the auth-required space when comparing against session limits,
which are limits on the size of the entire rpc reply, including any krb5
overhead.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c

index 8e22ea4..612b85a 100644 (file)
@@ -2208,11 +2208,13 @@ nfsd4_sequence(struct svc_rqst *rqstp,
               struct nfsd4_sequence *seq)
 {
        struct nfsd4_compoundres *resp = rqstp->rq_resp;
+       struct xdr_stream *xdr = &resp->xdr;
        struct nfsd4_session *session;
        struct nfs4_client *clp;
        struct nfsd4_slot *slot;
        struct nfsd4_conn *conn;
        __be32 status;
+       int buflen;
        struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id);
 
        if (resp->opcnt != 1)
@@ -2281,6 +2283,15 @@ nfsd4_sequence(struct svc_rqst *rqstp,
        if (status)
                goto out_put_session;
 
+       buflen = (seq->cachethis) ?
+                       session->se_fchannel.maxresp_cached :
+                       session->se_fchannel.maxresp_sz;
+       status = (seq->cachethis) ? nfserr_rep_too_big_to_cache :
+                                   nfserr_rep_too_big;
+       if (xdr_restrict_buflen(xdr, buflen - 2 * RPC_MAX_AUTH_SIZE))
+               goto out_put_session;
+
+       status = nfs_ok;
        /* Success! bump slot seqid */
        slot->sl_seqid = seq->seqid;
        slot->sl_flags |= NFSD4_SLOT_INUSE;
index 1278d98..5e7bac4 100644 (file)
@@ -3767,25 +3767,17 @@ static nfsd4_enc nfsd4_enc_ops[] = {
 __be32 nfsd4_check_resp_size(struct nfsd4_compoundres *resp, u32 respsize)
 {
        struct xdr_buf *buf = &resp->rqstp->rq_res;
-       struct nfsd4_session *session = resp->cstate.session;
+       struct nfsd4_slot *slot = resp->cstate.slot;
 
-       if (nfsd4_has_session(&resp->cstate)) {
-               struct nfsd4_slot *slot = resp->cstate.slot;
-
-               if (buf->len + respsize > session->se_fchannel.maxresp_sz)
-                       return nfserr_rep_too_big;
-
-               if ((slot->sl_flags & NFSD4_SLOT_CACHETHIS) &&
-                   buf->len + respsize > session->se_fchannel.maxresp_cached)
-                       return nfserr_rep_too_big_to_cache;
-       }
-
-       if (buf->len + respsize > buf->buflen) {
-               WARN_ON_ONCE(nfsd4_has_session(&resp->cstate));
+       if (buf->len + respsize <= buf->buflen)
+               return nfs_ok;
+       if (!nfsd4_has_session(&resp->cstate))
                return nfserr_resource;
+       if (slot->sl_flags & NFSD4_SLOT_CACHETHIS) {
+               WARN_ON_ONCE(1);
+               return nfserr_rep_too_big_to_cache;
        }
-
-       return 0;
+       return nfserr_rep_too_big;
 }
 
 void