[PATCH] Improve NFS READ reply sanity checking

From: Trond Myklebust (trond.myklebust@fys.uio.no)
Date: Tue Aug 20 2002 - 10:54:23 EST


  - 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.

Cheers,
  Trond

diff -u --recursive --new-file linux-2.5.31/fs/nfs/nfs2xdr.c linux-2.5.31-fix_read/fs/nfs/nfs2xdr.c
--- linux-2.5.31/fs/nfs/nfs2xdr.c Wed Jul 31 17:35:17 2002
+++ linux-2.5.31-fix_read/fs/nfs/nfs2xdr.c Tue Aug 20 17:10:08 2002
@@ -233,6 +233,7 @@
 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 @@
         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;
 }
diff -u --recursive --new-file linux-2.5.31/fs/nfs/nfs3xdr.c linux-2.5.31-fix_read/fs/nfs/nfs3xdr.c
--- linux-2.5.31/fs/nfs/nfs3xdr.c Wed Jul 31 17:31:30 2002
+++ linux-2.5.31-fix_read/fs/nfs/nfs3xdr.c Tue Aug 20 17:10:08 2002
@@ -793,16 +793,21 @@
         }
 
         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)
diff -u --recursive --new-file linux-2.5.31/fs/nfs/read.c linux-2.5.31-fix_read/fs/nfs/read.c
--- linux-2.5.31/fs/nfs/read.c Thu May 23 17:10:17 2002
+++ linux-2.5.31-fix_read/fs/nfs/read.c Tue Aug 20 17:10:08 2002
@@ -424,9 +424,14 @@
                                 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);
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Fri Aug 23 2002 - 22:00:20 EST