diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-08-14 17:30:10 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-08-16 16:15:50 -0400 |
commit | 519d3959e30a98f8e135e7a16647c10af5ad63d5 (patch) | |
tree | 95d4ba3b32892b4145f6054684102c83aca65025 /fs/nfs/nfs4xdr.c | |
parent | 425e776d93a7a5070b77d4f458a5bab0f924652c (diff) |
NFSv4: Fix pointer arithmetic in decode_getacl
Resetting the cursor xdr->p to a previous value is not a safe
practice: if the xdr_stream has crossed out of the initial iovec,
then a bunch of other fields would need to be reset too.
Fix this issue by using xdr_enter_page() so that the buffer gets
page aligned at the bitmap _before_ we decode it.
Also fix the confusion of the ACL length with the page buffer length
by not adding the base offset to the ACL length...
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: stable@vger.kernel.org
Diffstat (limited to 'fs/nfs/nfs4xdr.c')
-rw-r--r-- | fs/nfs/nfs4xdr.c | 21 |
1 files changed, 7 insertions, 14 deletions
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index ca13483edd60..54d3f5a9faa6 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -5049,18 +5049,14 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5049 | uint32_t attrlen, | 5049 | uint32_t attrlen, |
5050 | bitmap[3] = {0}; | 5050 | bitmap[3] = {0}; |
5051 | int status; | 5051 | int status; |
5052 | size_t page_len = xdr->buf->page_len; | ||
5053 | 5052 | ||
5054 | res->acl_len = 0; | 5053 | res->acl_len = 0; |
5055 | if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) | 5054 | if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) |
5056 | goto out; | 5055 | goto out; |
5057 | 5056 | ||
5057 | xdr_enter_page(xdr, xdr->buf->page_len); | ||
5058 | |||
5058 | bm_p = xdr->p; | 5059 | bm_p = xdr->p; |
5059 | res->acl_data_offset = be32_to_cpup(bm_p) + 2; | ||
5060 | res->acl_data_offset <<= 2; | ||
5061 | /* Check if the acl data starts beyond the allocated buffer */ | ||
5062 | if (res->acl_data_offset > page_len) | ||
5063 | return -ERANGE; | ||
5064 | 5060 | ||
5065 | if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) | 5061 | if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) |
5066 | goto out; | 5062 | goto out; |
@@ -5074,23 +5070,20 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
5074 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words | 5070 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words |
5075 | * are stored with the acl data to handle the problem of | 5071 | * are stored with the acl data to handle the problem of |
5076 | * variable length bitmaps.*/ | 5072 | * variable length bitmaps.*/ |
5077 | xdr->p = bm_p; | 5073 | res->acl_data_offset = (xdr->p - bm_p) << 2; |
5078 | 5074 | ||
5079 | /* We ignore &savep and don't do consistency checks on | 5075 | /* We ignore &savep and don't do consistency checks on |
5080 | * the attr length. Let userspace figure it out.... */ | 5076 | * the attr length. Let userspace figure it out.... */ |
5081 | attrlen += res->acl_data_offset; | 5077 | res->acl_len = attrlen; |
5082 | if (attrlen > page_len) { | 5078 | if (attrlen + res->acl_data_offset > xdr->buf->page_len) { |
5083 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { | 5079 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { |
5084 | /* getxattr interface called with a NULL buf */ | 5080 | /* getxattr interface called with a NULL buf */ |
5085 | res->acl_len = attrlen; | ||
5086 | goto out; | 5081 | goto out; |
5087 | } | 5082 | } |
5088 | dprintk("NFS: acl reply: attrlen %u > page_len %zu\n", | 5083 | dprintk("NFS: acl reply: attrlen %u > page_len %u\n", |
5089 | attrlen, page_len); | 5084 | attrlen, xdr->buf->page_len); |
5090 | return -EINVAL; | 5085 | return -EINVAL; |
5091 | } | 5086 | } |
5092 | xdr_read_pages(xdr, attrlen); | ||
5093 | res->acl_len = attrlen; | ||
5094 | } else | 5087 | } else |
5095 | status = -EOPNOTSUPP; | 5088 | status = -EOPNOTSUPP; |
5096 | 5089 | ||