aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-04-11 01:54:59 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-11 09:18:49 -0400
commit08a53cdce62d37d918530bbbf726cc01b21dc3d1 (patch)
tree2db5e37737da91f1b2b32136e4e10ad540d8dc09
parentce1d5a491f0ee50560416a73faa5e4ddbab074bd (diff)
[PATCH] fuse: account background requests
The previous patch removed limiting the number of outstanding requests. This patch adds a much simpler limiting, that is also compatible with file locking operations. A task may have at most one synchronous request allocated. So these requests need not be otherwise limited. However the number of background requests (release, forget, asynchronous reads, interrupted requests) can grow indefinitely. This can be used by a malicous user to cause FUSE to allocate arbitrary amounts of unswappable kernel memory, denying service. For this reason add a limit for the number of background requests, and block allocations of new requests until the number goes bellow the limit. Also use this mechanism to block all requests until the INIT reply is received. Signed-off-by: Miklos Szeredi <miklos@szeredi.hu> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/fuse/dev.c24
-rw-r--r--fs/fuse/fuse_i.h14
-rw-r--r--fs/fuse/inode.c4
3 files changed, 38 insertions, 4 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 4dc104c0e95d..6c740f860665 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -90,7 +90,17 @@ static void __fuse_put_request(struct fuse_req *req)
90 90
91struct fuse_req *fuse_get_req(struct fuse_conn *fc) 91struct fuse_req *fuse_get_req(struct fuse_conn *fc)
92{ 92{
93 struct fuse_req *req = fuse_request_alloc(); 93 struct fuse_req *req;
94 sigset_t oldset;
95 int err;
96
97 block_sigs(&oldset);
98 err = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
99 restore_sigs(&oldset);
100 if (err)
101 return ERR_PTR(-EINTR);
102
103 req = fuse_request_alloc();
94 if (!req) 104 if (!req)
95 return ERR_PTR(-ENOMEM); 105 return ERR_PTR(-ENOMEM);
96 106
@@ -118,6 +128,11 @@ void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
118 fput(req->file); 128 fput(req->file);
119 spin_lock(&fc->lock); 129 spin_lock(&fc->lock);
120 list_del(&req->bg_entry); 130 list_del(&req->bg_entry);
131 if (fc->num_background == FUSE_MAX_BACKGROUND) {
132 fc->blocked = 0;
133 wake_up_all(&fc->blocked_waitq);
134 }
135 fc->num_background--;
121 spin_unlock(&fc->lock); 136 spin_unlock(&fc->lock);
122} 137}
123 138
@@ -195,6 +210,9 @@ static void background_request(struct fuse_conn *fc, struct fuse_req *req)
195{ 210{
196 req->background = 1; 211 req->background = 1;
197 list_add(&req->bg_entry, &fc->background); 212 list_add(&req->bg_entry, &fc->background);
213 fc->num_background++;
214 if (fc->num_background == FUSE_MAX_BACKGROUND)
215 fc->blocked = 1;
198 if (req->inode) 216 if (req->inode)
199 req->inode = igrab(req->inode); 217 req->inode = igrab(req->inode);
200 if (req->inode2) 218 if (req->inode2)
@@ -288,6 +306,7 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req)
288static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req) 306static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
289{ 307{
290 spin_lock(&fc->lock); 308 spin_lock(&fc->lock);
309 background_request(fc, req);
291 if (fc->connected) { 310 if (fc->connected) {
292 queue_request(fc, req); 311 queue_request(fc, req);
293 spin_unlock(&fc->lock); 312 spin_unlock(&fc->lock);
@@ -306,9 +325,6 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
306void request_send_background(struct fuse_conn *fc, struct fuse_req *req) 325void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
307{ 326{
308 req->isreply = 1; 327 req->isreply = 1;
309 spin_lock(&fc->lock);
310 background_request(fc, req);
311 spin_unlock(&fc->lock);
312 request_send_nowait(fc, req); 328 request_send_nowait(fc, req);
313} 329}
314 330
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 242e69cb1251..19c7185a7546 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -18,6 +18,9 @@
18/** Max number of pages that can be used in a single read request */ 18/** Max number of pages that can be used in a single read request */
19#define FUSE_MAX_PAGES_PER_REQ 32 19#define FUSE_MAX_PAGES_PER_REQ 32
20 20
21/** Maximum number of outstanding background requests */
22#define FUSE_MAX_BACKGROUND 10
23
21/** It could be as large as PATH_MAX, but would that have any uses? */ 24/** It could be as large as PATH_MAX, but would that have any uses? */
22#define FUSE_NAME_MAX 1024 25#define FUSE_NAME_MAX 1024
23 26
@@ -241,6 +244,17 @@ struct fuse_conn {
241 interrupted request) */ 244 interrupted request) */
242 struct list_head background; 245 struct list_head background;
243 246
247 /** Number of requests currently in the background */
248 unsigned num_background;
249
250 /** Flag indicating if connection is blocked. This will be
251 the case before the INIT reply is received, and if there
252 are too many outstading backgrounds requests */
253 int blocked;
254
255 /** waitq for blocked connection */
256 wait_queue_head_t blocked_waitq;
257
244 /** RW semaphore for exclusion with fuse_put_super() */ 258 /** RW semaphore for exclusion with fuse_put_super() */
245 struct rw_semaphore sbput_sem; 259 struct rw_semaphore sbput_sem;
246 260
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 824ebbc428ed..fd34037b0588 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -381,6 +381,7 @@ static struct fuse_conn *new_conn(void)
381 if (fc) { 381 if (fc) {
382 spin_lock_init(&fc->lock); 382 spin_lock_init(&fc->lock);
383 init_waitqueue_head(&fc->waitq); 383 init_waitqueue_head(&fc->waitq);
384 init_waitqueue_head(&fc->blocked_waitq);
384 INIT_LIST_HEAD(&fc->pending); 385 INIT_LIST_HEAD(&fc->pending);
385 INIT_LIST_HEAD(&fc->processing); 386 INIT_LIST_HEAD(&fc->processing);
386 INIT_LIST_HEAD(&fc->io); 387 INIT_LIST_HEAD(&fc->io);
@@ -392,6 +393,7 @@ static struct fuse_conn *new_conn(void)
392 fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE; 393 fc->bdi.ra_pages = (VM_MAX_READAHEAD * 1024) / PAGE_CACHE_SIZE;
393 fc->bdi.unplug_io_fn = default_unplug_io_fn; 394 fc->bdi.unplug_io_fn = default_unplug_io_fn;
394 fc->reqctr = 0; 395 fc->reqctr = 0;
396 fc->blocked = 1;
395 } 397 }
396 return fc; 398 return fc;
397} 399}
@@ -438,6 +440,8 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
438 fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; 440 fc->max_write = arg->minor < 5 ? 4096 : arg->max_write;
439 } 441 }
440 fuse_put_request(fc, req); 442 fuse_put_request(fc, req);
443 fc->blocked = 0;
444 wake_up_all(&fc->blocked_waitq);
441} 445}
442 446
443static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req) 447static void fuse_send_init(struct fuse_conn *fc, struct fuse_req *req)