[PATCH 5.4 080/123] fuse: fix stack use after return

From: Greg Kroah-Hartman
Date: Tue Mar 17 2020 - 07:06:09 EST


From: Miklos Szeredi <mszeredi@xxxxxxxxxx>

commit 3e8cb8b2eaeb22f540f1cbc00cbb594047b7ba89 upstream.

Normal, synchronous requests will have their args allocated on the stack.
After the FR_FINISHED bit is set by receiving the reply from the userspace
fuse server, the originating task may return and reuse the stack frame,
resulting in an Oops if the args structure is dereferenced.

Fix by setting a flag in the request itself upon initializing, indicating
whether it has an asynchronous ->end() callback.

Reported-by: Kyle Sanderson <kyle.leet@xxxxxxxxx>
Reported-by: Michael Stapelberg <michael+lkml@xxxxxxxxxxxxx>
Fixes: 2b319d1f6f92 ("fuse: don't dereference req->args on finished request")
Cc: <stable@xxxxxxxxxxxxxxx> # v5.4
Tested-by: Michael Stapelberg <michael+lkml@xxxxxxxxxxxxx>
Signed-off-by: Miklos Szeredi <mszeredi@xxxxxxxxxx>
Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxxxxxxxxxxxxxx>

---
fs/fuse/dev.c | 6 +++---
fs/fuse/fuse_i.h | 2 ++
2 files changed, 5 insertions(+), 3 deletions(-)

--- a/fs/fuse/dev.c
+++ b/fs/fuse/dev.c
@@ -276,12 +276,10 @@ static void flush_bg_queue(struct fuse_c
void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
{
struct fuse_iqueue *fiq = &fc->iq;
- bool async;

if (test_and_set_bit(FR_FINISHED, &req->flags))
goto put_request;

- async = req->args->end;
/*
* test_and_set_bit() implies smp_mb() between bit
* changing and below intr_entry check. Pairs with
@@ -324,7 +322,7 @@ void fuse_request_end(struct fuse_conn *
wake_up(&req->waitq);
}

- if (async)
+ if (test_bit(FR_ASYNC, &req->flags))
req->args->end(fc, req->args, req->out.h.error);
put_request:
fuse_put_request(fc, req);
@@ -471,6 +469,8 @@ static void fuse_args_to_req(struct fuse
req->in.h.opcode = args->opcode;
req->in.h.nodeid = args->nodeid;
req->args = args;
+ if (args->end)
+ __set_bit(FR_ASYNC, &req->flags);
}

ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -301,6 +301,7 @@ struct fuse_io_priv {
* FR_SENT: request is in userspace, waiting for an answer
* FR_FINISHED: request is finished
* FR_PRIVATE: request is on private list
+ * FR_ASYNC: request is asynchronous
*/
enum fuse_req_flag {
FR_ISREPLY,
@@ -314,6 +315,7 @@ enum fuse_req_flag {
FR_SENT,
FR_FINISHED,
FR_PRIVATE,
+ FR_ASYNC,
};

/**