Merge tag 'for-7.3/dm-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm

Pull device mapper fixes from Mikulas Patocka:
 "dm-integrity:
   - validate the superblock after re-reading it
   - fix buffer overflow if tag size > 64

  dm:
   - fix reading up to 7 bytes beyond the end of block in dm-ioctl
   - fix reading free memory if the ioctls are called concurrently

  dm-crypt:
   - fix a crash on invalid table line

  dm-snap:
   - fix a crash on invalid table line"

* tag 'for-7.3/dm-fixes-2' of git://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm:
  dm-integrity: validate the superblock on resume
  dm-snap: reject the transient exception store for snapshot-merge
  dm: fix reading free memory in do_resume
  dm ioctl: don't let data_start run past the buffer
  dm-crypt: reject the lmk IV mode with AEAD ciphers
  dm-integrity: fix buffer overflow in inline mode with large tag size
This commit is contained in:
Linus Torvalds committed 2026-10-07 12:19:52 +02:00
commit 0f2732620d
4 files changed
+83 -6

No files matched your search

+6 -1
View File
@@ -2894,8 +2894,13 @@ static int crypt_ctr_cipher_new(struct dm_target *ti, char *cipher_in, char *key
}
}
if (*ivmode && !strcmp(*ivmode, "lmk"))
if (*ivmode && !strcmp(*ivmode, "lmk")) {
if (crypt_integrity_aead(cc)) {
ti->error = "AEAD transforms not supported for LMK";
return -EINVAL;
}
cc->tfms_count = 64;
}
if (*ivmode && !strcmp(*ivmode, "essiv")) {
if (!*ivopts) {
+52 -2
View File
@@ -180,6 +180,7 @@ struct dm_integrity_c {
struct dm_bufio_client *bufio;
struct workqueue_struct *metadata_wq;
struct superblock *sb;
struct superblock *sb_copy;
unsigned int journal_pages;
unsigned int n_bitmap_blocks;
@@ -2721,7 +2722,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)
outgoing_data = dio->integrity_payload + PAGE_SIZE;
while (dio->bio_details.bi_iter.bi_size) {
char digest[HASH_MAX_DIGESTSIZE];
char digest[MAX_T(size_t, HASH_MAX_DIGESTSIZE, MAX_TAG_SIZE)];
int r;
struct bio_integrity_payload *bip;
struct bio_vec bv;
@@ -2788,7 +2789,7 @@ static inline bool dm_integrity_check(struct dm_integrity_c *ic, struct dm_integ
unsigned pos = 0;
while (dio->bio_details.bi_iter.bi_size) {
char digest[HASH_MAX_DIGESTSIZE];
char digest[MAX_T(size_t, HASH_MAX_DIGESTSIZE, MAX_TAG_SIZE)];
struct bio_vec bv = bio_iter_iovec(bio, dio->bio_details.bi_iter);
char *mem = integrity_kmap(ic, bv.bv_page);
integrity_sector_checksum(ic, &dio->ahash_req, dio->bio_details.bi_iter.bi_sector, mem, bv.bv_offset, digest);
@@ -3858,6 +3859,35 @@ static void dm_integrity_postsuspend(struct dm_target *ti)
ic->journal_uptodate = true;
}
/*
* The superblock is re-read from the device on every resume, so that we pick
* up the flags and the recalculate position. The geometry described by the
* superblock must not change though - the in-memory structures (and the
* journal in particular) were sized according to the superblock that was
* validated in the constructor. Reject a superblock that was modified behind
* our back.
*
* Only the fields that the driver never rewrites may be tested here. In
* particular, "version" is recalculated by sb_set_version on every superblock
* write and it depends on SB_FLAG_RECALCULATING and SB_FLAG_DIRTY_BITMAP, and
* SB_FLAG_DISCARD_KEYED may be set by dm_integrity_resume itself.
*/
static bool superblock_changed(struct dm_integrity_c *ic)
{
const __le32 immutable_flags = cpu_to_le32(SB_FLAG_HAVE_JOURNAL_MAC |
SB_FLAG_FIXED_PADDING |
SB_FLAG_FIXED_HMAC |
SB_FLAG_INLINE);
return memcmp(ic->sb->magic, ic->sb_copy->magic, sizeof(ic->sb->magic)) != 0 ||
ic->sb->log2_interleave_sectors != ic->sb_copy->log2_interleave_sectors ||
ic->sb->integrity_tag_size != ic->sb_copy->integrity_tag_size ||
ic->sb->journal_sections != ic->sb_copy->journal_sections ||
ic->sb->log2_sectors_per_block != ic->sb_copy->log2_sectors_per_block ||
((ic->sb->flags ^ ic->sb_copy->flags) & immutable_flags) != 0 ||
memcmp(ic->sb->salt, ic->sb_copy->salt, SALT_SIZE) != 0;
}
static void dm_integrity_resume(struct dm_target *ti)
{
struct dm_integrity_c *ic = ti->private;
@@ -3876,6 +3906,18 @@ static void dm_integrity_resume(struct dm_target *ti)
if (r)
dm_integrity_io_error(ic, "reading superblock", r);
if (unlikely(superblock_changed(ic))) {
/*
* Restore the superblock that we validated in the constructor,
* so that the rest of the driver doesn't operate on values
* that don't match the in-memory structures.
*/
memcpy(ic->sb, ic->sb_copy, sizeof(struct superblock));
DMERR("The superblock was changed while the device was suspended");
dm_integrity_io_error(ic, "superblock check", -EINVAL);
goto skip_writes;
}
if (ic->mode == 'R')
goto skip_writes;
@@ -5427,6 +5469,13 @@ try_smaller_buffer:
ic->just_formatted = true;
}
ic->sb_copy = kmemdup(ic->sb, sizeof(struct superblock), GFP_KERNEL);
if (!ic->sb_copy) {
ti->error = "Cannot allocate superblock copy";
r = -ENOMEM;
goto bad;
}
if (!ic->meta_dev && ic->mode != 'I') {
r = dm_set_target_max_io_len(ti, 1U << ic->sb->log2_interleave_sectors);
if (r)
@@ -5525,6 +5574,7 @@ static void dm_integrity_dtr(struct dm_target *ti)
kvfree(ic->journal_tree);
if (ic->sb)
free_pages_exact(ic->sb, SB_SECTORS << SECTOR_SHIFT);
kfree(ic->sb_copy);
if (ic->internal_shash)
crypto_free_shash(ic->internal_shash);
+11 -3
View File
@@ -656,10 +656,16 @@ static void *get_result_buffer(struct dm_ioctl *param, size_t param_size,
{
param->data_start = align_ptr(param + 1) - (void *) param;
if (param->data_start < param_size)
if (param->data_start < param_size) {
*len = param_size - param->data_start;
else
} else {
/*
* The buffer can be as small as offsetof(*param, data), which
* is less than sizeof(*param), so don't run off the end of it.
*/
*len = 0;
param->data_start = param_size;
}
return ((void *) param) + param->data_start;
}
@@ -1267,6 +1273,7 @@ static int do_resume(struct dm_ioctl *param)
/* Do we need to load a new map ? */
if (new_map) {
sector_t old_size, new_size;
blk_mode_t new_map_mode;
dm_ima_context_table_op(md, ima_context, DM_IMA_TABLE_SAVE);
/* Suspend if it isn't already suspended */
@@ -1299,6 +1306,7 @@ static int do_resume(struct dm_ioctl *param)
}
}
new_map_mode = dm_table_get_mode(new_map);
old_size = dm_get_size(md);
old_map = dm_swap_table(md, new_map);
if (IS_ERR(old_map)) {
@@ -1314,7 +1322,7 @@ static int do_resume(struct dm_ioctl *param)
if (old_size && new_size && old_size != new_size)
need_resize_uevent = true;
if (dm_table_get_mode(new_map) & BLK_OPEN_WRITE)
if (new_map_mode & BLK_OPEN_WRITE)
set_disk_ro(dm_disk(md), 0);
else
set_disk_ro(dm_disk(md), 1);
+14
View File
@@ -1292,6 +1292,20 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad_store;
}
/*
* snapshot-merge requires the optional prepare_merge/commit_merge
* callbacks; the transient exception store does not implement them.
* The handover path checks this in __validate_exception_handover(),
* but a directly constructed snapshot-merge never goes through it.
*/
if (dm_target_is_snapshot_merge(ti) &&
(!s->store->type->prepare_merge ||
!s->store->type->commit_merge)) {
ti->error = "Snapshot exception store does not support snapshot-merge.";
r = -EINVAL;
goto bad_hash_tables;
}
argv += args_used;
argc -= args_used;