On Fri, 2012-02-03 at 18:08 -0500, J. Bruce Fields wrote: > On Fri, Feb 03, 2012 at 11:36:04PM +0100, koen@xxxxxxxxxxxx wrote: > > Hi, > > > > Since updating from kernel 3.2.1 to 3.2.2, copying a file from a nfs mount > > using `cp` results in a kernel oops. Copying the same file with `cat > > /nfsmount/file > /local/file` gives no problems. > > Hm, the oops is in getattr encoding, and doing a > > gitk v3.2.1..v3.2.2 fs/nfs net/sunrpc > > turns up one commit that modifies that code: > > 628fc192adbaae0c6178b9015fb916ce61d72b36 "NFSv4: include bitmap > in nfsv4 get acl data" > > I haven't looked at that, but maybe the problem will be obvious to > Andy.... It is obvious: firstly acl_scratch is only needed (and allocated) if the npages > 1. Secondly, the call to xdr_set_scratch_buffer() needs to be done in the decoder, not the encoder... The following patch should fix it. 8<----------------------------------------------------------------------------------- >From 074d857ded7df109ba696859964c8d5852175dad Mon Sep 17 00:00:00 2001 From: Trond Myklebust <Trond.Myklebust@xxxxxxxxxx> 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@xxxxxxxxxx> Cc: Andy Adamson <andros@xxxxxxxxxx> Cc: stable@xxxxxxxxxxxxxxx --- fs/nfs/nfs4proc.c | 8 ++++---- fs/nfs/nfs4xdr.c | 3 ++- include/linux/nfs_xdr.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index f0c849c..d202e04 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c @@ -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..c047bec 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -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); } @@ -6041,6 +6040,8 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr, status = decode_putfh(xdr); if (status) goto out; + if (res->acl_scratch != NULL) + xdr_set_scratch_buffer(xdr, page_address(res->acl_scratch), PAGE_SIZE); status = decode_getacl(xdr, rqstp, res); out: diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index a764cef..d6ba9a1 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h @@ -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.7.6 -- Trond Myklebust Linux NFS client maintainer NetApp Trond.Myklebust@xxxxxxxxxx www.netapp.com ��.n��������+%������w��{.n�����{��w���jg��������ݢj����G�������j:+v���w�m������w�������h�����٥