FAILED: patch "[PATCH] dm-integrity: fix buffer overflow in inline mode with large" failed to apply to 6.12-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.12-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.12.y
git checkout FETCH_HEAD
git cherry-pick -x 764b683a6180fd72c931c13fea98d383506a3475
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026101250-why-icon-9fa2@gregkh' --subject-prefix 'PATCH 6.12.y' 'HEAD^..'
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 764b683a6180fd72c931c13fea98d383506a3475 Mon Sep 17 00:00:00 2001
From: Ben Cressey <[email protected]>
Date: Tue, 15 Sep 2026 00:37:35 +0000
Subject: [PATCH] dm-integrity: fix buffer overflow in inline mode with large
tag size
In inline mode, dm_integrity_check and dm_integrity_inline_recheck compute the checksum of each block into an on-stack buffer of HASH_MAX_DIGESTSIZE bytes. integrity_sector_checksum pads its result with zeroes up to the tag size, so if the tag size is larger than HASH_MAX_DIGESTSIZE, the padding runs past the end of the buffer. With CONFIG_STACKPROTECTOR the kernel panics in dm_integrity_check on the first read.
Enlarge both buffers to hold MAX_TAG_SIZE bytes, as commit b93b6643e9b5 ("dm integrity: fix a crash with unusually large tag size") did for integrity_metadata.
Note that this is not a security bug because the buffer overflow can only be triggered by root.
drivers/md/dm-integrity.c +2 -2
@@ -2721,7 +2721,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 +2788,7 @@ static inline bool dm_integrity_check(struct dm_integrity_c *ic, struct dm_integunsigned 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);
Sasha Levin
maintainerPATCH 1/3 dm-integrity: use internal variable for digestsize
From: Mikulas Patocka <[email protected]>
[ Upstream commit e4cc9deca3da48986072327ae2eeb18e6fd0165d ]
Instead of calling digestsize() each time the digestsize for the internal hash is needed, store the digestsize in a new field internal_hash_digestsize within struct dm_integrity_c once and use this value when needed.
Stable-dep-of: 764b683a6180 ("dm-integrity: fix buffer overflow in inline mode with large tag size")
--- drivers/md/dm-integrity.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-)
drivers/md/dm-integrity.c +14 -11
@@ -223,6 +223,7 @@ struct dm_integrity_c {int failed;struct crypto_shash *internal_hash; +unsigned int internal_hash_digestsize;struct dm_target *ti;
@@ -1672,7 +1673,7 @@ static void integrity_sector_checksum(struct dm_integrity_c *ic, sector_t sector goto failed; } - digest_size =crypto_shash_digestsize (ic->internal_hash); + digest_size = ic->internal_hash_digestsize;if (unlikely (digest_size < ic->tag_size))memset (result + digest_size,0 , ic->tag_size - digest_size);
@@ -1772,7 +1773,7 @@ static void integrity_metadata(struct work_struct *w)if (ic->internal_hash) {struct bvec_iter iter;struct bio_vec bv; -unsigned int digest_size =crypto_shash_digestsize (ic->internal_hash); +unsigned int digest_size = ic->internal_hash_digestsize;struct bio *bio =dm_bio_from_per_bio_data (dio,sizeof (struct dm_integrity_io ));char *checksums;unsigned int extra_space =unlikely (digest_size > ic->tag_size) ? digest_size - ic->tag_size :0 ;
@@ -2120,7 +2121,7 @@ static bool __journal_read_write(struct dm_integrity_io *dio, struct bio *bio, }while (++s < ic->sectors_per_block);if (ic->internal_hash) { -unsigned int digest_size =crypto_shash_digestsize (ic->internal_hash); +unsigned int digest_size = ic->internal_hash_digestsize;if (unlikely (digest_size > ic->tag_size)) {char checksums_onstack[HASH_MAX_DIGESTSIZE ];
@@ -2427,7 +2428,7 @@ static int dm_integrity_map_inline(struct dm_integrity_io *dio, bool from_map)if (!dio->integrity_payload) {unsigned digest_size, extra_size; dio->payload_len = ic->tuple_size * (bio_sectors (bio) >> ic->sb->log2_sectors_per_block); - digest_size =crypto_shash_digestsize (ic->internal_hash); + digest_size = ic->internal_hash_digestsize; extra_size =unlikely (digest_size > ic->tag_size) ? digest_size - ic->tag_size :0 ; dio->payload_len += extra_size; dio->integrity_payload =kmalloc (dio->payload_len,GFP_NOIO | __GFP_NORETRY | __GFP_NOMEMALLOC | __GFP_NOWARN);
@@ -2590,7 +2591,7 @@ static void dm_integrity_inline_recheck(struct work_struct *w)bio_put (outgoing_bio);integrity_sector_checksum (ic, dio->bio_details.bi_iter.bi_sector, outgoing_data, digest); -if (unlikely (crypto_memneq (digest, dio->integrity_payload,min (crypto_shash_digestsize (ic->internal_hash), ic->tag_size)))) { +if (unlikely (crypto_memneq (digest, dio->integrity_payload,min (ic->internal_hash_digestsize, ic->tag_size)))) {DMERR_LIMIT ("%pg: Checksum failed at sector 0x%llx" , ic->dev->bdev, dio->bio_details.bi_iter.bi_sector);atomic64_inc (&ic->number_of_mismatches);
@@ -2630,7 +2631,7 @@ static int dm_integrity_end_io(struct dm_target *ti, struct bio *bio, blk_status//memset(mem, 0xff, ic->sectors_per_block << SECTOR_SHIFT); integrity_sector_checksum (ic, dio->bio_details.bi_iter.bi_sector, mem, digest);if (unlikely (crypto_memneq (digest, dio->integrity_payload + pos, -min (crypto_shash_digestsize (ic->internal_hash), ic->tag_size)))) { +min (ic->internal_hash_digestsize, ic->tag_size)))) {kunmap_local (mem);dm_integrity_free_payload (dio);INIT_WORK (&dio->work, dm_integrity_inline_recheck);
@@ -3012,8 +3013,8 @@ static void integrity_recalc(struct work_struct *w) goto free_ret; } recalc_tags_size = (recalc_sectors >> ic->sb->log2_sectors_per_block) * ic->tag_size; -if (crypto_shash_digestsize (ic->internal_hash) > ic->tag_size) - recalc_tags_size +=crypto_shash_digestsize (ic->internal_hash) - ic->tag_size; +if (ic->internal_hash_digestsize > ic->tag_size) + recalc_tags_size += ic->internal_hash_digestsize - ic->tag_size; recalc_tags =kvmalloc (recalc_tags_size,GFP_NOIO );if (!recalc_tags) {vfree (recalc_buffer);
@@ -3172,8 +3173,8 @@ static void integrity_recalc_inline(struct work_struct *w) } recalc_tags_size = (recalc_sectors >> ic->sb->log2_sectors_per_block) * ic->tuple_size; -if (crypto_shash_digestsize (ic->internal_hash) > ic->tuple_size) - recalc_tags_size +=crypto_shash_digestsize (ic->internal_hash) - ic->tuple_size; +if (ic->internal_hash_digestsize > ic->tuple_size) + recalc_tags_size += ic->internal_hash_digestsize - ic->tuple_size; recalc_tags =kmalloc (recalc_tags_size,GFP_NOIO | __GFP_NOWARN);if (!recalc_tags) {kfree (recalc_buffer);
@@ -4703,6 +4704,8 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv "Invalid internal hash", "Error setting internal hash key");if (r) goto bad; +if (ic->internal_hash) + ic->internal_hash_digestsize =crypto_shash_digestsize (ic->internal_hash); r =get_mac (&ic->journal_mac, &ic->journal_mac_alg, &ti->error,"Invalid journal mac" ,"Error setting journal mac key" );
@@ -4715,7 +4718,7 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv r = -EINVAL ; goto bad; } - ic->tag_size =crypto_shash_digestsize (ic->internal_hash); + ic->tag_size = ic->internal_hash_digestsize; }if (ic->tag_size >MAX_TAG_SIZE ) { ti->error ="Too big tag size" ;
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 2/3 dm-integrity: rename internal_hash
From: Mikulas Patocka <[email protected]>
[ Upstream commit e8a052ee1fb570b8e07f05f65752646eaac74355 ]
Rename "internal_hash" to "internal_shash" and introduce a boolean value "internal_hash".
Stable-dep-of: 764b683a6180 ("dm-integrity: fix buffer overflow in inline mode with large tag size")
--- drivers/md/dm-integrity.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-)
drivers/md/dm-integrity.c +11 -8
@@ -219,10 +219,11 @@ struct dm_integrity_c {__u8 log2_blocks_per_bitmap_bit;unsigned char mode; +bool internal_hash;int failed; -struct crypto_shash *internal_hash; +struct crypto_shash *internal_shash;unsigned int internal_hash_digestsize;struct dm_target *ti;
@@ -1635,11 +1636,11 @@ static void integrity_sector_checksum(struct dm_integrity_c *ic, sector_t sector const char *data, char *result) { __le64 sector_le = cpu_to_le64(sector); -SHASH_DESC_ON_STACK (req, ic->internal_hash); +SHASH_DESC_ON_STACK (req, ic->internal_shash);int r;unsigned int digest_size; - req->tfm = ic->internal_hash; + req->tfm = ic->internal_shash; r =crypto_shash_init (req);if (unlikely (r <0 )) {
@@ -4700,12 +4701,14 @@ static int dm_integrity_ctr(struct dm_target *ti, unsigned int argc, char **argv buffer_sectors =1 ; ic->log2_buffer_sectors =min ((int )__fls (buffer_sectors),31 -SECTOR_SHIFT ); - r =get_mac (&ic->internal_hash, &ic->internal_hash_alg, &ti->error, + r =get_mac (&ic->internal_shash, &ic->internal_hash_alg, &ti->error,"Invalid internal hash" ,"Error setting internal hash key" );if (r) goto bad; -if (ic->internal_hash) - ic->internal_hash_digestsize =crypto_shash_digestsize (ic->internal_hash); +if (ic->internal_shash) { + ic->internal_hash = true; + ic->internal_hash_digestsize =crypto_shash_digestsize (ic->internal_shash); + } r =get_mac (&ic->journal_mac, &ic->journal_mac_alg, &ti->error,"Invalid journal mac" ,"Error setting journal mac key" );
@@ -5239,8 +5242,8 @@ static void dm_integrity_dtr(struct dm_target *ti)if (ic->sb)free_pages_exact (ic->sb,SB_SECTORS <<SECTOR_SHIFT ); -if (ic->internal_hash) -crypto_free_shash (ic->internal_hash); +if (ic->internal_shash) +crypto_free_shash (ic->internal_shash);free_alg (&ic->internal_hash_alg);if (ic->journal_crypt)
Signature · 1 line
2.53.0
Sasha Levin
maintainerPATCH 3/3 dm-integrity: fix buffer overflow in inline mode with large tag size
In reply to Sasha LevinFrom: Ben Cressey <[email protected]>
[ Upstream commit 764b683a6180fd72c931c13fea98d383506a3475 ]
In inline mode, dm_integrity_check and dm_integrity_inline_recheck compute the checksum of each block into an on-stack buffer of HASH_MAX_DIGESTSIZE bytes. integrity_sector_checksum pads its result with zeroes up to the tag size, so if the tag size is larger than HASH_MAX_DIGESTSIZE, the padding runs past the end of the buffer. With CONFIG_STACKPROTECTOR the kernel panics in dm_integrity_check on the first read.
Enlarge both buffers to hold MAX_TAG_SIZE bytes, as commit b93b6643e9b5 ("dm integrity: fix a crash with unusually large tag size") did for integrity_metadata.
Note that this is not a security bug because the buffer overflow can only be triggered by root.
--- drivers/md/dm-integrity.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
drivers/md/dm-integrity.c +2 -2
@@ -2548,7 +2548,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;
@@ -2626,7 +2626,7 @@ static int dm_integrity_end_io(struct dm_target *ti, struct bio *bio, blk_status unlikely(dio->integrity_range_locked)) goto skip_check;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 =bvec_kmap_local (&bv);//memset(mem, 0xff, ic->sectors_per_block << SECTOR_SHIFT);
Signature · 1 line
2.53.0