aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2007-01-26 03:56:59 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-01-26 16:50:59 -0500
commit250f3915183d377d36e012bac9caa7345ce465b8 (patch)
treee7fd20dfa79a8b4f6435083bf48f4cd3d1836cbf
parent1a8eff6d977c28162c61c9532ca58634e7090b69 (diff)
[PATCH] knfsd: fix an NFSD bug with full sized, non-page-aligned reads
NFSd assumes that largest number of pages that will be needed for a request+response is 2+N where N pages is the size of the largest permitted read/write request. The '2' are 1 for the non-data part of the request, and 1 for the non-data part of the reply. However, when a read request is not page-aligned, and we choose to use ->sendfile to send it directly from the page cache, we may need N+1 pages to hold the whole reply. This can overflow and array and cause an Oops. This patch increases size of the array for holding pages by one and makes sure that entry is NULL when it is not in use. Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/nfsd/vfs.c3
-rw-r--r--include/linux/sunrpc/svc.h5
-rw-r--r--net/sunrpc/svcsock.c2
3 files changed, 8 insertions, 2 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 7a79c23aa6d4..ea855629a1c2 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -822,7 +822,8 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset
822 rqstp->rq_res.page_len = size; 822 rqstp->rq_res.page_len = size;
823 } else if (page != pp[-1]) { 823 } else if (page != pp[-1]) {
824 get_page(page); 824 get_page(page);
825 put_page(*pp); 825 if (*pp)
826 put_page(*pp);
826 *pp = page; 827 *pp = page;
827 rqstp->rq_resused++; 828 rqstp->rq_resused++;
828 rqstp->rq_res.page_len += size; 829 rqstp->rq_res.page_len += size;
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h
index 965d6c20086e..64f3d60c72af 100644
--- a/include/linux/sunrpc/svc.h
+++ b/include/linux/sunrpc/svc.h
@@ -144,8 +144,11 @@ extern u32 svc_max_payload(const struct svc_rqst *rqstp);
144 * 144 *
145 * Each request/reply pair can have at most one "payload", plus two pages, 145 * Each request/reply pair can have at most one "payload", plus two pages,
146 * one for the request, and one for the reply. 146 * one for the request, and one for the reply.
147 * We using ->sendfile to return read data, we might need one extra page
148 * if the request is not page-aligned. So add another '1'.
147 */ 149 */
148#define RPCSVC_MAXPAGES ((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE + 2) 150#define RPCSVC_MAXPAGES ((RPCSVC_MAXPAYLOAD+PAGE_SIZE-1)/PAGE_SIZE \
151 + 2 + 1)
149 152
150static inline u32 svc_getnl(struct kvec *iov) 153static inline u32 svc_getnl(struct kvec *iov)
151{ 154{
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c
index 99f54fb6d669..45120f268d03 100644
--- a/net/sunrpc/svcsock.c
+++ b/net/sunrpc/svcsock.c
@@ -1278,6 +1278,8 @@ svc_recv(struct svc_rqst *rqstp, long timeout)
1278 schedule_timeout_uninterruptible(msecs_to_jiffies(500)); 1278 schedule_timeout_uninterruptible(msecs_to_jiffies(500));
1279 rqstp->rq_pages[i] = p; 1279 rqstp->rq_pages[i] = p;
1280 } 1280 }
1281 rqstp->rq_pages[i++] = NULL; /* this might be seen in nfs_read_actor */
1282 BUG_ON(pages >= RPCSVC_MAXPAGES);
1281 1283
1282 /* Make arg->head point to first page and arg->pages point to rest */ 1284 /* Make arg->head point to first page and arg->pages point to rest */
1283 arg = &rqstp->rq_arg; 1285 arg = &rqstp->rq_arg;