[PATCH] Improve NFS READ reply sanity checking

Trond Myklebust (trond.myklebust@fys.uio.no)
Tue, 20 Aug 2002 17:54:23 +0200


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