[PATCH 2/2] dm-integrity: fix infinite loop on discard with large tag size
From: Ben Cressey
Date: Thu Aug 20 2026 - 17:47:03 EST
When integrity_metadata handles a discard, it fills a buffer with
DISCARD_FILLER and writes it over the tags, max_blocks blocks at a
time. If the kmalloc fails, the buffer is the on-stack array
checksums_onstack and max_size is set to HASH_MAX_DIGESTSIZE. So if the
tag size is larger than HASH_MAX_DIGESTSIZE, max_blocks is zero, bi_size
is never decremented and the loop never terminates.
Fix this by using sizeof(checksums_onstack) as max_size. The array has
MAX_TAG_SIZE bytes since commit b93b6643e9b5 ("dm integrity: fix a
crash with unusually large tag size"), so max_blocks is at least 1.
Fixes: 84597a44a9d8 ("dm integrity: add optional discard support")
Cc: stable@xxxxxxxxxxxxxxx
Reviewed-by: Jose Fernandez (Anthropic) <jose.fernandez@xxxxxxxxx>
Signed-off-by: Ben Cressey <ben@xxxxxxxxxxx>
Assisted-by: Claude:unspecified
---
The natural trigger needs the kmalloc to fail, so this was tested with
the fallback forced (a test-only hunk that frees the buffer for
discards): J mode, internal_hash:sha256, tag_size 100, allow_discards,
BLKDISCARD of 16 MiB. Without the fix the discard never completes and a
dm-integrity-offload kworker spins in integrity_metadata ->
dm_integrity_rw_tag (soft lockup with preempt=none); with the fix it
completes immediately. This goes after 1/2; on its own it would let the
keyed-discard fallback overflow checksums_onstack for tag sizes below
the digest size.
For stable: 1/2 is not stable material and it changes the context line
right above this hunk, so this patch will not apply verbatim to released
trees. The backport is the same one-line change; I will send it in
reply to the failed-to-apply notice.
---
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 73c1db7e55d5c..48eca96ca6d8c 100644
--- a/drivers/md/dm-integrity.c
+++ b/drivers/md/dm-integrity.c
@@ -1979,7 +1979,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_size = likely(checksums != checksums_onstack) ? PAGE_SIZE : sizeof(checksums_onstack);
unsigned int max_blocks = (max_size - extra_space) / ic->tag_size;
sector_t sector = dio->range.logical_sector;
--
2.53.0