aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorSachin Prabhu <sprabhu@redhat.com>2012-04-17 09:35:39 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-04-27 13:15:07 -0400
commit5a00689930ab975fdd1b37b034475017e460cf2a (patch)
tree9cc6df10ac849488efe28ea811e55c213c22a754 /fs/nfs
parent10bd295a0b6488ebe634b72a11d8986bd3af3819 (diff)
Avoid reading past buffer when calling GETACL
Bug noticed in commit bf118a342f10dafe44b14451a1392c3254629a1f When calling GETACL, if the size of the bitmap array, the length attribute and the acl returned by the server is greater than the allocated buffer(args.acl_len), we can Oops with a General Protection fault at _copy_from_pages() when we attempt to read past the pages allocated. This patch allocates an extra PAGE for the bitmap and checks to see that the bitmap + attribute_length + ACLs don't exceed the buffer space allocated to it. Signed-off-by: Sachin Prabhu <sprabhu@redhat.com> Reported-by: Jian Li <jiali@redhat.com> [Trond: Fixed a size_t vs unsigned int printk() warning] Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/nfs4proc.c16
-rw-r--r--fs/nfs/nfs4xdr.c18
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);