Commit d9a4ea27 authored by Trond Myklebust's avatar Trond Myklebust

[PATCH] Improve NFS READ reply sanity checking

 - 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.
parent e4039bb2
...@@ -233,6 +233,7 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args) ...@@ -233,6 +233,7 @@ nfs_xdr_readargs(struct rpc_rqst *req, u32 *p, struct nfs_readargs *args)
static int static int
nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) 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; struct iovec *iov = req->rq_rvec;
int status, count, recvd, hdrlen; int status, count, recvd, hdrlen;
...@@ -241,25 +242,33 @@ nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) ...@@ -241,25 +242,33 @@ nfs_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
p = xdr_decode_fattr(p, res->fattr); p = xdr_decode_fattr(p, res->fattr);
count = ntohl(*p++); 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; 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"); dprintk("NFS: READ header is short. iovec will be shifted.\n");
xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
} }
recvd = req->rq_rlen - hdrlen; recvd = req->rq_received - hdrlen;
if (count > recvd) { if (count > recvd) {
printk(KERN_WARNING "NFS: server cheating in read reply: " printk(KERN_WARNING "NFS: server cheating in read reply: "
"count %d > recvd %d\n", count, recvd); "count %d > recvd %d\n", count, recvd);
count = recvd; count = recvd;
res->eof = 0;
} }
dprintk("RPC: readres OK count %d\n", count); dprintk("RPC: readres OK count %d\n", count);
if (count < res->count) { if (count < res->count)
res->count = count; res->count = count;
res->eof = 1; /* Silly NFSv3ism which can't be helped */
} else
res->eof = 0;
return count; return count;
} }
......
...@@ -793,16 +793,21 @@ nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res) ...@@ -793,16 +793,21 @@ nfs3_xdr_readres(struct rpc_rqst *req, u32 *p, struct nfs_readres *res)
} }
hdrlen = (u8 *) p - (u8 *) iov->iov_base; 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"); dprintk("NFS: READ header is short. iovec will be shifted.\n");
xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen); xdr_shift_buf(&req->rq_rcv_buf, iov->iov_len - hdrlen);
} }
recvd = req->rq_rlen - hdrlen; recvd = req->rq_received - hdrlen;
if (count > recvd) { if (count > recvd) {
printk(KERN_WARNING "NFS: server cheating in read reply: " printk(KERN_WARNING "NFS: server cheating in read reply: "
"count %d > recvd %d\n", count, recvd); "count %d > recvd %d\n", count, recvd);
count = recvd; count = recvd;
res->eof = 0;
} }
if (count < res->count) if (count < res->count)
......
...@@ -424,9 +424,14 @@ nfs_readpage_result(struct rpc_task *task) ...@@ -424,9 +424,14 @@ nfs_readpage_result(struct rpc_task *task)
memset(p + count, 0, PAGE_CACHE_SIZE - count); memset(p + count, 0, PAGE_CACHE_SIZE - count);
kunmap(page); kunmap(page);
count = 0; count = 0;
} else if (data->res.eof)
SetPageUptodate(page);
else
SetPageError(page);
} else {
count -= PAGE_CACHE_SIZE; count -= PAGE_CACHE_SIZE;
SetPageUptodate(page); SetPageUptodate(page);
}
} else } else
SetPageError(page); SetPageError(page);
flush_dcache_page(page); flush_dcache_page(page);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment