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: [email protected]
Reviewed-by: Jose Fernandez (Anthropic) <[email protected]>
Signed-off-by: Ben Cressey <[email protected]>
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


Reply via email to