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 | |
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')
-rw-r--r-- | fs/nfs/nfs2xdr.c | 22 | ||||
-rw-r--r-- | fs/nfs/nfs3xdr.c | 23 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 39 |
3 files changed, 12 insertions, 72 deletions
diff --git a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c index db81166182c9..d04f0df7be55 100644 --- a/fs/nfs/nfs2xdr.c +++ b/fs/nfs/nfs2xdr.c | |||
@@ -106,19 +106,16 @@ static void print_overflow_msg(const char *func, const struct xdr_stream *xdr) | |||
106 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) | 106 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) |
107 | { | 107 | { |
108 | u32 recvd, count; | 108 | u32 recvd, count; |
109 | size_t hdrlen; | ||
110 | __be32 *p; | 109 | __be32 *p; |
111 | 110 | ||
112 | p = xdr_inline_decode(xdr, 4); | 111 | p = xdr_inline_decode(xdr, 4); |
113 | if (unlikely(p == NULL)) | 112 | if (unlikely(p == NULL)) |
114 | goto out_overflow; | 113 | goto out_overflow; |
115 | count = be32_to_cpup(p); | 114 | count = be32_to_cpup(p); |
116 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | 115 | recvd = xdr_read_pages(xdr, count); |
117 | recvd = xdr->buf->len - hdrlen; | ||
118 | if (unlikely(count > recvd)) | 116 | if (unlikely(count > recvd)) |
119 | goto out_cheating; | 117 | goto out_cheating; |
120 | out: | 118 | out: |
121 | xdr_read_pages(xdr, count); | ||
122 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ | 119 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ |
123 | result->count = count; | 120 | result->count = count; |
124 | return count; | 121 | return count; |
@@ -440,7 +437,6 @@ static void encode_path(struct xdr_stream *xdr, struct page **pages, u32 length) | |||
440 | static int decode_path(struct xdr_stream *xdr) | 437 | static int decode_path(struct xdr_stream *xdr) |
441 | { | 438 | { |
442 | u32 length, recvd; | 439 | u32 length, recvd; |
443 | size_t hdrlen; | ||
444 | __be32 *p; | 440 | __be32 *p; |
445 | 441 | ||
446 | p = xdr_inline_decode(xdr, 4); | 442 | p = xdr_inline_decode(xdr, 4); |
@@ -449,12 +445,9 @@ static int decode_path(struct xdr_stream *xdr) | |||
449 | length = be32_to_cpup(p); | 445 | length = be32_to_cpup(p); |
450 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) | 446 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) |
451 | goto out_size; | 447 | goto out_size; |
452 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | 448 | recvd = xdr_read_pages(xdr, length); |
453 | recvd = xdr->buf->len - hdrlen; | ||
454 | if (unlikely(length > recvd)) | 449 | if (unlikely(length > recvd)) |
455 | goto out_cheating; | 450 | goto out_cheating; |
456 | |||
457 | xdr_read_pages(xdr, length); | ||
458 | xdr_terminate_string(xdr->buf, length); | 451 | xdr_terminate_string(xdr->buf, length); |
459 | return 0; | 452 | return 0; |
460 | out_size: | 453 | out_size: |
@@ -972,16 +965,7 @@ out_overflow: | |||
972 | */ | 965 | */ |
973 | static int decode_readdirok(struct xdr_stream *xdr) | 966 | static int decode_readdirok(struct xdr_stream *xdr) |
974 | { | 967 | { |
975 | u32 recvd, pglen; | 968 | return xdr_read_pages(xdr, xdr->buf->page_len); |
976 | size_t hdrlen; | ||
977 | |||
978 | pglen = xdr->buf->page_len; | ||
979 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
980 | recvd = xdr->buf->len - hdrlen; | ||
981 | if (pglen > recvd) | ||
982 | pglen = recvd; | ||
983 | xdr_read_pages(xdr, pglen); | ||
984 | return pglen; | ||
985 | } | 969 | } |
986 | 970 | ||
987 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, | 971 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, |
diff --git a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c index 3c61c7f80a4b..d64a00ff5a16 100644 --- a/fs/nfs/nfs3xdr.c +++ b/fs/nfs/nfs3xdr.c | |||
@@ -246,7 +246,6 @@ static void encode_nfspath3(struct xdr_stream *xdr, struct page **pages, | |||
246 | static int decode_nfspath3(struct xdr_stream *xdr) | 246 | static int decode_nfspath3(struct xdr_stream *xdr) |
247 | { | 247 | { |
248 | u32 recvd, count; | 248 | u32 recvd, count; |
249 | size_t hdrlen; | ||
250 | __be32 *p; | 249 | __be32 *p; |
251 | 250 | ||
252 | p = xdr_inline_decode(xdr, 4); | 251 | p = xdr_inline_decode(xdr, 4); |
@@ -255,12 +254,9 @@ static int decode_nfspath3(struct xdr_stream *xdr) | |||
255 | count = be32_to_cpup(p); | 254 | count = be32_to_cpup(p); |
256 | if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN)) | 255 | if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN)) |
257 | goto out_nametoolong; | 256 | goto out_nametoolong; |
258 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | 257 | recvd = xdr_read_pages(xdr, count); |
259 | recvd = xdr->buf->len - hdrlen; | ||
260 | if (unlikely(count > recvd)) | 258 | if (unlikely(count > recvd)) |
261 | goto out_cheating; | 259 | goto out_cheating; |
262 | |||
263 | xdr_read_pages(xdr, count); | ||
264 | xdr_terminate_string(xdr->buf, count); | 260 | xdr_terminate_string(xdr->buf, count); |
265 | return 0; | 261 | return 0; |
266 | 262 | ||
@@ -1587,7 +1583,6 @@ static int decode_read3resok(struct xdr_stream *xdr, | |||
1587 | struct nfs_readres *result) | 1583 | struct nfs_readres *result) |
1588 | { | 1584 | { |
1589 | u32 eof, count, ocount, recvd; | 1585 | u32 eof, count, ocount, recvd; |
1590 | size_t hdrlen; | ||
1591 | __be32 *p; | 1586 | __be32 *p; |
1592 | 1587 | ||
1593 | p = xdr_inline_decode(xdr, 4 + 4 + 4); | 1588 | p = xdr_inline_decode(xdr, 4 + 4 + 4); |
@@ -1598,13 +1593,10 @@ static int decode_read3resok(struct xdr_stream *xdr, | |||
1598 | ocount = be32_to_cpup(p++); | 1593 | ocount = be32_to_cpup(p++); |
1599 | if (unlikely(ocount != count)) | 1594 | if (unlikely(ocount != count)) |
1600 | goto out_mismatch; | 1595 | goto out_mismatch; |
1601 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | 1596 | recvd = xdr_read_pages(xdr, count); |
1602 | recvd = xdr->buf->len - hdrlen; | ||
1603 | if (unlikely(count > recvd)) | 1597 | if (unlikely(count > recvd)) |
1604 | goto out_cheating; | 1598 | goto out_cheating; |
1605 | |||
1606 | out: | 1599 | out: |
1607 | xdr_read_pages(xdr, count); | ||
1608 | result->eof = eof; | 1600 | result->eof = eof; |
1609 | result->count = count; | 1601 | result->count = count; |
1610 | return count; | 1602 | return count; |
@@ -2039,16 +2031,7 @@ out_truncated: | |||
2039 | */ | 2031 | */ |
2040 | static int decode_dirlist3(struct xdr_stream *xdr) | 2032 | static int decode_dirlist3(struct xdr_stream *xdr) |
2041 | { | 2033 | { |
2042 | u32 recvd, pglen; | 2034 | return xdr_read_pages(xdr, xdr->buf->page_len); |
2043 | size_t hdrlen; | ||
2044 | |||
2045 | pglen = xdr->buf->page_len; | ||
2046 | hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | ||
2047 | recvd = xdr->buf->len - hdrlen; | ||
2048 | if (pglen > recvd) | ||
2049 | pglen = recvd; | ||
2050 | xdr_read_pages(xdr, pglen); | ||
2051 | return pglen; | ||
2052 | } | 2035 | } |
2053 | 2036 | ||
2054 | static int decode_readdir3resok(struct xdr_stream *xdr, | 2037 | static int decode_readdir3resok(struct xdr_stream *xdr, |
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 |