io_uring: grab task reference for poll requests
authorJens Axboe <axboe@kernel.dk>
Fri, 3 Apr 2020 17:19:06 +0000 (11:19 -0600)
committerJens Axboe <axboe@kernel.dk>
Fri, 3 Apr 2020 17:35:57 +0000 (11:35 -0600)
We can have a task exit if it's not the owner of the ring. Be safe and
grab an actual reference to it, to avoid a potential use-after-free.

Reported-by: Dan Melnic <dmm@fb.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/io_uring.c

index 8ad4a15..b343525 100644 (file)
@@ -615,10 +615,8 @@ struct io_kiocb {
        struct list_head        list;
        unsigned int            flags;
        refcount_t              refs;
-       union {
-               struct task_struct      *task;
-               unsigned long           fsize;
-       };
+       struct task_struct      *task;
+       unsigned long           fsize;
        u64                     user_data;
        u32                     result;
        u32                     sequence;
@@ -1336,6 +1334,7 @@ got_it:
        req->flags = 0;
        /* one is dropped after submission, the other at completion */
        refcount_set(&req->refs, 2);
+       req->task = NULL;
        req->result = 0;
        INIT_IO_WORK(&req->work, io_wq_submit_work);
        return req;
@@ -1372,6 +1371,8 @@ static void __io_req_aux_free(struct io_kiocb *req)
        kfree(req->io);
        if (req->file)
                io_put_file(req, req->file, (req->flags & REQ_F_FIXED_FILE));
+       if (req->task)
+               put_task_struct(req->task);
 
        io_req_work_drop_env(req);
 }
@@ -4256,10 +4257,7 @@ static bool io_arm_poll_handler(struct io_kiocb *req)
        req->flags |= REQ_F_POLLED;
        memcpy(&apoll->work, &req->work, sizeof(req->work));
 
-       /*
-        * Don't need a reference here, as we're adding it to the task
-        * task_works list. If the task exits, the list is pruned.
-        */
+       get_task_struct(current);
        req->task = current;
        req->apoll = apoll;
        INIT_HLIST_NODE(&req->hash_node);
@@ -4482,10 +4480,7 @@ static int io_poll_add_prep(struct io_kiocb *req, const struct io_uring_sqe *sqe
        events = READ_ONCE(sqe->poll_events);
        poll->events = demangle_poll(events) | EPOLLERR | EPOLLHUP;
 
-       /*
-        * Don't need a reference here, as we're adding it to the task
-        * task_works list. If the task exits, the list is pruned.
-        */
+       get_task_struct(current);
        req->task = current;
        return 0;
 }