Commit 64bd577ea0021f5903505de061b3b7d8a785ee94

Authored by Trond Myklebust
1 parent c337d3655c

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

... ... @@ -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,
... ... @@ -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,
... ... @@ -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