dm crypt: do not call bio_endio() from the dm-crypt tasklet
Sometimes, when dm-crypt executes decryption in a tasklet, we may get
"BUG: KASAN: use-after-free in tasklet_action_common.constprop..."
with a kasan-enabled kernel.
When the decryption fully completes in the tasklet, dm-crypt will call
bio_endio(), which in turn will call clone_endio() from dm.c core code. That
function frees the resources associated with the bio, including per bio private
structures. For dm-crypt it will free the current struct dm_crypt_io, which
contains our tasklet object, causing use-after-free, when the tasklet is being
dequeued by the kernel.
To avoid this, do not call bio_endio() from the current tasklet context, but
delay its execution to the dm-crypt IO workqueue.
Fixes: 39d42fa96b
("dm crypt: add flags to optionally bypass kcryptd workqueues")
Cc: <stable@vger.kernel.org> # v5.9+
Signed-off-by: Ignat Korchagin <ignat@cloudflare.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
This commit is contained in:
Родитель
9b5948267a
Коммит
8e14f61015
|
@ -1730,6 +1730,12 @@ static void crypt_inc_pending(struct dm_crypt_io *io)
|
|||
atomic_inc(&io->io_pending);
|
||||
}
|
||||
|
||||
static void kcryptd_io_bio_endio(struct work_struct *work)
|
||||
{
|
||||
struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);
|
||||
bio_endio(io->base_bio);
|
||||
}
|
||||
|
||||
/*
|
||||
* One of the bios was finished. Check for completion of
|
||||
* the whole request and correctly clean up the buffer.
|
||||
|
@ -1752,7 +1758,23 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
|
|||
kfree(io->integrity_metadata);
|
||||
|
||||
base_bio->bi_status = error;
|
||||
bio_endio(base_bio);
|
||||
|
||||
/*
|
||||
* If we are running this function from our tasklet,
|
||||
* we can't call bio_endio() here, because it will call
|
||||
* clone_endio() from dm.c, which in turn will
|
||||
* free the current struct dm_crypt_io structure with
|
||||
* our tasklet. In this case we need to delay bio_endio()
|
||||
* execution to after the tasklet is done and dequeued.
|
||||
*/
|
||||
if (tasklet_trylock(&io->tasklet)) {
|
||||
tasklet_unlock(&io->tasklet);
|
||||
bio_endio(base_bio);
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_WORK(&io->work, kcryptd_io_bio_endio);
|
||||
queue_work(cc->io_queue, &io->work);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Загрузка…
Ссылка в новой задаче