aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/write.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/write.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/write.c')
-rw-r--r--fs/nfs/write.c37
1 files changed, 15 insertions, 22 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 50774991f8d5..8ab3cf10d792 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -90,22 +90,13 @@ static mempool_t *nfs_commit_mempool;
90 90
91static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion); 91static DECLARE_WAIT_QUEUE_HEAD(nfs_write_congestion);
92 92
93struct nfs_write_data *nfs_commit_alloc(unsigned int pagecount) 93struct nfs_write_data *nfs_commit_alloc(void)
94{ 94{
95 struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS); 95 struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, SLAB_NOFS);
96 96
97 if (p) { 97 if (p) {
98 memset(p, 0, sizeof(*p)); 98 memset(p, 0, sizeof(*p));
99 INIT_LIST_HEAD(&p->pages); 99 INIT_LIST_HEAD(&p->pages);
100 if (pagecount <= ARRAY_SIZE(p->page_array))
101 p->pagevec = p->page_array;
102 else {
103 p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS);
104 if (!p->pagevec) {
105 mempool_free(p, nfs_commit_mempool);
106 p = NULL;
107 }
108 }
109 } 100 }
110 return p; 101 return p;
111} 102}
@@ -117,13 +108,15 @@ void nfs_commit_free(struct nfs_write_data *p)
117 mempool_free(p, nfs_commit_mempool); 108 mempool_free(p, nfs_commit_mempool);
118} 109}
119 110
120struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) 111struct nfs_write_data *nfs_writedata_alloc(size_t len)
121{ 112{
113 unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
122 struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS); 114 struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, SLAB_NOFS);
123 115
124 if (p) { 116 if (p) {
125 memset(p, 0, sizeof(*p)); 117 memset(p, 0, sizeof(*p));
126 INIT_LIST_HEAD(&p->pages); 118 INIT_LIST_HEAD(&p->pages);
119 p->npages = pagecount;
127 if (pagecount <= ARRAY_SIZE(p->page_array)) 120 if (pagecount <= ARRAY_SIZE(p->page_array))
128 p->pagevec = p->page_array; 121 p->pagevec = p->page_array;
129 else { 122 else {
@@ -208,7 +201,7 @@ static int nfs_writepage_sync(struct nfs_open_context *ctx, struct inode *inode,
208 int result, written = 0; 201 int result, written = 0;
209 struct nfs_write_data *wdata; 202 struct nfs_write_data *wdata;
210 203
211 wdata = nfs_writedata_alloc(1); 204 wdata = nfs_writedata_alloc(wsize);
212 if (!wdata) 205 if (!wdata)
213 return -ENOMEM; 206 return -ENOMEM;
214 207
@@ -999,24 +992,24 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, int how)
999 struct nfs_page *req = nfs_list_entry(head->next); 992 struct nfs_page *req = nfs_list_entry(head->next);
1000 struct page *page = req->wb_page; 993 struct page *page = req->wb_page;
1001 struct nfs_write_data *data; 994 struct nfs_write_data *data;
1002 unsigned int wsize = NFS_SERVER(inode)->wsize; 995 size_t wsize = NFS_SERVER(inode)->wsize, nbytes;
1003 unsigned int nbytes, offset; 996 unsigned int offset;
1004 int requests = 0; 997 int requests = 0;
1005 LIST_HEAD(list); 998 LIST_HEAD(list);
1006 999
1007 nfs_list_remove_request(req); 1000 nfs_list_remove_request(req);
1008 1001
1009 nbytes = req->wb_bytes; 1002 nbytes = req->wb_bytes;
1010 for (;;) { 1003 do {
1011 data = nfs_writedata_alloc(1); 1004 size_t len = min(nbytes, wsize);
1005
1006 data = nfs_writedata_alloc(len);
1012 if (!data) 1007 if (!data)
1013 goto out_bad; 1008 goto out_bad;
1014 list_add(&data->pages, &list); 1009 list_add(&data->pages, &list);
1015 requests++; 1010 requests++;
1016 if (nbytes <= wsize) 1011 nbytes -= len;
1017 break; 1012 } while (nbytes != 0);
1018 nbytes -= wsize;
1019 }
1020 atomic_set(&req->wb_complete, requests); 1013 atomic_set(&req->wb_complete, requests);
1021 1014
1022 ClearPageError(page); 1015 ClearPageError(page);
@@ -1070,7 +1063,7 @@ static int nfs_flush_one(struct inode *inode, struct list_head *head, int how)
1070 struct nfs_write_data *data; 1063 struct nfs_write_data *data;
1071 unsigned int count; 1064 unsigned int count;
1072 1065
1073 data = nfs_writedata_alloc(NFS_SERVER(inode)->wpages); 1066 data = nfs_writedata_alloc(NFS_SERVER(inode)->wsize);
1074 if (!data) 1067 if (!data)
1075 goto out_bad; 1068 goto out_bad;
1076 1069
@@ -1378,7 +1371,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
1378 struct nfs_write_data *data; 1371 struct nfs_write_data *data;
1379 struct nfs_page *req; 1372 struct nfs_page *req;
1380 1373
1381 data = nfs_commit_alloc(NFS_SERVER(inode)->wpages); 1374 data = nfs_commit_alloc();
1382 1375
1383 if (!data) 1376 if (!data)
1384 goto out_bad; 1377 goto out_bad;