summaryrefslogtreecommitdiffstats
path: root/fs/fuse/dev.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2006-04-10 22:54:59 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-04-11 06:18:49 -0700
commit08a53cdce62d37d918530bbbf726cc01b21dc3d1 (patch)
tree2db5e37737da91f1b2b32136e4e10ad540d8dc09 /fs/fuse/dev.c
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>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r--fs/fuse/dev.c24
1 files changed, 20 insertions, 4 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 4dc104c0e95..6c740f86066 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -90,7 +90,17 @@ static void __fuse_put_request(struct fuse_req *req)
struct fuse_req *fuse_get_req(struct fuse_conn *fc)
{
- struct fuse_req *req = fuse_request_alloc();
+ struct fuse_req *req;
+ sigset_t oldset;
+ int err;
+
+ block_sigs(&oldset);
+ err = wait_event_interruptible(fc->blocked_waitq, !fc->blocked);
+ restore_sigs(&oldset);
+ if (err)
+ return ERR_PTR(-EINTR);
+
+ req = fuse_request_alloc();
if (!req)
return ERR_PTR(-ENOMEM);
@@ -118,6 +128,11 @@ void fuse_release_background(struct fuse_conn *fc, struct fuse_req *req)
fput(req->file);
spin_lock(&fc->lock);
list_del(&req->bg_entry);
+ if (fc->num_background == FUSE_MAX_BACKGROUND) {
+ fc->blocked = 0;
+ wake_up_all(&fc->blocked_waitq);
+ }
+ fc->num_background--;
spin_unlock(&fc->lock);
}
@@ -195,6 +210,9 @@ static void background_request(struct fuse_conn *fc, struct fuse_req *req)
{
req->background = 1;
list_add(&req->bg_entry, &fc->background);
+ fc->num_background++;
+ if (fc->num_background == FUSE_MAX_BACKGROUND)
+ fc->blocked = 1;
if (req->inode)
req->inode = igrab(req->inode);
if (req->inode2)
@@ -288,6 +306,7 @@ void request_send(struct fuse_conn *fc, struct fuse_req *req)
static void request_send_nowait(struct fuse_conn *fc, struct fuse_req *req)
{
spin_lock(&fc->lock);
+ background_request(fc, req);
if (fc->connected) {
queue_request(fc, req);
spin_unlock(&fc->lock);
@@ -306,9 +325,6 @@ void request_send_noreply(struct fuse_conn *fc, struct fuse_req *req)
void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
{
req->isreply = 1;
- spin_lock(&fc->lock);
- background_request(fc, req);
- spin_unlock(&fc->lock);
request_send_nowait(fc, req);
}