NFSv4: Fix an Oops in the NFSv4 getacl code
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 3 Feb 2012 23:30:53 +0000 (18:30 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 3 Feb 2012 23:50:34 +0000 (18:50 -0500)
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
fs/nfs/nfs4xdr.c
include/linux/nfs_xdr.h

index f0c849c..d202e04 100644 (file)
@@ -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;
 }
 
index 95e92e4..33bd8d0 100644 (file)
@@ -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;
index a764cef..d6ba9a1 100644 (file)
@@ -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;
 };