aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/read.c
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2006-09-08 12:48:54 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-08 13:22:51 -0400
commite9f7bee1df223dcf83743b46cb06c08d95497ec0 (patch)
tree67beae4733ef0286645112a52623c81c8f8a19a9 /fs/nfs/read.c
parent016eb4a0ed06a3677d67a584da901f0e9a63c666 (diff)
[PATCH] NFS: large non-page-aligned direct I/O clobbers memory
The logic in nfs_direct_read_schedule and nfs_direct_write_schedule can allow data->npages to be one larger than rpages. This causes a page pointer to be written beyond the end of the pagevec in nfs_read_data (or nfs_write_data). Fix this by making nfs_(read|write)_alloc() calculate the size of the pagevec array, and initialise data->npages. Also get rid of the redundant argument to nfs_commit_alloc(). Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Cc: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'fs/nfs/read.c')
-rw-r--r--fs/nfs/read.c24
1 files changed, 13 insertions, 11 deletions
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index da9cf11c326f..7a9ee00e0c61 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -43,13 +43,15 @@ static mempool_t *nfs_rdata_mempool;
43 43
44#define MIN_POOL_READ (32) 44#define MIN_POOL_READ (32)
45 45
46struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount) 46struct nfs_read_data *nfs_readdata_alloc(size_t len)
47{ 47{
48 unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
48 struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS); 49 struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, SLAB_NOFS);
49 50
50 if (p) { 51 if (p) {
51 memset(p, 0, sizeof(*p)); 52 memset(p, 0, sizeof(*p));
52 INIT_LIST_HEAD(&p->pages); 53 INIT_LIST_HEAD(&p->pages);
54 p->npages = pagecount;
53 if (pagecount <= ARRAY_SIZE(p->page_array)) 55 if (pagecount <= ARRAY_SIZE(p->page_array))
54 p->pagevec = p->page_array; 56 p->pagevec = p->page_array;
55 else { 57 else {
@@ -140,7 +142,7 @@ static int nfs_readpage_sync(struct nfs_open_context *ctx, struct inode *inode,
140 int result; 142 int result;
141 struct nfs_read_data *rdata; 143 struct nfs_read_data *rdata;
142 144
143 rdata = nfs_readdata_alloc(1); 145 rdata = nfs_readdata_alloc(count);
144 if (!rdata) 146 if (!rdata)
145 return -ENOMEM; 147 return -ENOMEM;
146 148
@@ -336,25 +338,25 @@ static int nfs_pagein_multi(struct list_head *head, struct inode *inode)
336 struct nfs_page *req = nfs_list_entry(head->next); 338 struct nfs_page *req = nfs_list_entry(head->next);
337 struct page *page = req->wb_page; 339 struct page *page = req->wb_page;
338 struct nfs_read_data *data; 340 struct nfs_read_data *data;
339 unsigned int rsize = NFS_SERVER(inode)->rsize; 341 size_t rsize = NFS_SERVER(inode)->rsize, nbytes;
340 unsigned int nbytes, offset; 342 unsigned int offset;
341 int requests = 0; 343 int requests = 0;
342 LIST_HEAD(list); 344 LIST_HEAD(list);
343 345
344 nfs_list_remove_request(req); 346 nfs_list_remove_request(req);
345 347
346 nbytes = req->wb_bytes; 348 nbytes = req->wb_bytes;
347 for(;;) { 349 do {
348 data = nfs_readdata_alloc(1); 350 size_t len = min(nbytes,rsize);
351
352 data = nfs_readdata_alloc(len);
349 if (!data) 353 if (!data)
350 goto out_bad; 354 goto out_bad;
351 INIT_LIST_HEAD(&data->pages); 355 INIT_LIST_HEAD(&data->pages);
352 list_add(&data->pages, &list); 356 list_add(&data->pages, &list);
353 requests++; 357 requests++;
354 if (nbytes <= rsize) 358 nbytes -= len;
355 break; 359 } while(nbytes != 0);
356 nbytes -= rsize;
357 }
358 atomic_set(&req->wb_complete, requests); 360 atomic_set(&req->wb_complete, requests);
359 361
360 ClearPageError(page); 362 ClearPageError(page);
@@ -402,7 +404,7 @@ static int nfs_pagein_one(struct list_head *head, struct inode *inode)
402 if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) 404 if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
403 return nfs_pagein_multi(head, inode); 405 return nfs_pagein_multi(head, inode);
404 406
405 data = nfs_readdata_alloc(NFS_SERVER(inode)->rpages); 407 data = nfs_readdata_alloc(NFS_SERVER(inode)->rsize);
406 if (!data) 408 if (!data)
407 goto out_bad; 409 goto out_bad;
408 410