| From 331818f1c468a24e581aedcbe52af799366a9dfe Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Date: Fri, 3 Feb 2012 18:30:53 -0500 |
| Subject: [PATCH] NFSv4: Fix an Oops in the NFSv4 getacl code |
| |
| Commit bf118a342f10dafe44b14451a1392c3254629a1f (NFSv4: include bitmap |
| in nfsv4 get acl data) introduces the 'acl_scratch' page for the case |
| where we may need to decode multi-page data. However it fails to take |
| into account the fact that the variable may be NULL (for the case where |
| we're not doing multi-page decode), and it also attaches it to the |
| encoding xdr_stream rather than the decoding one. |
| |
| The immediate result is an Oops in nfs4_xdr_enc_getacl due to the |
| call to page_address() with a NULL page pointer. |
| |
| Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Cc: Andy Adamson <andros@netapp.com> |
| Cc: stable@vger.kernel.org |
| |
| fs/nfs/nfs4proc.c | 8 ++++---- |
| fs/nfs/nfs4xdr.c | 5 ++++- |
| include/linux/nfs_xdr.h | 2 +- |
| 3 files changed, 9 insertions(+), 6 deletions(-) |
| |
| diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c |
| index f0c849c..d202e04 100644 |
| |
| |
| @@ -3575,8 +3575,8 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu |
| } |
| if (npages > 1) { |
| /* for decoding across pages */ |
| - args.acl_scratch = alloc_page(GFP_KERNEL); |
| - if (!args.acl_scratch) |
| + res.acl_scratch = alloc_page(GFP_KERNEL); |
| + if (!res.acl_scratch) |
| goto out_free; |
| } |
| args.acl_len = npages * PAGE_SIZE; |
| @@ -3612,8 +3612,8 @@ out_free: |
| for (i = 0; i < npages; i++) |
| if (pages[i]) |
| __free_page(pages[i]); |
| - if (args.acl_scratch) |
| - __free_page(args.acl_scratch); |
| + if (res.acl_scratch) |
| + __free_page(res.acl_scratch); |
| return ret; |
| } |
| |
| diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c |
| index 95e92e4..33bd8d0 100644 |
| |
| |
| @@ -2522,7 +2522,6 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr, |
| |
| xdr_inline_pages(&req->rq_rcv_buf, replen << 2, |
| args->acl_pages, args->acl_pgbase, args->acl_len); |
| - xdr_set_scratch_buffer(xdr, page_address(args->acl_scratch), PAGE_SIZE); |
| |
| encode_nops(&hdr); |
| } |
| @@ -6032,6 +6031,10 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, |
| struct compound_hdr hdr; |
| int status; |
| |
| + if (res->acl_scratch != NULL) { |
| + void *p = page_address(res->acl_scratch); |
| + xdr_set_scratch_buffer(xdr, p, PAGE_SIZE); |
| + } |
| status = decode_compound_hdr(xdr, &hdr); |
| if (status) |
| goto out; |
| diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h |
| index a764cef..d6ba9a1 100644 |
| |
| |
| @@ -614,7 +614,6 @@ struct nfs_getaclargs { |
| size_t acl_len; |
| unsigned int acl_pgbase; |
| struct page ** acl_pages; |
| - struct page * acl_scratch; |
| struct nfs4_sequence_args seq_args; |
| }; |
| |
| @@ -624,6 +623,7 @@ struct nfs_getaclres { |
| size_t acl_len; |
| size_t acl_data_offset; |
| int acl_flags; |
| + struct page * acl_scratch; |
| struct nfs4_sequence_res seq_res; |
| }; |
| |
| -- |
| 1.7.4.1 |
| |