diff mbox series

io_uring: fix a use after free in io_async_task_func()

Message ID 20200708184711.GA31157@mwanda (mailing list archive)
State New, archived
Headers show
Series io_uring: fix a use after free in io_async_task_func() | expand

Commit Message

Dan Carpenter July 8, 2020, 6:47 p.m. UTC
The "apoll" variable is freed and then used on the next line.  We need
to move the free down a few lines.

Fixes: 0be0b0e33b0b ("io_uring: simplify io_async_task_func()")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
---
 fs/io_uring.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Comments

Pavel Begunkov July 8, 2020, 7:01 p.m. UTC | #1
On 08/07/2020 21:47, Dan Carpenter wrote:
> The "apoll" variable is freed and then used on the next line.  We need
> to move the free down a few lines.

My bad, thanks for spotting that!
A comment below

> 
> Fixes: 0be0b0e33b0b ("io_uring: simplify io_async_task_func()")
> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
> ---
>  fs/io_uring.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/io_uring.c b/fs/io_uring.c
> index 70828e2470e2..f2993070a9e8 100644
> --- a/fs/io_uring.c
> +++ b/fs/io_uring.c
> @@ -4652,12 +4652,13 @@ static void io_async_task_func(struct callback_head *cb)
>  	/* restore ->work in case we need to retry again */
>  	if (req->flags & REQ_F_WORK_INITIALIZED)
>  		memcpy(&req->work, &apoll->work, sizeof(req->work));
> -	kfree(apoll);

__io_req_task_submit() can take some time, e.g. waiting for a mutex, so
I'd rather free it before, but save req->poll.canceled in a local var.

>  
>  	if (!READ_ONCE(apoll->poll.canceled))
>  		__io_req_task_submit(req);
>  	else
>  		__io_req_task_cancel(req, -ECANCELED);
> +
> +	kfree(apoll);
>  }
>  
>  static int io_async_wake(struct wait_queue_entry *wait, unsigned mode, int sync,
>
Jens Axboe July 8, 2020, 7:14 p.m. UTC | #2
On 7/8/20 1:01 PM, Pavel Begunkov wrote:
> On 08/07/2020 21:47, Dan Carpenter wrote:
>> The "apoll" variable is freed and then used on the next line.  We need
>> to move the free down a few lines.
> 
> My bad, thanks for spotting that!
> A comment below
> 
>>
>> Fixes: 0be0b0e33b0b ("io_uring: simplify io_async_task_func()")
>> Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
>> ---
>>  fs/io_uring.c | 3 ++-
>>  1 file changed, 2 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/io_uring.c b/fs/io_uring.c
>> index 70828e2470e2..f2993070a9e8 100644
>> --- a/fs/io_uring.c
>> +++ b/fs/io_uring.c
>> @@ -4652,12 +4652,13 @@ static void io_async_task_func(struct callback_head *cb)
>>  	/* restore ->work in case we need to retry again */
>>  	if (req->flags & REQ_F_WORK_INITIALIZED)
>>  		memcpy(&req->work, &apoll->work, sizeof(req->work));
>> -	kfree(apoll);
> 
> __io_req_task_submit() can take some time, e.g. waiting for a mutex, so
> I'd rather free it before, but save req->poll.canceled in a local var.

It's 64 bytes of data, really doesn't matter. Let's just keep it simple.
Jens Axboe July 8, 2020, 7:15 p.m. UTC | #3
On 7/8/20 12:47 PM, Dan Carpenter wrote:
> The "apoll" variable is freed and then used on the next line.  We need
> to move the free down a few lines.

Thanks for spotting this Dan, applied.
Pavel Begunkov July 8, 2020, 7:28 p.m. UTC | #4
On 08/07/2020 22:15, Jens Axboe wrote:
> On 7/8/20 12:47 PM, Dan Carpenter wrote:
>> The "apoll" variable is freed and then used on the next line.  We need
>> to move the free down a few lines.
> 
> Thanks for spotting this Dan, applied.

I wonder why gcc can't find it... It shouldn't be hard to do after
marking free-like functions with an attribute.

Are there such tools for the kernel?
Matthew Wilcox July 8, 2020, 7:56 p.m. UTC | #5
On Wed, Jul 08, 2020 at 10:28:51PM +0300, Pavel Begunkov wrote:
> On 08/07/2020 22:15, Jens Axboe wrote:
> > On 7/8/20 12:47 PM, Dan Carpenter wrote:
> >> The "apoll" variable is freed and then used on the next line.  We need
> >> to move the free down a few lines.
> > 
> > Thanks for spotting this Dan, applied.
> 
> I wonder why gcc can't find it... It shouldn't be hard to do after
> marking free-like functions with an attribute.
> 
> Are there such tools for the kernel?

GCC doesn't have an __attribute__((free)) yet.  Martin Sebor is working on
it: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87736
also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94527

(I just confirmed with him on IRC that he's still working on it; it's
part of an ongoing larger project)
Pavel Begunkov July 9, 2020, 9:47 a.m. UTC | #6
On 08/07/2020 22:56, Matthew Wilcox wrote:
> On Wed, Jul 08, 2020 at 10:28:51PM +0300, Pavel Begunkov wrote:
>> On 08/07/2020 22:15, Jens Axboe wrote:
>>> On 7/8/20 12:47 PM, Dan Carpenter wrote:
>>>> The "apoll" variable is freed and then used on the next line.  We need
>>>> to move the free down a few lines.
>>>
>>> Thanks for spotting this Dan, applied.
>>
>> I wonder why gcc can't find it... It shouldn't be hard to do after
>> marking free-like functions with an attribute.
>>
>> Are there such tools for the kernel?
> 
> GCC doesn't have an __attribute__((free)) yet.  Martin Sebor is working on
> it: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87736
> also: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=94527
> 
> (I just confirmed with him on IRC that he's still working on it; it's
> part of an ongoing larger project)

Good to know. It looks broader indeed, hence won't probably be here too soon.
diff mbox series

Patch

diff --git a/fs/io_uring.c b/fs/io_uring.c
index 70828e2470e2..f2993070a9e8 100644
--- a/fs/io_uring.c
+++ b/fs/io_uring.c
@@ -4652,12 +4652,13 @@  static void io_async_task_func(struct callback_head *cb)
 	/* restore ->work in case we need to retry again */
 	if (req->flags & REQ_F_WORK_INITIALIZED)
 		memcpy(&req->work, &apoll->work, sizeof(req->work));
-	kfree(apoll);
 
 	if (!READ_ONCE(apoll->poll.canceled))
 		__io_req_task_submit(req);
 	else
 		__io_req_task_cancel(req, -ECANCELED);
+
+	kfree(apoll);
 }
 
 static int io_async_wake(struct wait_queue_entry *wait, unsigned mode, int sync,