Skip to content

Commit 8e14f61

Browse files
Ignat Korchaginsnitm
authored andcommitted
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: 39d42fa ("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>
1 parent 9b59482 commit 8e14f61

1 file changed

Lines changed: 23 additions & 1 deletion

File tree

drivers/md/dm-crypt.c

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1730,6 +1730,12 @@ static void crypt_inc_pending(struct dm_crypt_io *io)
17301730
atomic_inc(&io->io_pending);
17311731
}
17321732

1733+
static void kcryptd_io_bio_endio(struct work_struct *work)
1734+
{
1735+
struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);
1736+
bio_endio(io->base_bio);
1737+
}
1738+
17331739
/*
17341740
* One of the bios was finished. Check for completion of
17351741
* the whole request and correctly clean up the buffer.
@@ -1752,7 +1758,23 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
17521758
kfree(io->integrity_metadata);
17531759

17541760
base_bio->bi_status = error;
1755-
bio_endio(base_bio);
1761+
1762+
/*
1763+
* If we are running this function from our tasklet,
1764+
* we can't call bio_endio() here, because it will call
1765+
* clone_endio() from dm.c, which in turn will
1766+
* free the current struct dm_crypt_io structure with
1767+
* our tasklet. In this case we need to delay bio_endio()
1768+
* execution to after the tasklet is done and dequeued.
1769+
*/
1770+
if (tasklet_trylock(&io->tasklet)) {
1771+
tasklet_unlock(&io->tasklet);
1772+
bio_endio(base_bio);
1773+
return;
1774+
}
1775+
1776+
INIT_WORK(&io->work, kcryptd_io_bio_endio);
1777+
queue_work(cc->io_queue, &io->work);
17561778
}
17571779

17581780
/*

0 commit comments

Comments
 (0)