Messages in this thread Patch in this message | | | Date | Tue, 20 Aug 2002 18:24:02 +0200 | Subject | [PATCH] Improve READDIR/READDIRPLUS sanity checking.. | From | Trond Myklebust <> |
| |
- 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/
| |