]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - fs/nfsd/nfssvc.c
sunrpc: properly type pc_encode callbacks
[linux.git] / fs / nfsd / nfssvc.c
index 31e1f959345715a59f8f6b9d9ec00a0ec00fece5..5552336641246e376573956bfa667d2d4cea3c3a 100644 (file)
@@ -747,11 +747,41 @@ static __be32 map_new_errors(u32 vers, __be32 nfserr)
        return nfserr;
 }
 
+/*
+ * A write procedure can have a large argument, and a read procedure can
+ * have a large reply, but no NFSv2 or NFSv3 procedure has argument and
+ * reply that can both be larger than a page.  The xdr code has taken
+ * advantage of this assumption to be a sloppy about bounds checking in
+ * some cases.  Pending a rewrite of the NFSv2/v3 xdr code to fix that
+ * problem, we enforce these assumptions here:
+ */
+static bool nfs_request_too_big(struct svc_rqst *rqstp,
+                               struct svc_procedure *proc)
+{
+       /*
+        * The ACL code has more careful bounds-checking and is not
+        * susceptible to this problem:
+        */
+       if (rqstp->rq_prog != NFS_PROGRAM)
+               return false;
+       /*
+        * Ditto NFSv4 (which can in theory have argument and reply both
+        * more than a page):
+        */
+       if (rqstp->rq_vers >= 4)
+               return false;
+       /* The reply will be small, we're OK: */
+       if (proc->pc_xdrressize > 0 &&
+           proc->pc_xdrressize < XDR_QUADLEN(PAGE_SIZE))
+               return false;
+
+       return rqstp->rq_arg.len > PAGE_SIZE;
+}
+
 int
 nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
 {
        struct svc_procedure    *proc;
-       kxdrproc_t              xdr;
        __be32                  nfserr;
        __be32                  *nfserrp;
 
@@ -759,15 +789,19 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
                                rqstp->rq_vers, rqstp->rq_proc);
        proc = rqstp->rq_procinfo;
 
+       if (nfs_request_too_big(rqstp, proc)) {
+               dprintk("nfsd: NFSv%d argument too large\n", rqstp->rq_vers);
+               *statp = rpc_garbage_args;
+               return 1;
+       }
        /*
         * Give the xdr decoder a chance to change this if it wants
         * (necessary in the NFSv4.0 compound case)
         */
        rqstp->rq_cachetype = proc->pc_cachetype;
        /* Decode arguments */
-       xdr = proc->pc_decode;
-       if (xdr && !xdr(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base,
-                       rqstp->rq_argp)) {
+       if (proc->pc_decode &&
+           !proc->pc_decode(rqstp, (__be32*)rqstp->rq_arg.head[0].iov_base)) {
                dprintk("nfsd: failed to decode arguments!\n");
                *statp = rpc_garbage_args;
                return 1;
@@ -791,7 +825,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
        rqstp->rq_res.head[0].iov_len += sizeof(__be32);
 
        /* Now call the procedure handler, and encode NFS status. */
-       nfserr = proc->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);
+       nfserr = proc->pc_func(rqstp);
        nfserr = map_new_errors(rqstp->rq_vers, nfserr);
        if (nfserr == nfserr_dropit || test_bit(RQ_DROPME, &rqstp->rq_flags)) {
                dprintk("nfsd: Dropping request; may be revisited later\n");
@@ -806,9 +840,7 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp)
         * For NFSv2, additional info is never returned in case of an error.
         */
        if (!(nfserr && rqstp->rq_vers == 2)) {
-               xdr = proc->pc_encode;
-               if (xdr && !xdr(rqstp, nfserrp,
-                               rqstp->rq_resp)) {
+               if (proc->pc_encode && !proc->pc_encode(rqstp, nfserrp)) {
                        /* Failed to encode result. Release cache entry */
                        dprintk("nfsd: failed to encode result!\n");
                        nfsd_cache_update(rqstp, RC_NOCACHE, NULL);