invalidate_cblocks parses cache block numbers with sscanf() and then stores them in the narrower dm_cblock_t type. Values larger than the cblock representation are truncated before invalidation, so a request for one cache block can invalidate a different block.
Checking the parsed value after sscanf() is not sufficient because sscanf() does not reliably reject values beyond U64_MAX before storing into the destination. Such inputs can still be converted to a wrapped u64 value and then pass a later range check. Split ranges in place and parse each single value or range endpoint directly with kstrtouint() instead. This rejects malformed values and values that do not fit in dm_cblock_t before they can be converted to cblock values. The existing range validation continues to reject empty or out-of-cache ranges, including the single-value U32_MAX case whose exclusive end wraps to zero. Assisted-by: Codex:gpt-5.5-cyber-preview Signed-off-by: Samuel Moelius <[email protected]> --- Changes in v2: - Now splits in place and parses directly as u32 drivers/md/dm-cache-target.c | 48 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 097315a9bf0f..5fb8b10c9b9e 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -16,6 +16,7 @@ #include <linux/dm-kcopyd.h> #include <linux/jiffies.h> #include <linux/init.h> +#include <linux/kstrtox.h> #include <linux/mempool.h> #include <linux/module.h> #include <linux/rwsem.h> @@ -3311,42 +3312,48 @@ struct cblock_range { dm_cblock_t end; }; +static inline dm_cblock_t cblock_succ(dm_cblock_t b) +{ + return to_cblock(from_cblock(b) + 1); +} + /* * A cache block range can take two forms: * * i) A single cblock, eg. '3456' * ii) A begin and end cblock with a dash between, eg. 123-234 */ -static int parse_cblock_range(struct cache *cache, const char *str, +static int parse_cblock_range(struct cache *cache, char *str, struct cblock_range *result) { - char dummy; - uint64_t b, e; + char *begin = strsep(&str, "-"); + unsigned int b, e; int r; - /* - * Try and parse form (ii) first. - */ - r = sscanf(str, "%llu-%llu%c", &b, &e, &dummy); + if (str) { + r = kstrtouint(begin, 10, &b); + if (r) + goto bad; + + r = kstrtouint(str, 10, &e); + if (r) + goto bad; - if (r == 2) { result->begin = to_cblock(b); result->end = to_cblock(e); return 0; } - /* - * That didn't work, try form (i). - */ - r = sscanf(str, "%llu%c", &b, &dummy); - - if (r == 1) { + r = kstrtouint(begin, 10, &b); + if (!r) { result->begin = to_cblock(b); - result->end = to_cblock(from_cblock(result->begin) + 1u); + result->end = cblock_succ(result->begin); return 0; } - DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), str); +bad: + DMERR("%s: invalid cblock range '%s%s%s'", cache_device_name(cache), + begin, str ? "-" : "", str ?: ""); return -EINVAL; } @@ -3377,11 +3384,6 @@ static int validate_cblock_range(struct cache *cache, struct cblock_range *range return 0; } -static inline dm_cblock_t cblock_succ(dm_cblock_t b) -{ - return to_cblock(from_cblock(b) + 1); -} - static int request_invalidation(struct cache *cache, struct cblock_range *range) { int r = 0; @@ -3405,7 +3407,7 @@ static int request_invalidation(struct cache *cache, struct cblock_range *range) } static int process_invalidate_cblocks_message(struct cache *cache, unsigned int count, - const char **cblock_ranges) + char **cblock_ranges) { int r = 0; unsigned int i; @@ -3460,7 +3462,7 @@ static int cache_message(struct dm_target *ti, unsigned int argc, char **argv, } if (!strcasecmp(argv[0], "invalidate_cblocks")) - return process_invalidate_cblocks_message(cache, argc - 1, (const char **) argv + 1); + return process_invalidate_cblocks_message(cache, argc - 1, argv + 1); if (argc != 2) return -EINVAL; -- 2.43.0

