[PATCH] Improve READDIR/READDIRPLUS sanity checking..

From: Trond Myklebust (trond.myklebust@fys.uio.no)
Date: Tue Aug 20 2002 - 11:24:02 EST


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

Cheers,
  Trond

diff -u --recursive --new-file linux-2.5.31-fix_read/fs/nfs/nfs2xdr.c linux-2.5.31-fix_readdir/fs/nfs/nfs2xdr.c
--- linux-2.5.31-fix_read/fs/nfs/nfs2xdr.c Tue Aug 20 17:10:08 2002
+++ linux-2.5.31-fix_readdir/fs/nfs/nfs2xdr.c Tue Aug 20 17:10:52 2002
@@ -393,7 +393,7 @@
         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 @@
                 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 @@
                 }
                 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;
diff -u --recursive --new-file linux-2.5.31-fix_read/fs/nfs/nfs3xdr.c linux-2.5.31-fix_readdir/fs/nfs/nfs3xdr.c
--- linux-2.5.31-fix_read/fs/nfs/nfs3xdr.c Tue Aug 20 17:10:08 2002
+++ linux-2.5.31-fix_readdir/fs/nfs/nfs3xdr.c Tue Aug 20 17:10:52 2002
@@ -504,7 +504,7 @@
         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 @@
         }
 
         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 @@
 
                 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;
-
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