Commit 3e8cb8b2 authored by Miklos Szeredi's avatar Miklos Szeredi

fuse: fix stack use after return

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: default avatarKyle Sanderson <kyle.leet@gmail.com>
Reported-by: default avatarMichael Stapelberg <michael+lkml@stapelberg.ch>
Fixes: 2b319d1f ("fuse: don't dereference req->args on finished request")
Cc: <stable@vger.kernel.org> # v5.4
Tested-by: default avatarMichael Stapelberg <michael+lkml@stapelberg.ch>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent bb6d3fb3
...@@ -276,12 +276,10 @@ static void flush_bg_queue(struct fuse_conn *fc) ...@@ -276,12 +276,10 @@ static void flush_bg_queue(struct fuse_conn *fc)
void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req) void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
{ {
struct fuse_iqueue *fiq = &fc->iq; struct fuse_iqueue *fiq = &fc->iq;
bool async;
if (test_and_set_bit(FR_FINISHED, &req->flags)) if (test_and_set_bit(FR_FINISHED, &req->flags))
goto put_request; goto put_request;
async = req->args->end;
/* /*
* test_and_set_bit() implies smp_mb() between bit * test_and_set_bit() implies smp_mb() between bit
* changing and below intr_entry check. Pairs with * changing and below intr_entry check. Pairs with
...@@ -324,7 +322,7 @@ void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req) ...@@ -324,7 +322,7 @@ void fuse_request_end(struct fuse_conn *fc, struct fuse_req *req)
wake_up(&req->waitq); wake_up(&req->waitq);
} }
if (async) if (test_bit(FR_ASYNC, &req->flags))
req->args->end(fc, req->args, req->out.h.error); req->args->end(fc, req->args, req->out.h.error);
put_request: put_request:
fuse_put_request(fc, req); fuse_put_request(fc, req);
...@@ -471,6 +469,8 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args) ...@@ -471,6 +469,8 @@ static void fuse_args_to_req(struct fuse_req *req, struct fuse_args *args)
req->in.h.opcode = args->opcode; req->in.h.opcode = args->opcode;
req->in.h.nodeid = args->nodeid; req->in.h.nodeid = args->nodeid;
req->args = args; 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) ssize_t fuse_simple_request(struct fuse_conn *fc, struct fuse_args *args)
......
...@@ -301,6 +301,7 @@ struct fuse_io_priv { ...@@ -301,6 +301,7 @@ struct fuse_io_priv {
* FR_SENT: request is in userspace, waiting for an answer * FR_SENT: request is in userspace, waiting for an answer
* FR_FINISHED: request is finished * FR_FINISHED: request is finished
* FR_PRIVATE: request is on private list * FR_PRIVATE: request is on private list
* FR_ASYNC: request is asynchronous
*/ */
enum fuse_req_flag { enum fuse_req_flag {
FR_ISREPLY, FR_ISREPLY,
...@@ -314,6 +315,7 @@ enum fuse_req_flag { ...@@ -314,6 +315,7 @@ enum fuse_req_flag {
FR_SENT, FR_SENT,
FR_FINISHED, FR_FINISHED,
FR_PRIVATE, FR_PRIVATE,
FR_ASYNC,
}; };
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment