summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKirill Tkhai <ktkhai@virtuozzo.com>2018-11-08 12:05:42 +0300
committerMiklos Szeredi <mszeredi@redhat.com>2019-02-13 13:15:13 +0100
commitb782911b5297c4be0c2de88fc3b36a760eca6321 (patch)
treeb74ae0e6193a1f48779a6b9fa0d0cdf794633322
parent7407a10de57f5810f3f0e778eb1db0923d24ab16 (diff)
fuse: Verify userspace asks to requeue interrupt that we really sent
When queue_interrupt() is called from fuse_dev_do_write(), it came from userspace directly. Userspace may pass any request id, even the request's we have not interrupted (or even background's request). This patch adds sanity check to make kernel safe against that. Signed-off-by: Kirill Tkhai <ktkhai@virtuozzo.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
-rw-r--r--fs/fuse/dev.c13
1 files changed, 10 insertions, 3 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c
index 682c7914a0a0..ed9318d4e7dc 100644
--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -479,9 +479,15 @@ put_request:
fuse_put_request(fc, req);
}
-static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
+static int queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
{
spin_lock(&fiq->waitq.lock);
+ /* Check for we've sent request to interrupt this req */
+ if (unlikely(!test_bit(FR_INTERRUPTED, &req->flags))) {
+ spin_unlock(&fiq->waitq.lock);
+ return -EINVAL;
+ }
+
if (list_empty(&req->intr_entry)) {
list_add_tail(&req->intr_entry, &fiq->interrupts);
/*
@@ -492,12 +498,13 @@ static void queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req)
if (test_bit(FR_FINISHED, &req->flags)) {
list_del_init(&req->intr_entry);
spin_unlock(&fiq->waitq.lock);
- return;
+ return 0;
}
wake_up_locked(&fiq->waitq);
kill_fasync(&fiq->fasync, SIGIO, POLL_IN);
}
spin_unlock(&fiq->waitq.lock);
+ return 0;
}
static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
@@ -1962,7 +1969,7 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud,
else if (oh.error == -ENOSYS)
fc->no_interrupt = 1;
else if (oh.error == -EAGAIN)
- queue_interrupt(&fc->iq, req);
+ err = queue_interrupt(&fc->iq, req);
fuse_put_request(fc, req);