| From 2bc3c1179c781b359d4f2f3439cb3df72afc17fc Mon Sep 17 00:00:00 2001 |
| From: Neil Brown <neilb@suse.de> |
| Date: Tue, 20 Apr 2010 12:16:52 +1000 |
| Subject: nfsd4: bug in read_buf |
| |
| From: Neil Brown <neilb@suse.de> |
| |
| commit 2bc3c1179c781b359d4f2f3439cb3df72afc17fc upstream. |
| |
| When read_buf is called to move over to the next page in the pagelist |
| of an NFSv4 request, it sets argp->end to essentially a random |
| number, certainly not an address within the page which argp->p now |
| points to. So subsequent calls to READ_BUF will think there is much |
| more than a page of spare space (the cast to u32 ensures an unsigned |
| comparison) so we can expect to fall off the end of the second |
| page. |
| |
| We never encountered thsi in testing because typically the only |
| operations which use more than two pages are write-like operations, |
| which have their own decoding logic. Something like a getattr after a |
| write may cross a page boundary, but it would be very unusual for it to |
| cross another boundary after that. |
| |
| Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| |
| --- |
| fs/nfsd/nfs4xdr.c | 8 ++++---- |
| 1 file changed, 4 insertions(+), 4 deletions(-) |
| |
| --- a/fs/nfsd/nfs4xdr.c |
| +++ b/fs/nfsd/nfs4xdr.c |
| @@ -179,10 +179,10 @@ static __be32 *read_buf(struct nfsd4_com |
| argp->p = page_address(argp->pagelist[0]); |
| argp->pagelist++; |
| if (argp->pagelen < PAGE_SIZE) { |
| - argp->end = p + (argp->pagelen>>2); |
| + argp->end = argp->p + (argp->pagelen>>2); |
| argp->pagelen = 0; |
| } else { |
| - argp->end = p + (PAGE_SIZE>>2); |
| + argp->end = argp->p + (PAGE_SIZE>>2); |
| argp->pagelen -= PAGE_SIZE; |
| } |
| memcpy(((char*)p)+avail, argp->p, (nbytes - avail)); |
| @@ -1115,10 +1115,10 @@ nfsd4_decode_compound(struct nfsd4_compo |
| argp->p = page_address(argp->pagelist[0]); |
| argp->pagelist++; |
| if (argp->pagelen < PAGE_SIZE) { |
| - argp->end = p + (argp->pagelen>>2); |
| + argp->end = argp->p + (argp->pagelen>>2); |
| argp->pagelen = 0; |
| } else { |
| - argp->end = p + (PAGE_SIZE>>2); |
| + argp->end = argp->p + (PAGE_SIZE>>2); |
| argp->pagelen -= PAGE_SIZE; |
| } |
| } |