Commit 64bd577ea0021f5903505de061b3b7d8a785ee94
1 parent
c337d3655c
Exists in
master
and in
20 other branches
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>
Showing 3 changed files with 12 additions and 72 deletions Side-by-side Diff
fs/nfs/nfs2xdr.c
... | ... | @@ -106,19 +106,16 @@ |
106 | 106 | static int decode_nfsdata(struct xdr_stream *xdr, struct nfs_readres *result) |
107 | 107 | { |
108 | 108 | u32 recvd, count; |
109 | - size_t hdrlen; | |
110 | 109 | __be32 *p; |
111 | 110 | |
112 | 111 | p = xdr_inline_decode(xdr, 4); |
113 | 112 | if (unlikely(p == NULL)) |
114 | 113 | goto out_overflow; |
115 | 114 | count = be32_to_cpup(p); |
116 | - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | |
117 | - recvd = xdr->buf->len - hdrlen; | |
115 | + recvd = xdr_read_pages(xdr, count); | |
118 | 116 | if (unlikely(count > recvd)) |
119 | 117 | goto out_cheating; |
120 | 118 | out: |
121 | - xdr_read_pages(xdr, count); | |
122 | 119 | result->eof = 0; /* NFSv2 does not pass EOF flag on the wire. */ |
123 | 120 | result->count = count; |
124 | 121 | return count; |
... | ... | @@ -440,7 +437,6 @@ |
440 | 437 | static int decode_path(struct xdr_stream *xdr) |
441 | 438 | { |
442 | 439 | u32 length, recvd; |
443 | - size_t hdrlen; | |
444 | 440 | __be32 *p; |
445 | 441 | |
446 | 442 | p = xdr_inline_decode(xdr, 4); |
447 | 443 | |
... | ... | @@ -449,12 +445,9 @@ |
449 | 445 | length = be32_to_cpup(p); |
450 | 446 | if (unlikely(length >= xdr->buf->page_len || length > NFS_MAXPATHLEN)) |
451 | 447 | goto out_size; |
452 | - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | |
453 | - recvd = xdr->buf->len - hdrlen; | |
448 | + recvd = xdr_read_pages(xdr, length); | |
454 | 449 | if (unlikely(length > recvd)) |
455 | 450 | goto out_cheating; |
456 | - | |
457 | - xdr_read_pages(xdr, length); | |
458 | 451 | xdr_terminate_string(xdr->buf, length); |
459 | 452 | return 0; |
460 | 453 | out_size: |
... | ... | @@ -972,16 +965,7 @@ |
972 | 965 | */ |
973 | 966 | static int decode_readdirok(struct xdr_stream *xdr) |
974 | 967 | { |
975 | - u32 recvd, pglen; | |
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; | |
968 | + return xdr_read_pages(xdr, xdr->buf->page_len); | |
985 | 969 | } |
986 | 970 | |
987 | 971 | static int nfs2_xdr_dec_readdirres(struct rpc_rqst *req, |
fs/nfs/nfs3xdr.c
... | ... | @@ -246,7 +246,6 @@ |
246 | 246 | static int decode_nfspath3(struct xdr_stream *xdr) |
247 | 247 | { |
248 | 248 | u32 recvd, count; |
249 | - size_t hdrlen; | |
250 | 249 | __be32 *p; |
251 | 250 | |
252 | 251 | p = xdr_inline_decode(xdr, 4); |
253 | 252 | |
... | ... | @@ -255,12 +254,9 @@ |
255 | 254 | count = be32_to_cpup(p); |
256 | 255 | if (unlikely(count >= xdr->buf->page_len || count > NFS3_MAXPATHLEN)) |
257 | 256 | goto out_nametoolong; |
258 | - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | |
259 | - recvd = xdr->buf->len - hdrlen; | |
257 | + recvd = xdr_read_pages(xdr, count); | |
260 | 258 | if (unlikely(count > recvd)) |
261 | 259 | goto out_cheating; |
262 | - | |
263 | - xdr_read_pages(xdr, count); | |
264 | 260 | xdr_terminate_string(xdr->buf, count); |
265 | 261 | return 0; |
266 | 262 | |
... | ... | @@ -1587,7 +1583,6 @@ |
1587 | 1583 | struct nfs_readres *result) |
1588 | 1584 | { |
1589 | 1585 | u32 eof, count, ocount, recvd; |
1590 | - size_t hdrlen; | |
1591 | 1586 | __be32 *p; |
1592 | 1587 | |
1593 | 1588 | p = xdr_inline_decode(xdr, 4 + 4 + 4); |
1594 | 1589 | |
1595 | 1590 | |
... | ... | @@ -1598,13 +1593,10 @@ |
1598 | 1593 | ocount = be32_to_cpup(p++); |
1599 | 1594 | if (unlikely(ocount != count)) |
1600 | 1595 | goto out_mismatch; |
1601 | - hdrlen = (u8 *)xdr->p - (u8 *)xdr->iov->iov_base; | |
1602 | - recvd = xdr->buf->len - hdrlen; | |
1596 | + recvd = xdr_read_pages(xdr, count); | |
1603 | 1597 | if (unlikely(count > recvd)) |
1604 | 1598 | goto out_cheating; |
1605 | - | |
1606 | 1599 | out: |
1607 | - xdr_read_pages(xdr, count); | |
1608 | 1600 | result->eof = eof; |
1609 | 1601 | result->count = count; |
1610 | 1602 | return count; |
... | ... | @@ -2039,16 +2031,7 @@ |
2039 | 2031 | */ |
2040 | 2032 | static int decode_dirlist3(struct xdr_stream *xdr) |
2041 | 2033 | { |
2042 | - u32 recvd, pglen; | |
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; | |
2034 | + return xdr_read_pages(xdr, xdr->buf->page_len); | |
2052 | 2035 | } |
2053 | 2036 | |
2054 | 2037 | static int decode_readdir3resok(struct xdr_stream *xdr, |
fs/nfs/nfs4xdr.c
... | ... | @@ -4920,9 +4920,8 @@ |
4920 | 4920 | |
4921 | 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 | 4923 | __be32 *p; |
4925 | - uint32_t count, eof, recvd, hdrlen; | |
4924 | + uint32_t count, eof, recvd; | |
4926 | 4925 | int status; |
4927 | 4926 | |
4928 | 4927 | status = decode_op_hdr(xdr, OP_READ); |
4929 | 4928 | |
... | ... | @@ -4933,15 +4932,13 @@ |
4933 | 4932 | goto out_overflow; |
4934 | 4933 | eof = be32_to_cpup(p++); |
4935 | 4934 | count = be32_to_cpup(p); |
4936 | - hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; | |
4937 | - recvd = req->rq_rcv_buf.len - hdrlen; | |
4935 | + recvd = xdr_read_pages(xdr, count); | |
4938 | 4936 | if (count > recvd) { |
4939 | 4937 | dprintk("NFS: server cheating in read reply: " |
4940 | 4938 | "count %u > recvd %u\n", count, recvd); |
4941 | 4939 | count = recvd; |
4942 | 4940 | eof = 0; |
4943 | 4941 | } |
4944 | - xdr_read_pages(xdr, count); | |
4945 | 4942 | res->eof = eof; |
4946 | 4943 | res->count = count; |
4947 | 4944 | return 0; |
... | ... | @@ -4952,10 +4949,6 @@ |
4952 | 4949 | |
4953 | 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 | 4952 | int status; |
4960 | 4953 | __be32 verf[2]; |
4961 | 4954 | |
4962 | 4955 | |
... | ... | @@ -4967,22 +4960,12 @@ |
4967 | 4960 | memcpy(verf, readdir->verifier.data, sizeof(verf)); |
4968 | 4961 | dprintk("%s: verifier = %08x:%08x\n", |
4969 | 4962 | __func__, verf[0], verf[1]); |
4970 | - | |
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; | |
4963 | + return xdr_read_pages(xdr, xdr->buf->page_len); | |
4979 | 4964 | } |
4980 | 4965 | |
4981 | 4966 | static int decode_readlink(struct xdr_stream *xdr, struct rpc_rqst *req) |
4982 | 4967 | { |
4983 | 4968 | struct xdr_buf *rcvbuf = &req->rq_rcv_buf; |
4984 | - struct kvec *iov = rcvbuf->head; | |
4985 | - size_t hdrlen; | |
4986 | 4969 | u32 len, recvd; |
4987 | 4970 | __be32 *p; |
4988 | 4971 | int status; |
4989 | 4972 | |
... | ... | @@ -5000,14 +4983,12 @@ |
5000 | 4983 | dprintk("nfs: server returned giant symlink!\n"); |
5001 | 4984 | return -ENAMETOOLONG; |
5002 | 4985 | } |
5003 | - hdrlen = (char *) xdr->p - (char *) iov->iov_base; | |
5004 | - recvd = req->rq_rcv_buf.len - hdrlen; | |
4986 | + recvd = xdr_read_pages(xdr, len); | |
5005 | 4987 | if (recvd < len) { |
5006 | 4988 | dprintk("NFS: server cheating in readlink reply: " |
5007 | 4989 | "count %u > recvd %u\n", len, recvd); |
5008 | 4990 | return -EIO; |
5009 | 4991 | } |
5010 | - xdr_read_pages(xdr, len); | |
5011 | 4992 | /* |
5012 | 4993 | * The XDR encode routine has set things up so that |
5013 | 4994 | * the link text will be copied directly into the |
... | ... | @@ -5066,7 +5047,6 @@ |
5066 | 5047 | __be32 *savep, *bm_p; |
5067 | 5048 | uint32_t attrlen, |
5068 | 5049 | bitmap[3] = {0}; |
5069 | - struct kvec *iov = req->rq_rcv_buf.head; | |
5070 | 5050 | int status; |
5071 | 5051 | size_t page_len = xdr->buf->page_len; |
5072 | 5052 | |
... | ... | @@ -5089,7 +5069,6 @@ |
5089 | 5069 | if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U))) |
5090 | 5070 | return -EIO; |
5091 | 5071 | if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { |
5092 | - size_t hdrlen; | |
5093 | 5072 | |
5094 | 5073 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words |
5095 | 5074 | * are stored with the acl data to handle the problem of |
... | ... | @@ -5098,7 +5077,6 @@ |
5098 | 5077 | |
5099 | 5078 | /* We ignore &savep and don't do consistency checks on |
5100 | 5079 | * the attr length. Let userspace figure it out.... */ |
5101 | - hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; | |
5102 | 5080 | attrlen += res->acl_data_offset; |
5103 | 5081 | if (attrlen > page_len) { |
5104 | 5082 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { |
... | ... | @@ -5707,9 +5685,7 @@ |
5707 | 5685 | __be32 *p; |
5708 | 5686 | int status; |
5709 | 5687 | u32 layout_count; |
5710 | - struct xdr_buf *rcvbuf = &req->rq_rcv_buf; | |
5711 | - struct kvec *iov = rcvbuf->head; | |
5712 | - u32 hdrlen, recvd; | |
5688 | + u32 recvd; | |
5713 | 5689 | |
5714 | 5690 | status = decode_op_hdr(xdr, OP_LAYOUTGET); |
5715 | 5691 | if (status) |
5716 | 5692 | |
... | ... | @@ -5746,16 +5722,13 @@ |
5746 | 5722 | res->type, |
5747 | 5723 | res->layoutp->len); |
5748 | 5724 | |
5749 | - hdrlen = (u8 *) xdr->p - (u8 *) iov->iov_base; | |
5750 | - recvd = req->rq_rcv_buf.len - hdrlen; | |
5725 | + recvd = xdr_read_pages(xdr, res->layoutp->len); | |
5751 | 5726 | if (res->layoutp->len > recvd) { |
5752 | 5727 | dprintk("NFS: server cheating in layoutget reply: " |
5753 | 5728 | "layout len %u > recvd %u\n", |
5754 | 5729 | res->layoutp->len, recvd); |
5755 | 5730 | return -EINVAL; |
5756 | 5731 | } |
5757 | - | |
5758 | - xdr_read_pages(xdr, res->layoutp->len); | |
5759 | 5732 | |
5760 | 5733 | if (layout_count > 1) { |
5761 | 5734 | /* We only handle a length one array at the moment. Any |