On 8/20/26 23:44, Ben Cressey wrote:
Since commit 68c5c42567bc ("dm-integrity: replace forgeable discard
filler with a keyed sector marker"), integrity_metadata computes a
checksum for every discarded block into the "checksums" buffer.
integrity_sector_checksum always writes the whole digest. So if the tag
size is smaller than the digest size, the checksum of the last block
that fits into the buffer is written past the end of it. For example,
with hmac(sha256) and tag size 16, a 4MiB discard writes 16 bytes past
the kmalloc'ed page.

Hi,

This initially got stuck in spam somehow. Reviewed this together with Shukai. Agreed this is an oversight in the original patch for the (unlikely) case where tag_size < digest_size.
Fix this by subtracting extra_space from the buffer size when computing
max_blocks, like we do for writes.

The code path is quite convoluted, but looks OK to us (and arguably more elegant/efficient than the alternatives, which would allocate digest_size local variables or allocate extra_space bytes for every page-sized buffer allocation).

Fixes: 68c5c42567bc ("dm-integrity: replace forgeable discard filler with a keyed 
sector marker")
Reviewed-by: Jose Fernandez (Anthropic) <[email protected]>
Signed-off-by: Ben Cressey <[email protected]>
Assisted-by: Claude:unspecified
---
Found with KASAN on current mainline (after the 7.3 dm merge): brd,
"0 <n> integrity /dev/ram0 0 16 J 2 internal_hash:hmac(sha256):<key>
allow_discards_keyed", 8MiB written, then BLKDISCARD of 4MiB:

   BUG: KASAN: slab-out-of-bounds in __hmac_sha256_final+0x1be/0x1e0
   Write of size 4 at addr ff11000104b05000 by task kworker/0:0/9
   Workqueue: dm-integrity-offload integrity_bio_wait
    integrity_sector_checksum_shash+0x181/0x490
    integrity_metadata+0x101e/0x18a0
    dm_integrity_map_continue+0x1eb2/0x34d0
   The buggy address is located 0 bytes to the right of
    allocated 4096-byte region
---
  drivers/md/dm-integrity.c | 2 +-
  1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/md/dm-integrity.c b/drivers/md/dm-integrity.c
index c50feaa98bf9e..73c1db7e55d5c 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1980,7 +1980,7 @@ static void integrity_metadata(struct work_struct *w)
                if (unlikely(dio->op == REQ_OP_DISCARD)) {
                        unsigned int bi_size = dio->bio_details.bi_iter.bi_size;
                        unsigned int max_size = likely(checksums != 
checksums_onstack) ? PAGE_SIZE : HASH_MAX_DIGESTSIZE;
-                       unsigned int max_blocks = max_size / ic->tag_size;
+                       unsigned int max_blocks = (max_size - extra_space) / 
ic->tag_size;
                        sector_t sector = dio->range.logical_sector;
if (!ic->discard_keyed)



Reply via email to