Skip to content

Commit 6897cea

Browse files
committed
Merge tag 'for-6.8/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm
Pull device mapper fixes from Mike Snitzer: - Fix DM ioctl interface to avoid INT_MAX overflow warnings from kvmalloc by limiting the number of targets and parameter size area. - Fix DM stats to avoid INT_MAX overflow warnings from kvmalloc by limiting the number of entries supported. - Fix DM writecache to support mapping devices larger than 1 TiB by switching from using kvmalloc_array to vmalloc_array -- which avoids INT_MAX overflow in kvmalloc_node and associated warnings. - Remove the (ab)use of tasklets from both the DM crypt and verity targets. They will be converted to use BH workqueue in future. * tag 'for-6.8/dm-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm: dm-crypt, dm-verity: disable tasklets dm writecache: allow allocations larger than 2GiB dm stats: limit the number of entries dm: limit the number of targets and parameter size area
2 parents 0350327 + 0a9bab3 commit 6897cea

File tree

8 files changed

+28
-68
lines changed

8 files changed

+28
-68
lines changed

drivers/md/dm-core.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
#include "dm-ima.h"
2323

2424
#define DM_RESERVED_MAX_IOS 1024
25+
#define DM_MAX_TARGETS 1048576
26+
#define DM_MAX_TARGET_PARAMS 1024
2527

2628
struct dm_io;
2729

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-ioctl.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1941,7 +1941,8 @@ static int copy_params(struct dm_ioctl __user *user, struct dm_ioctl *param_kern
19411941
minimum_data_size - sizeof(param_kernel->version)))
19421942
return -EFAULT;
19431943

1944-
if (param_kernel->data_size < minimum_data_size) {
1944+
if (unlikely(param_kernel->data_size < minimum_data_size) ||
1945+
unlikely(param_kernel->data_size > DM_MAX_TARGETS * DM_MAX_TARGET_PARAMS)) {
19451946
DMERR("Invalid data size in the ioctl structure: %u",
19461947
param_kernel->data_size);
19471948
return -EINVAL;

drivers/md/dm-stats.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ struct dm_stats_last_position {
6666
unsigned int last_rw;
6767
};
6868

69+
#define DM_STAT_MAX_ENTRIES 8388608
70+
#define DM_STAT_MAX_HISTOGRAM_ENTRIES 134217728
71+
6972
/*
7073
* A typo on the command line could possibly make the kernel run out of memory
7174
* and crash. To prevent the crash we account all used memory. We fail if we
@@ -285,6 +288,9 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
285288
if (n_entries != (size_t)n_entries || !(size_t)(n_entries + 1))
286289
return -EOVERFLOW;
287290

291+
if (n_entries > DM_STAT_MAX_ENTRIES)
292+
return -EOVERFLOW;
293+
288294
shared_alloc_size = struct_size(s, stat_shared, n_entries);
289295
if ((shared_alloc_size - sizeof(struct dm_stat)) / sizeof(struct dm_stat_shared) != n_entries)
290296
return -EOVERFLOW;
@@ -297,6 +303,9 @@ static int dm_stats_create(struct dm_stats *stats, sector_t start, sector_t end,
297303
if (histogram_alloc_size / (n_histogram_entries + 1) != (size_t)n_entries * sizeof(unsigned long long))
298304
return -EOVERFLOW;
299305

306+
if ((n_histogram_entries + 1) * (size_t)n_entries > DM_STAT_MAX_HISTOGRAM_ENTRIES)
307+
return -EOVERFLOW;
308+
300309
if (!check_shared_memory(shared_alloc_size + histogram_alloc_size +
301310
num_possible_cpus() * (percpu_alloc_size + histogram_alloc_size)))
302311
return -ENOMEM;

drivers/md/dm-table.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,12 @@ static int alloc_targets(struct dm_table *t, unsigned int num)
129129
int dm_table_create(struct dm_table **result, blk_mode_t mode,
130130
unsigned int num_targets, struct mapped_device *md)
131131
{
132-
struct dm_table *t = kzalloc(sizeof(*t), GFP_KERNEL);
132+
struct dm_table *t;
133+
134+
if (num_targets > DM_MAX_TARGETS)
135+
return -EOVERFLOW;
136+
137+
t = kzalloc(sizeof(*t), GFP_KERNEL);
133138

134139
if (!t)
135140
return -ENOMEM;
@@ -144,7 +149,7 @@ int dm_table_create(struct dm_table **result, blk_mode_t mode,
144149

145150
if (!num_targets) {
146151
kfree(t);
147-
return -ENOMEM;
152+
return -EOVERFLOW;
148153
}
149154

150155
if (alloc_targets(t, num_targets)) {

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:

drivers/md/dm-writecache.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ static int persistent_memory_claim(struct dm_writecache *wc)
299299
long i;
300300

301301
wc->memory_map = NULL;
302-
pages = kvmalloc_array(p, sizeof(struct page *), GFP_KERNEL);
302+
pages = vmalloc_array(p, sizeof(struct page *));
303303
if (!pages) {
304304
r = -ENOMEM;
305305
goto err2;
@@ -330,7 +330,7 @@ static int persistent_memory_claim(struct dm_writecache *wc)
330330
r = -ENOMEM;
331331
goto err3;
332332
}
333-
kvfree(pages);
333+
vfree(pages);
334334
wc->memory_vmapped = true;
335335
}
336336

@@ -341,7 +341,7 @@ static int persistent_memory_claim(struct dm_writecache *wc)
341341

342342
return 0;
343343
err3:
344-
kvfree(pages);
344+
vfree(pages);
345345
err2:
346346
dax_read_unlock(id);
347347
err1:
@@ -962,7 +962,7 @@ static int writecache_alloc_entries(struct dm_writecache *wc)
962962

963963
if (wc->entries)
964964
return 0;
965-
wc->entries = vmalloc(array_size(sizeof(struct wc_entry), wc->n_blocks));
965+
wc->entries = vmalloc_array(wc->n_blocks, sizeof(struct wc_entry));
966966
if (!wc->entries)
967967
return -ENOMEM;
968968
for (b = 0; b < wc->n_blocks; b++) {

0 commit comments

Comments
 (0)