]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] Improve READDIR/READDIRPLUS sanity checking..
authorTrond Myklebust <trond.myklebust@fys.uio.no>
Tue, 20 Aug 2002 05:24:20 +0000 (22:24 -0700)
committerTrond Myklebust <trond.myklebust@fys.uio.no>
Tue, 20 Aug 2002 05:24:20 +0000 (22:24 -0700)
 - Use req->rq_received to determine the message length instead of
   assuming that it goes to the end of the page.

 - If the server returned an illegal record so that we cannot make
   progress by retrying the request on a fresh page, truncate the
   entire listing and return a syslog error.

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

index e2272ca7e20240a23d4e40c411b83c24bc0469f4..09d6baa1d99e4d3ab8cb84ebdc9b2415e656e738 100644 (file)
@@ -393,7 +393,7 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, void *dummy)
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        struct iovec *iov = rcvbuf->head;
        struct page **page;
-       int hdrlen;
+       int hdrlen, recvd;
        int status, nr;
        unsigned int len, pglen;
        u32 *end, *entry;
@@ -402,17 +402,24 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, void *dummy)
                return -nfs_stat_to_errno(status);
 
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len > hdrlen) {
+       if (iov->iov_len < hdrlen) {
+               printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
+                               "length %d > %d\n", hdrlen, iov->iov_len);
+               return -errno_NFSERR_IO;
+       } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
                xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
        }
 
        pglen = rcvbuf->page_len;
+       recvd = req->rq_received - hdrlen;
+       if (pglen > recvd)
+               pglen = recvd;
        page = rcvbuf->pages;
        p = kmap(*page);
        end = (u32 *)((char *)p + pglen);
+       entry = p;
        for (nr = 0; *p++; nr++) {
-               entry = p - 1;
                if (p + 2 > end)
                        goto short_pkt;
                p++; /* fileid */
@@ -425,14 +432,21 @@ nfs_xdr_readdirres(struct rpc_rqst *req, u32 *p, void *dummy)
                }
                if (p + 2 > end)
                        goto short_pkt;
+               entry = p;
        }
+       if (!nr)
+               goto short_pkt;
+ out:
        kunmap(*page);
        return nr;
  short_pkt:
-       printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
        entry[0] = entry[1] = 0;
-       kunmap(*page);
-       return nr;
+       /* truncate listing ? */
+       if (!nr) {
+               printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
+               entry[1] = 1;
+       }
+       goto out;
 err_unmap:
        kunmap(*page);
        return -errno_NFSERR_IO;
index 2c72ae9f23618e151863f0ba8123473db2d7bb6b..0efc06b7cc145137e4833478d34c54f7deccb587 100644 (file)
@@ -504,7 +504,7 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
        struct xdr_buf *rcvbuf = &req->rq_rcv_buf;
        struct iovec *iov = rcvbuf->head;
        struct page **page;
-       int hdrlen;
+       int hdrlen, recvd;
        int status, nr;
        unsigned int len, pglen;
        u32 *entry, *end;
@@ -523,17 +523,24 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
        }
 
        hdrlen = (u8 *) p - (u8 *) iov->iov_base;
-       if (iov->iov_len > hdrlen) {
+       if (iov->iov_len < hdrlen) {
+               printk(KERN_WARNING "NFS: READDIR reply header overflowed:"
+                               "length %d > %d\n", hdrlen, iov->iov_len);
+               return -errno_NFSERR_IO;
+       } else if (iov->iov_len != hdrlen) {
                dprintk("NFS: READDIR header is short. iovec will be shifted.\n");
                xdr_shift_buf(rcvbuf, iov->iov_len - hdrlen);
        }
 
        pglen = rcvbuf->page_len;
+       recvd = req->rq_received - hdrlen;
+       if (pglen > recvd)
+               pglen = recvd;
        page = rcvbuf->pages;
        p = kmap(*page);
        end = (u32 *)((char *)p + pglen);
+       entry = p;
        for (nr = 0; *p++; nr++) {
-               entry = p - 1;
                if (p + 3 > end)
                        goto short_pkt;
                p += 2;                         /* inode # */
@@ -570,15 +577,21 @@ nfs3_xdr_readdirres(struct rpc_rqst *req, u32 *p, struct nfs3_readdirres *res)
 
                if (p + 2 > end)
                        goto short_pkt;
+               entry = p;
        }
+       if (!nr)
+               goto short_pkt;
+ out:
        kunmap(*page);
        return nr;
  short_pkt:
-       printk(KERN_NOTICE "NFS: short packet in readdir reply!\n");
-       /* truncate listing */
        entry[0] = entry[1] = 0;
-       kunmap(*page);
-       return nr;
+       /* truncate listing ? */
+       if (!nr) {
+               printk(KERN_NOTICE "NFS: readdir reply truncated!\n");
+               entry[1] = 1;
+       }
+       goto out;
 err_unmap:
        kunmap(*page);
        return -errno_NFSERR_IO;