mirror of
https://github.com/lkl/linux.git
synced 2025-12-19 08:03:01 +09:00
block, bfq: fix use after free in bfq_bfqq_expire
The function bfq_bfqq_expire() invokes the function __bfq_bfqq_expire(), and the latter may free the in-service bfq-queue. If this happens, then no other instruction of bfq_bfqq_expire() must be executed, or a use-after-free will occur. Basing on the assumption that __bfq_bfqq_expire() invokes bfq_put_queue() on the in-service bfq-queue exactly once, the queue is assumed to be freed if its refcounter is equal to one right before invoking __bfq_bfqq_expire(). But, since commit9dee8b3b05("block, bfq: fix queue removal from weights tree") this assumption is false. __bfq_bfqq_expire() may also invoke bfq_weights_tree_remove() and, since commit9dee8b3b05("block, bfq: fix queue removal from weights tree"), also the latter function may invoke bfq_put_queue(). So __bfq_bfqq_expire() may invoke bfq_put_queue() twice, and this is the actual case where the in-service queue may happen to be freed. To address this issue, this commit moves the check on the refcounter of the queue right around the last bfq_put_queue() that may be invoked on the queue. Fixes:9dee8b3b05("block, bfq: fix queue removal from weights tree") Reported-by: Dmitrii Tcvetkov <demfloro@demfloro.ru> Reported-by: Douglas Anderson <dianders@chromium.org> Tested-by: Dmitrii Tcvetkov <demfloro@demfloro.ru> Tested-by: Douglas Anderson <dianders@chromium.org> Signed-off-by: Paolo Valente <paolo.valente@linaro.org> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
committed by
Jens Axboe
parent
3ec482d15c
commit
eed47d19d9
@@ -1605,7 +1605,8 @@ struct bfq_queue *bfq_get_next_queue(struct bfq_data *bfqd)
|
||||
return bfqq;
|
||||
}
|
||||
|
||||
void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd)
|
||||
/* returns true if the in-service queue gets freed */
|
||||
bool __bfq_bfqd_reset_in_service(struct bfq_data *bfqd)
|
||||
{
|
||||
struct bfq_queue *in_serv_bfqq = bfqd->in_service_queue;
|
||||
struct bfq_entity *in_serv_entity = &in_serv_bfqq->entity;
|
||||
@@ -1629,8 +1630,20 @@ void __bfq_bfqd_reset_in_service(struct bfq_data *bfqd)
|
||||
* service tree either, then release the service reference to
|
||||
* the queue it represents (taken with bfq_get_entity).
|
||||
*/
|
||||
if (!in_serv_entity->on_st)
|
||||
if (!in_serv_entity->on_st) {
|
||||
/*
|
||||
* If no process is referencing in_serv_bfqq any
|
||||
* longer, then the service reference may be the only
|
||||
* reference to the queue. If this is the case, then
|
||||
* bfqq gets freed here.
|
||||
*/
|
||||
int ref = in_serv_bfqq->ref;
|
||||
bfq_put_queue(in_serv_bfqq);
|
||||
if (ref == 1)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void bfq_deactivate_bfqq(struct bfq_data *bfqd, struct bfq_queue *bfqq,
|
||||
|
||||
Reference in New Issue
Block a user