mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
synced 2026-10-28 12:53:14 +01:00
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:
commit
0f2732620d
4 files changed
+83
-6
No files matched your search
@@ -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) {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in new issue
Block a user