]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Improve NFS READ reply sanity checking
authorTrond Myklebust <trond.myklebust@fys.uio.no>
Tue, 20 Aug 2002 05:24:14 +0000 (22:24 -0700)
committerTrond Myklebust <trond.myklebust@fys.uio.no>
Tue, 20 Aug 2002 05:24:14 +0000 (22:24 -0700)
 - Fix the check for whether or not the received message length has
   somehow been truncated: we need to use req->rq_received rather
   than the receive buffer length (req->rq_rlen).

 - Ensure that we set res->eof correctly. In particular, we need to
   clear it if we find ourselves attempting to recover from a
   truncated READ.

 - Don't set PageUptodate() on those pages that are the victim of
   message truncation.

fs/nfs/nfs2xdr.c
fs/nfs/nfs3xdr.c
fs/nfs/read.c

index 4883b923d5f18dd04751fd5d926a32cc16df811a..e2272ca7e20240a23d4e40c411b83c24bc0469f4 100644 (file)
@@ -233,6 +233,7 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
 static int
 nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
 {
+       struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        struct iovec *iov = req->rq_rvec;
        int     status, count, recvd, hdrlen;
 
@@ -241,25 +242,33 @@ nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
        p = xdr_decode_fattr(p, res->fattr);
 
        count = ntohl(*p++);
+       res->eof = 0;
+       if (rcvbuf->page_len) {
+               u32 end = page_offset(rcvbuf->pages[0]) + rcvbuf->page_base + count;
+               if (end >= res->fattr->size)
+                       res->eof = 1;
+       }
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len > hdrlen) {
+       if (iov->iov_len < hdrlen) {
+               printk(KERN_WARNING "NFS: READ reply header overflowed:"
+                               "length %d > %d\n", hdrlen, iov->iov_len);
+               return -errno_NFSERR_IO;
+       } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READ header is short. iovec will be shifted.\n");
                xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
        }
 
-       recvd = req->rq_rlen - hdrlen;
+       recvd = req->rq_received - hdrlen;
        if (count > recvd) {
                printk(KERN_WARNING "NFS: server cheating in read reply: "
                        "count %d > recvd %d\n", count, recvd);
                count = recvd;
+               res->eof = 0;
        }
 
        dprintk("RPC:      readres OK count %d\n", count);
-       if (count < res->count) {
+       if (count < res->count)
                res->count = count;
-               res->eof = 1;  /* Silly NFSv3ism which can't be helped */
-       } else
-               res->eof = 0;
 
        return count;
 }
index 4ebce82ad8e04218a36c904764e49a5aa1c2affc..2c72ae9f23618e151863f0ba8123473db2d7bb6b 100644 (file)
@@ -793,16 +793,21 @@ nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
        }
 
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len > hdrlen) {
+       if (iov->iov_len < hdrlen) {
+               printk(KERN_WARNING "NFS: READ reply header overflowed:"
+                               "length %d > %d\n", hdrlen, iov->iov_len);
+                       return -errno_NFSERR_IO;
+       } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READ header is short. iovec will be shifted.\n");
                xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
        }
 
-       recvd = req->rq_rlen - hdrlen;
+       recvd = req->rq_received - hdrlen;
        if (count > recvd) {
                printk(KERN_WARNING "NFS: server cheating in read reply: "
                        "count %d > recvd %d\n", count, recvd);
                count = recvd;
+               res->eof = 0;
        }
 
        if (count < res->count)
index b6231a33cb3940233482fa9545324e0be29e36ae..8d5bbe64cb8dcb85898fa3b107a8b8adc726677b 100644 (file)
@@ -424,9 +424,14 @@ nfs_readpage_result(struct rpc_task *task)
                                memset(p + count, 0, PAGE_CACHE_SIZE - count);
                                kunmap(page);
                                count = 0;
-                       } else
+                               if (data->res.eof)
+                                       SetPageUptodate(page);
+                               else
+                                       SetPageError(page);
+                       } else {
                                count -= PAGE_CACHE_SIZE;
-                       SetPageUptodate(page);
+                               SetPageUptodate(page);
+                       }
                } else
                        SetPageError(page);
                flush_dcache_page(page);