too_many_discard_blocks() uses sector_div(), whose divisor is a u32.
discard_block_size is a 64-bit sector_t that calculate_discard_block_size()
keeps doubling, so once it reaches a multiple of 2^32 the divisor is
truncated to 0 and the division traps with a divide error. Smaller values
above 2^32 are also silently truncated, giving a wrong quotient.

This needs an origin of more than 2^45 sectors, which is unrealistic (this
is a thing still in 2026!!) for a real device but reachable with a fuzzer.

Switching the div operation to one that uses 64 bits divisor would cause 
regression,
so we just limit the value of cache->discard_block_size to U32_MAX.

Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=3e16980543e238b95bd1
Fixes: 08b184514f65 (" dm cache: revert "prevent corruption caused by 
discard_block_size > cache_block_size" ")
Tested-by: [email protected]
Cc: [email protected]
Signed-off-by: Drif Abdelmalek Mohamed Said 
<[email protected]>
---
 drivers/md/dm-cache-target.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 1a5072425c4a..b51ceca60afc 100644
--- a/drivers/md/dm-cache-target.c
+++ b/drivers/md/dm-cache-target.c
@@ -2383,6 +2383,8 @@ static int create_cache_policy(struct cache *cache, 
struct cache_args *ca,
 static bool too_many_discard_blocks(sector_t discard_block_size,
                                    sector_t origin_size)
 {
+       if (discard_block_size > U32_MAX / 2)
+               return false;
        (void) sector_div(origin_size, discard_block_size);
 
        return origin_size > MAX_DISCARD_BLOCKS;
-- 
2.47.3


Reply via email to