aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2007-04-10 09:26:35 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2007-05-01 01:17:07 -0400
commit8d5658c949e6d89edc579a1f112aeee3bc232a8e (patch)
treef206d3f6809eeb0ca23c1999cf79aa294968b113
parentc63c7b051395368573779c8309aa5c990dcf2f96 (diff)
NFS: Fix a buffer overflow in the allocation of struct nfs_read/writedata
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/direct.c5
-rw-r--r--fs/nfs/internal.h12
-rw-r--r--fs/nfs/pagelist.c10
-rw-r--r--fs/nfs/read.c19
-rw-r--r--fs/nfs/write.c11
-rw-r--r--include/linux/nfs_fs.h4
-rw-r--r--include/linux/nfs_page.h4
7 files changed, 41 insertions, 24 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 2877744cb606..889de60f8a84 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -54,6 +54,7 @@
54#include <asm/uaccess.h> 54#include <asm/uaccess.h>
55#include <asm/atomic.h> 55#include <asm/atomic.h>
56 56
57#include "internal.h"
57#include "iostat.h" 58#include "iostat.h"
58 59
59#define NFSDBG_FACILITY NFSDBG_VFS 60#define NFSDBG_FACILITY NFSDBG_VFS
@@ -271,7 +272,7 @@ static ssize_t nfs_direct_read_schedule(struct nfs_direct_req *dreq, unsigned lo
271 bytes = min(rsize,count); 272 bytes = min(rsize,count);
272 273
273 result = -ENOMEM; 274 result = -ENOMEM;
274 data = nfs_readdata_alloc(pgbase + bytes); 275 data = nfs_readdata_alloc(nfs_page_array_len(pgbase, bytes));
275 if (unlikely(!data)) 276 if (unlikely(!data))
276 break; 277 break;
277 278
@@ -602,7 +603,7 @@ static ssize_t nfs_direct_write_schedule(struct nfs_direct_req *dreq, unsigned l
602 bytes = min(wsize,count); 603 bytes = min(wsize,count);
603 604
604 result = -ENOMEM; 605 result = -ENOMEM;
605 data = nfs_writedata_alloc(pgbase + bytes); 606 data = nfs_writedata_alloc(nfs_page_array_len(pgbase, bytes));
606 if (unlikely(!data)) 607 if (unlikely(!data))
607 break; 608 break;
608 609
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 6610f2b02077..ad2b40db1e65 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -231,3 +231,15 @@ unsigned int nfs_page_length(struct page *page)
231 } 231 }
232 return 0; 232 return 0;
233} 233}
234
235/*
236 * Determine the number of pages in an array of length 'len' and
237 * with a base offset of 'base'
238 */
239static inline
240unsigned int nfs_page_array_len(unsigned int base, size_t len)
241{
242 return ((unsigned long)len + (unsigned long)base +
243 PAGE_SIZE - 1) >> PAGE_SHIFT;
244}
245
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index ea1a85df9ab1..096efd73eb4c 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -18,6 +18,8 @@
18#include <linux/nfs_fs.h> 18#include <linux/nfs_fs.h>
19#include <linux/nfs_mount.h> 19#include <linux/nfs_mount.h>
20 20
21#include "internal.h"
22
21#define NFS_PARANOIA 1 23#define NFS_PARANOIA 1
22 24
23static struct kmem_cache *nfs_page_cachep; 25static struct kmem_cache *nfs_page_cachep;
@@ -231,7 +233,7 @@ out:
231 */ 233 */
232void nfs_pageio_init(struct nfs_pageio_descriptor *desc, 234void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
233 struct inode *inode, 235 struct inode *inode,
234 int (*doio)(struct inode *, struct list_head *, size_t, int), 236 int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
235 unsigned int bsize, 237 unsigned int bsize,
236 int io_flags) 238 int io_flags)
237{ 239{
@@ -298,8 +300,10 @@ static int nfs_pageio_do_add_request(struct nfs_pageio_descriptor *desc,
298 * since nfs_flush_multi and nfs_pagein_multi assume you 300 * since nfs_flush_multi and nfs_pagein_multi assume you
299 * can have only one struct nfs_page. 301 * can have only one struct nfs_page.
300 */ 302 */
303 if (desc->pg_bsize < PAGE_SIZE)
304 return 0;
301 newlen += desc->pg_count; 305 newlen += desc->pg_count;
302 if (desc->pg_base + newlen > desc->pg_bsize) 306 if (newlen > desc->pg_bsize)
303 return 0; 307 return 0;
304 prev = nfs_list_entry(desc->pg_list.prev); 308 prev = nfs_list_entry(desc->pg_list.prev);
305 if (!nfs_can_coalesce_requests(prev, req)) 309 if (!nfs_can_coalesce_requests(prev, req))
@@ -320,6 +324,8 @@ static void nfs_pageio_doio(struct nfs_pageio_descriptor *desc)
320 if (!list_empty(&desc->pg_list)) { 324 if (!list_empty(&desc->pg_list)) {
321 int error = desc->pg_doio(desc->pg_inode, 325 int error = desc->pg_doio(desc->pg_inode,
322 &desc->pg_list, 326 &desc->pg_list,
327 nfs_page_array_len(desc->pg_base,
328 desc->pg_count),
323 desc->pg_count, 329 desc->pg_count,
324 desc->pg_ioflags); 330 desc->pg_ioflags);
325 if (error < 0) 331 if (error < 0)
diff --git a/fs/nfs/read.c b/fs/nfs/read.c
index f0016062340d..9a55807b2a70 100644
--- a/fs/nfs/read.c
+++ b/fs/nfs/read.c
@@ -27,8 +27,8 @@
27 27
28#define NFSDBG_FACILITY NFSDBG_PAGECACHE 28#define NFSDBG_FACILITY NFSDBG_PAGECACHE
29 29
30static int nfs_pagein_multi(struct inode *, struct list_head *, size_t, int); 30static int nfs_pagein_multi(struct inode *, struct list_head *, unsigned int, size_t, int);
31static int nfs_pagein_one(struct inode *, struct list_head *, size_t, int); 31static int nfs_pagein_one(struct inode *, struct list_head *, unsigned int, size_t, int);
32static const struct rpc_call_ops nfs_read_partial_ops; 32static const struct rpc_call_ops nfs_read_partial_ops;
33static const struct rpc_call_ops nfs_read_full_ops; 33static const struct rpc_call_ops nfs_read_full_ops;
34 34
@@ -37,9 +37,8 @@ static mempool_t *nfs_rdata_mempool;
37 37
38#define MIN_POOL_READ (32) 38#define MIN_POOL_READ (32)
39 39
40struct nfs_read_data *nfs_readdata_alloc(size_t len) 40struct nfs_read_data *nfs_readdata_alloc(unsigned int pagecount)
41{ 41{
42 unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
43 struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS); 42 struct nfs_read_data *p = mempool_alloc(nfs_rdata_mempool, GFP_NOFS);
44 43
45 if (p) { 44 if (p) {
@@ -135,9 +134,9 @@ static int nfs_readpage_async(struct nfs_open_context *ctx, struct inode *inode,
135 134
136 nfs_list_add_request(new, &one_request); 135 nfs_list_add_request(new, &one_request);
137 if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE) 136 if (NFS_SERVER(inode)->rsize < PAGE_CACHE_SIZE)
138 nfs_pagein_multi(inode, &one_request, len, 0); 137 nfs_pagein_multi(inode, &one_request, 1, len, 0);
139 else 138 else
140 nfs_pagein_one(inode, &one_request, len, 0); 139 nfs_pagein_one(inode, &one_request, 1, len, 0);
141 return 0; 140 return 0;
142} 141}
143 142
@@ -234,7 +233,7 @@ static void nfs_execute_read(struct nfs_read_data *data)
234 * won't see the new data until our attribute cache is updated. This is more 233 * won't see the new data until our attribute cache is updated. This is more
235 * or less conventional NFS client behavior. 234 * or less conventional NFS client behavior.
236 */ 235 */
237static int nfs_pagein_multi(struct inode *inode, struct list_head *head, size_t count, int flags) 236static int nfs_pagein_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
238{ 237{
239 struct nfs_page *req = nfs_list_entry(head->next); 238 struct nfs_page *req = nfs_list_entry(head->next);
240 struct page *page = req->wb_page; 239 struct page *page = req->wb_page;
@@ -250,7 +249,7 @@ static int nfs_pagein_multi(struct inode *inode, struct list_head *head, size_t
250 do { 249 do {
251 size_t len = min(nbytes,rsize); 250 size_t len = min(nbytes,rsize);
252 251
253 data = nfs_readdata_alloc(len); 252 data = nfs_readdata_alloc(1);
254 if (!data) 253 if (!data)
255 goto out_bad; 254 goto out_bad;
256 INIT_LIST_HEAD(&data->pages); 255 INIT_LIST_HEAD(&data->pages);
@@ -291,13 +290,13 @@ out_bad:
291 return -ENOMEM; 290 return -ENOMEM;
292} 291}
293 292
294static int nfs_pagein_one(struct inode *inode, struct list_head *head, size_t count, int flags) 293static int nfs_pagein_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int flags)
295{ 294{
296 struct nfs_page *req; 295 struct nfs_page *req;
297 struct page **pages; 296 struct page **pages;
298 struct nfs_read_data *data; 297 struct nfs_read_data *data;
299 298
300 data = nfs_readdata_alloc(count); 299 data = nfs_readdata_alloc(npages);
301 if (!data) 300 if (!data)
302 goto out_bad; 301 goto out_bad;
303 302
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 6ce2d94e7b3f..0a8bbc399689 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -72,9 +72,8 @@ void nfs_commit_free(struct nfs_write_data *wdata)
72 call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free); 72 call_rcu_bh(&wdata->task.u.tk_rcu, nfs_commit_rcu_free);
73} 73}
74 74
75struct nfs_write_data *nfs_writedata_alloc(size_t len) 75struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
76{ 76{
77 unsigned int pagecount = (len + PAGE_SIZE - 1) >> PAGE_SHIFT;
78 struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); 77 struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS);
79 78
80 if (p) { 79 if (p) {
@@ -832,7 +831,7 @@ static void nfs_execute_write(struct nfs_write_data *data)
832 * Generate multiple small requests to write out a single 831 * Generate multiple small requests to write out a single
833 * contiguous dirty area on one page. 832 * contiguous dirty area on one page.
834 */ 833 */
835static int nfs_flush_multi(struct inode *inode, struct list_head *head, size_t count, int how) 834static int nfs_flush_multi(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
836{ 835{
837 struct nfs_page *req = nfs_list_entry(head->next); 836 struct nfs_page *req = nfs_list_entry(head->next);
838 struct page *page = req->wb_page; 837 struct page *page = req->wb_page;
@@ -848,7 +847,7 @@ static int nfs_flush_multi(struct inode *inode, struct list_head *head, size_t c
848 do { 847 do {
849 size_t len = min(nbytes, wsize); 848 size_t len = min(nbytes, wsize);
850 849
851 data = nfs_writedata_alloc(len); 850 data = nfs_writedata_alloc(1);
852 if (!data) 851 if (!data)
853 goto out_bad; 852 goto out_bad;
854 list_add(&data->pages, &list); 853 list_add(&data->pages, &list);
@@ -897,13 +896,13 @@ out_bad:
897 * This is the case if nfs_updatepage detects a conflicting request 896 * This is the case if nfs_updatepage detects a conflicting request
898 * that has been written but not committed. 897 * that has been written but not committed.
899 */ 898 */
900static int nfs_flush_one(struct inode *inode, struct list_head *head, size_t count, int how) 899static int nfs_flush_one(struct inode *inode, struct list_head *head, unsigned int npages, size_t count, int how)
901{ 900{
902 struct nfs_page *req; 901 struct nfs_page *req;
903 struct page **pages; 902 struct page **pages;
904 struct nfs_write_data *data; 903 struct nfs_write_data *data;
905 904
906 data = nfs_writedata_alloc(count); 905 data = nfs_writedata_alloc(npages);
907 if (!data) 906 if (!data)
908 goto out_bad; 907 goto out_bad;
909 908
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index e9ae0c6e2c62..0543439a97af 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -455,7 +455,7 @@ nfs_have_writebacks(struct inode *inode)
455/* 455/*
456 * Allocate nfs_write_data structures 456 * Allocate nfs_write_data structures
457 */ 457 */
458extern struct nfs_write_data *nfs_writedata_alloc(size_t len); 458extern struct nfs_write_data *nfs_writedata_alloc(unsigned int npages);
459 459
460/* 460/*
461 * linux/fs/nfs/read.c 461 * linux/fs/nfs/read.c
@@ -469,7 +469,7 @@ extern void nfs_readdata_release(void *data);
469/* 469/*
470 * Allocate nfs_read_data structures 470 * Allocate nfs_read_data structures
471 */ 471 */
472extern struct nfs_read_data *nfs_readdata_alloc(size_t len); 472extern struct nfs_read_data *nfs_readdata_alloc(unsigned int npages);
473 473
474/* 474/*
475 * linux/fs/nfs3proc.c 475 * linux/fs/nfs3proc.c
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index e556e57ef7ad..8e9e7bceda48 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -55,7 +55,7 @@ struct nfs_pageio_descriptor {
55 unsigned int pg_base; 55 unsigned int pg_base;
56 56
57 struct inode *pg_inode; 57 struct inode *pg_inode;
58 int (*pg_doio)(struct inode *, struct list_head *, size_t, int); 58 int (*pg_doio)(struct inode *, struct list_head *, unsigned int, size_t, int);
59 int pg_ioflags; 59 int pg_ioflags;
60 int pg_error; 60 int pg_error;
61}; 61};
@@ -75,7 +75,7 @@ extern int nfs_scan_list(struct nfs_inode *nfsi, struct list_head *head, struct
75 unsigned long idx_start, unsigned int npages); 75 unsigned long idx_start, unsigned int npages);
76extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc, 76extern void nfs_pageio_init(struct nfs_pageio_descriptor *desc,
77 struct inode *inode, 77 struct inode *inode,
78 int (*doio)(struct inode *, struct list_head *, size_t, int), 78 int (*doio)(struct inode *, struct list_head *, unsigned int, size_t, int),
79 size_t bsize, 79 size_t bsize,
80 int how); 80 int how);
81extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *, 81extern int nfs_pageio_add_request(struct nfs_pageio_descriptor *,