diff options
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/nfs4proc.c | 16 | ||||
-rw-r--r-- | fs/nfs/nfs4xdr.c | 18 |
2 files changed, 21 insertions, 13 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 60d5f4c26dda..f5f125fdae1b 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -3684,19 +3684,23 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu | |||
3684 | if (npages == 0) | 3684 | if (npages == 0) |
3685 | npages = 1; | 3685 | npages = 1; |
3686 | 3686 | ||
3687 | /* Add an extra page to handle the bitmap returned */ | ||
3688 | npages++; | ||
3689 | |||
3687 | for (i = 0; i < npages; i++) { | 3690 | for (i = 0; i < npages; i++) { |
3688 | pages[i] = alloc_page(GFP_KERNEL); | 3691 | pages[i] = alloc_page(GFP_KERNEL); |
3689 | if (!pages[i]) | 3692 | if (!pages[i]) |
3690 | goto out_free; | 3693 | goto out_free; |
3691 | } | 3694 | } |
3692 | if (npages > 1) { | 3695 | |
3693 | /* for decoding across pages */ | 3696 | /* for decoding across pages */ |
3694 | res.acl_scratch = alloc_page(GFP_KERNEL); | 3697 | res.acl_scratch = alloc_page(GFP_KERNEL); |
3695 | if (!res.acl_scratch) | 3698 | if (!res.acl_scratch) |
3696 | goto out_free; | 3699 | goto out_free; |
3697 | } | 3700 | |
3698 | args.acl_len = npages * PAGE_SIZE; | 3701 | args.acl_len = npages * PAGE_SIZE; |
3699 | args.acl_pgbase = 0; | 3702 | args.acl_pgbase = 0; |
3703 | |||
3700 | /* Let decode_getfacl know not to fail if the ACL data is larger than | 3704 | /* Let decode_getfacl know not to fail if the ACL data is larger than |
3701 | * the page we send as a guess */ | 3705 | * the page we send as a guess */ |
3702 | if (buf == NULL) | 3706 | if (buf == NULL) |
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 77fc5f959c4e..9312dd78d349 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c | |||
@@ -4902,11 +4902,19 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
4902 | bitmap[3] = {0}; | 4902 | bitmap[3] = {0}; |
4903 | struct kvec *iov = req->rq_rcv_buf.head; | 4903 | struct kvec *iov = req->rq_rcv_buf.head; |
4904 | int status; | 4904 | int status; |
4905 | size_t page_len = xdr->buf->page_len; | ||
4905 | 4906 | ||
4906 | res->acl_len = 0; | 4907 | res->acl_len = 0; |
4907 | if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) | 4908 | if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0) |
4908 | goto out; | 4909 | goto out; |
4910 | |||
4909 | bm_p = xdr->p; | 4911 | bm_p = xdr->p; |
4912 | res->acl_data_offset = be32_to_cpup(bm_p) + 2; | ||
4913 | res->acl_data_offset <<= 2; | ||
4914 | /* Check if the acl data starts beyond the allocated buffer */ | ||
4915 | if (res->acl_data_offset > page_len) | ||
4916 | return -ERANGE; | ||
4917 | |||
4910 | if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) | 4918 | if ((status = decode_attr_bitmap(xdr, bitmap)) != 0) |
4911 | goto out; | 4919 | goto out; |
4912 | if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) | 4920 | if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0) |
@@ -4916,28 +4924,24 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req, | |||
4916 | return -EIO; | 4924 | return -EIO; |
4917 | if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { | 4925 | if (likely(bitmap[0] & FATTR4_WORD0_ACL)) { |
4918 | size_t hdrlen; | 4926 | size_t hdrlen; |
4919 | u32 recvd; | ||
4920 | 4927 | ||
4921 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words | 4928 | /* The bitmap (xdr len + bitmaps) and the attr xdr len words |
4922 | * are stored with the acl data to handle the problem of | 4929 | * are stored with the acl data to handle the problem of |
4923 | * variable length bitmaps.*/ | 4930 | * variable length bitmaps.*/ |
4924 | xdr->p = bm_p; | 4931 | xdr->p = bm_p; |
4925 | res->acl_data_offset = be32_to_cpup(bm_p) + 2; | ||
4926 | res->acl_data_offset <<= 2; | ||
4927 | 4932 | ||
4928 | /* We ignore &savep and don't do consistency checks on | 4933 | /* We ignore &savep and don't do consistency checks on |
4929 | * the attr length. Let userspace figure it out.... */ | 4934 | * the attr length. Let userspace figure it out.... */ |
4930 | hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; | 4935 | hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base; |
4931 | attrlen += res->acl_data_offset; | 4936 | attrlen += res->acl_data_offset; |
4932 | recvd = req->rq_rcv_buf.len - hdrlen; | 4937 | if (attrlen > page_len) { |
4933 | if (attrlen > recvd) { | ||
4934 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { | 4938 | if (res->acl_flags & NFS4_ACL_LEN_REQUEST) { |
4935 | /* getxattr interface called with a NULL buf */ | 4939 | /* getxattr interface called with a NULL buf */ |
4936 | res->acl_len = attrlen; | 4940 | res->acl_len = attrlen; |
4937 | goto out; | 4941 | goto out; |
4938 | } | 4942 | } |
4939 | dprintk("NFS: acl reply: attrlen %u > recvd %u\n", | 4943 | dprintk("NFS: acl reply: attrlen %zu > page_len %u\n", |
4940 | attrlen, recvd); | 4944 | attrlen, page_len); |
4941 | return -EINVAL; | 4945 | return -EINVAL; |
4942 | } | 4946 | } |
4943 | xdr_read_pages(xdr, attrlen); | 4947 | xdr_read_pages(xdr, attrlen); |