Skip to content

Commit 0a9bab3

Browse files
Mikulas PatockaMike Snitzer
authored andcommitted
dm-crypt, dm-verity: disable tasklets
Tasklets have an inherent problem with memory corruption. The function tasklet_action_common calls tasklet_trylock, then it calls the tasklet callback and then it calls tasklet_unlock. If the tasklet callback frees the structure that contains the tasklet or if it calls some code that may free it, tasklet_unlock will write into free memory. The commits 8e14f61 and d9a02e0 try to fix it for dm-crypt, but it is not a sufficient fix and the data corruption can still happen [1]. There is no fix for dm-verity and dm-verity will write into free memory with every tasklet-processed bio. There will be atomic workqueues implemented in the kernel 6.9 [2]. They will have better interface and they will not suffer from the memory corruption problem. But we need something that stops the memory corruption now and that can be backported to the stable kernels. So, I'm proposing this commit that disables tasklets in both dm-crypt and dm-verity. This commit doesn't remove the tasklet support, because the tasklet code will be reused when atomic workqueues will be implemented. [1] https://lore.kernel.org/all/d390d7ee-f142-44d3-822a-87949e14608b@suse.de/T/ [2] https://lore.kernel.org/lkml/20240130091300.2968534-1-tj@kernel.org/ Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: stable@vger.kernel.org Fixes: 39d42fa ("dm crypt: add flags to optionally bypass kcryptd workqueues") Fixes: 5721d4e ("dm verity: Add optional "try_verify_in_tasklet" feature") Signed-off-by: Mike Snitzer <snitzer@kernel.org>
1 parent 40ef875 commit 0a9bab3

File tree

3 files changed

+4
-61
lines changed

3 files changed

+4
-61
lines changed

drivers/md/dm-crypt.c

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,8 @@ struct dm_crypt_io {
7373
struct bio *base_bio;
7474
u8 *integrity_metadata;
7575
bool integrity_metadata_from_pool:1;
76-
bool in_tasklet:1;
7776

7877
struct work_struct work;
79-
struct tasklet_struct tasklet;
8078

8179
struct convert_context ctx;
8280

@@ -1762,7 +1760,6 @@ static void crypt_io_init(struct dm_crypt_io *io, struct crypt_config *cc,
17621760
io->ctx.r.req = NULL;
17631761
io->integrity_metadata = NULL;
17641762
io->integrity_metadata_from_pool = false;
1765-
io->in_tasklet = false;
17661763
atomic_set(&io->io_pending, 0);
17671764
}
17681765

@@ -1771,13 +1768,6 @@ static void crypt_inc_pending(struct dm_crypt_io *io)
17711768
atomic_inc(&io->io_pending);
17721769
}
17731770

1774-
static void kcryptd_io_bio_endio(struct work_struct *work)
1775-
{
1776-
struct dm_crypt_io *io = container_of(work, struct dm_crypt_io, work);
1777-
1778-
bio_endio(io->base_bio);
1779-
}
1780-
17811771
/*
17821772
* One of the bios was finished. Check for completion of
17831773
* the whole request and correctly clean up the buffer.
@@ -1801,20 +1791,6 @@ static void crypt_dec_pending(struct dm_crypt_io *io)
18011791

18021792
base_bio->bi_status = error;
18031793

1804-
/*
1805-
* If we are running this function from our tasklet,
1806-
* we can't call bio_endio() here, because it will call
1807-
* clone_endio() from dm.c, which in turn will
1808-
* free the current struct dm_crypt_io structure with
1809-
* our tasklet. In this case we need to delay bio_endio()
1810-
* execution to after the tasklet is done and dequeued.
1811-
*/
1812-
if (io->in_tasklet) {
1813-
INIT_WORK(&io->work, kcryptd_io_bio_endio);
1814-
queue_work(cc->io_queue, &io->work);
1815-
return;
1816-
}
1817-
18181794
bio_endio(base_bio);
18191795
}
18201796

@@ -2246,11 +2222,6 @@ static void kcryptd_crypt(struct work_struct *work)
22462222
kcryptd_crypt_write_convert(io);
22472223
}
22482224

2249-
static void kcryptd_crypt_tasklet(unsigned long work)
2250-
{
2251-
kcryptd_crypt((struct work_struct *)work);
2252-
}
2253-
22542225
static void kcryptd_queue_crypt(struct dm_crypt_io *io)
22552226
{
22562227
struct crypt_config *cc = io->cc;
@@ -2262,15 +2233,10 @@ static void kcryptd_queue_crypt(struct dm_crypt_io *io)
22622233
* irqs_disabled(): the kernel may run some IO completion from the idle thread, but
22632234
* it is being executed with irqs disabled.
22642235
*/
2265-
if (in_hardirq() || irqs_disabled()) {
2266-
io->in_tasklet = true;
2267-
tasklet_init(&io->tasklet, kcryptd_crypt_tasklet, (unsigned long)&io->work);
2268-
tasklet_schedule(&io->tasklet);
2236+
if (!(in_hardirq() || irqs_disabled())) {
2237+
kcryptd_crypt(&io->work);
22692238
return;
22702239
}
2271-
2272-
kcryptd_crypt(&io->work);
2273-
return;
22742240
}
22752241

22762242
INIT_WORK(&io->work, kcryptd_crypt);

drivers/md/dm-verity-target.c

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -645,23 +645,6 @@ static void verity_work(struct work_struct *w)
645645
verity_finish_io(io, errno_to_blk_status(verity_verify_io(io)));
646646
}
647647

648-
static void verity_tasklet(unsigned long data)
649-
{
650-
struct dm_verity_io *io = (struct dm_verity_io *)data;
651-
int err;
652-
653-
io->in_tasklet = true;
654-
err = verity_verify_io(io);
655-
if (err == -EAGAIN || err == -ENOMEM) {
656-
/* fallback to retrying with work-queue */
657-
INIT_WORK(&io->work, verity_work);
658-
queue_work(io->v->verify_wq, &io->work);
659-
return;
660-
}
661-
662-
verity_finish_io(io, errno_to_blk_status(err));
663-
}
664-
665648
static void verity_end_io(struct bio *bio)
666649
{
667650
struct dm_verity_io *io = bio->bi_private;
@@ -674,13 +657,8 @@ static void verity_end_io(struct bio *bio)
674657
return;
675658
}
676659

677-
if (static_branch_unlikely(&use_tasklet_enabled) && io->v->use_tasklet) {
678-
tasklet_init(&io->tasklet, verity_tasklet, (unsigned long)io);
679-
tasklet_schedule(&io->tasklet);
680-
} else {
681-
INIT_WORK(&io->work, verity_work);
682-
queue_work(io->v->verify_wq, &io->work);
683-
}
660+
INIT_WORK(&io->work, verity_work);
661+
queue_work(io->v->verify_wq, &io->work);
684662
}
685663

686664
/*

drivers/md/dm-verity.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ struct dm_verity_io {
8383
struct bvec_iter iter;
8484

8585
struct work_struct work;
86-
struct tasklet_struct tasklet;
8786

8887
/*
8988
* Three variably-size fields follow this struct:

0 commit comments

Comments
 (0)