diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-06-20 22:35:05 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-06-28 17:20:43 -0400 |
commit | 64bd577ea0021f5903505de061b3b7d8a785ee94 (patch) | |
tree | 7081480d903325bb82d2384b69cff4579f289de1 /fs/nfs/nfs4xdr.c | |
parent | c337d3655ce85e12c7c476cb81406521926cacd2 (diff) |
NFS: Let xdr_read_pages() check for buffer overflows
xdr_read_pages will already do all of the buffer overflow checks that are
currently being open-coded in the various callers. This patch simplifies
the existing code by replacing the open coded checks.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4xdr.c')
-rw-r--r-- | fs/nfs/nfs4xdr.c | 39 |
1 files changed, 6 insertions, 33 deletions
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 18fae29b0301..2754f7268c1f 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -4920,9 +4920,8 @@ static int decode_putrootfh(struct xdr_stream *xdr) | |||
4920 | 4920 | ||
4921 | static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res) | 4921 | static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_readres *res) |
4922 | { | 4922 | { |
4923 | struct kvec *iov = req->rq_rcv_buf.head; | ||
4924 | __be32 *p; | 4923 | __be32 *p; |
4925 | uint32_t count, eof, recvd, hdrlen; | 4924 | uint32_t count, eof, recvd; |
4926 | int status; | 4925 | int status; |
4927 | 4926 | ||
4928 | status = decode_op_hdr(xdr, OP_READ); | 4927 | status = decode_op_hdr(xdr, OP_READ); |
@@ -4933,15 +4932,13 @@ static int decode_read(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs_ | |||
4933 | goto out_overflow; | 4932 | goto out_overflow; |
4934 | eof = be32_to_cpup(p++); | 4933 | eof = be32_to_cpup(p++); |
4935 | count = be32_to_cpup(p); | 4934 | count = be32_to_cpup(p); |
4936 | hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; | 4935 | recvd = xdr_read_pages(xdr, count); |
4937 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
4938 | if (count > recvd) { | 4936 | if (count > recvd) { |
4939 | dprintk("NFS: server cheating in read reply: " | 4937 | dprintk("NFS: server cheating in read reply: " |
4940 | "count %u > recvd %u\n", count, recvd); | 4938 | "count %u > recvd %u\n", count, recvd); |
4941 | count = recvd; | 4939 | count = recvd; |
4942 | eof = 0; | 4940 | eof = 0; |
4943 | } | 4941 | } |
4944 | xdr_read_pages(xdr, count); | ||
4945 | res->eof = eof; | 4942 | res->eof = eof; |
4946 | res->count = count; | 4943 | res->count = count; |
4947 | return 0; | 4944 | return 0; |
@@ -4952,10 +4949,6 @@ out_overflow: | |||
4952 | 4949 | ||
4953 | static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) | 4950 | static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct nfs4_readdir_res *readdir) |
4954 | { | 4951 | { |
4955 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | ||
4956 | struct kvec *iov = rcvbuf->head; | ||
4957 | size_t hdrlen; | ||
4958 | u32 recvd, pglen = rcvbuf->page_len; | ||
4959 | int status; | 4952 | int status; |
4960 | __be32 verf[2]; | 4953 | __be32 verf[2]; |
4961 | 4954 | ||
@@ -4967,22 +4960,12 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n | |||
4967 | memcpy(verf, readdir->verifier.data, sizeof(verf)); | 4960 | memcpy(verf, readdir->verifier.data, sizeof(verf)); |
4968 | dprintk("%s: verifier = %08x:%08x\n", | 4961 | dprintk("%s: verifier = %08x:%08x\n", |
4969 | __func__, verf[0], verf[1]); | 4962 | __func__, verf[0], verf[1]); |
4970 | 4963 | return xdr_read_pages(xdr, xdr->buf->page_len); | |
4971 | hdrlen = (char *) xdr->p - (char *) iov->iov_base; | ||
4972 | recvd = rcvbuf->len - hdrlen; | ||
4973 | if (pglen > recvd) | ||
4974 | pglen = recvd; | ||
4975 | xdr_read_pages(xdr, pglen); | ||
4976 | |||
4977 | |||
4978 | return pglen; | ||
4979 | } | 4964 | } |
4980 | 4965 | ||
4981 | static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) | 4966 | static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) |
4982 | { | 4967 | { |
4983 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 4968 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; |
4984 | struct kvec *iov = rcvbuf->head; | ||
4985 | size_t hdrlen; | ||
4986 | u32 len, recvd; | 4969 | u32 len, recvd; |
4987 | __be32 *p; | 4970 | __be32 *p; |
4988 | int status; | 4971 | int status; |
@@ -5000,14 +4983,12 @@ static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) | |||
5000 | dprintk("nfs: server returned giant symlink!\n"); | 4983 | dprintk("nfs: server returned giant symlink!\n"); |
5001 | return -ENAMETOOLONG; | 4984 | return -ENAMETOOLONG; |
5002 | } | 4985 | } |
5003 | hdrlen = (char *) xdr->p - (char *) iov->iov_base; | 4986 | recvd = xdr_read_pages(xdr, len); |
5004 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
5005 | if (recvd < len) { | 4987 | if (recvd < len) { |
5006 | dprintk("NFS: server cheating in readlink reply: " | 4988 | dprintk("NFS: server cheating in readlink reply: " |
5007 | "count %u > recvd %u\n", len, recvd); | 4989 | "count %u > recvd %u\n", len, recvd); |
5008 | return -EIO; | 4990 | return -EIO; |
5009 | } | 4991 | } |
5010 | xdr_read_pages(xdr, len); | ||
5011 | /* | 4992 | /* |
5012 | * The XDR encode routine has set things up so that | 4993 | * The XDR encode routine has set things up so that |
5013 | * the link text will be copied directly into the | 4994 | * the link text will be copied directly into the |
@@ -5066,7 +5047,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5066 | __be32 *savep, *bm_p; | 5047 | __be32 *savep, *bm_p; |
5067 | uint32_t attrlen, | 5048 | uint32_t attrlen, |
5068 | bitmap[3] = {0}; | 5049 | bitmap[3] = {0}; |
5069 | struct kvec *iov = req->rq_rcv_buf.head; | ||
5070 | int status; | 5050 | int status; |
5071 | size_t page_len = xdr->buf->page_len; | 5051 | size_t page_len = xdr->buf->page_len; |
5072 | 5052 | ||
@@ -5089,7 +5069,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5089 | if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U))) | 5069 | if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U))) |
5090 | return -EIO; | 5070 | return -EIO; |
5091 | if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { | 5071 | if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { |
5092 | size_t hdrlen; | ||
5093 | 5072 | ||
5094 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words | 5073 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words |
5095 | * are stored with the acl data to handle the problem of | 5074 | * are stored with the acl data to handle the problem of |
@@ -5098,7 +5077,6 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5098 | 5077 | ||
5099 | /* We ignore &savep and don't do consistency checks on | 5078 | /* We ignore &savep and don't do consistency checks on |
5100 | * the attr length. Let userspace figure it out.... */ | 5079 | * the attr length. Let userspace figure it out.... */ |
5101 | hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; | ||
5102 | attrlen += res->acl_data_offset; | 5080 | attrlen += res->acl_data_offset; |
5103 | if (attrlen > page_len) { | 5081 | if (attrlen > page_len) { |
5104 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { | 5082 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { |
@@ -5707,9 +5685,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5707 | __be32 *p; | 5685 | __be32 *p; |
5708 | int status; | 5686 | int status; |
5709 | u32 layout_count; | 5687 | u32 layout_count; |
5710 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | 5688 | u32 recvd; |
5711 | struct kvec *iov = rcvbuf->head; | ||
5712 | u32 hdrlen, recvd; | ||
5713 | 5689 | ||
5714 | status = decode_op_hdr(xdr, OP_LAYOUTGET); | 5690 | status = decode_op_hdr(xdr, OP_LAYOUTGET); |
5715 | if (status) | 5691 | if (status) |
@@ -5746,8 +5722,7 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5746 | res->type, | 5722 | res->type, |
5747 | res->layoutp->len); | 5723 | res->layoutp->len); |
5748 | 5724 | ||
5749 | hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; | 5725 | recvd = xdr_read_pages(xdr, res->layoutp->len); |
5750 | recvd = req->rq_rcv_buf.len - hdrlen; | ||
5751 | if (res->layoutp->len > recvd) { | 5726 | if (res->layoutp->len > recvd) { |
5752 | dprintk("NFS: server cheating in layoutget reply: " | 5727 | dprintk("NFS: server cheating in layoutget reply: " |
5753 | "layout len %u > recvd %u\n", | 5728 | "layout len %u > recvd %u\n", |
@@ -5755,8 +5730,6 @@ static int decode_layoutget(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5755 | return -EINVAL; | 5730 | return -EINVAL; |
5756 | } | 5731 | } |
5757 | 5732 | ||
5758 | xdr_read_pages(xdr, res->layoutp->len); | ||
5759 | |||
5760 | if (layout_count > 1) { | 5733 | if (layout_count > 1) { |
5761 | /* We only handle a length one array at the moment. Any | 5734 | /* We only handle a length one array at the moment. Any |
5762 | * further entries are just ignored. Note that this means | 5735 | * further entries are just ignored. Note that this means |