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.

Parse each single value or range endpoint with kstrtoull() instead, and
reject values that do not fit in dm_cblock_t before converting them 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]>
---
 drivers/md/dm-cache-target.c | 48 +++++++++++++++++++++++++++---------
 1 file changed, 36 insertions(+), 12 deletions(-)

diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c
index 097315a9bf0f..35c1eea48116 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,6 +3312,26 @@ struct cblock_range {
        dm_cblock_t end;
 };
 
+static int parse_cblock(const char *str, size_t len, uint64_t *result)
+{
+       char *buf;
+       int r;
+
+       if (!len)
+               return -EINVAL;
+
+       buf = kstrndup(str, len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       r = kstrtoull(buf, 10, result);
+       kfree(buf);
+       if (r)
+               return r;
+
+       return *result > U32_MAX ? -EINVAL : 0;
+}
+
 /*
  * A cache block range can take two forms:
  *
@@ -3320,32 +3341,35 @@ struct cblock_range {
 static int parse_cblock_range(struct cache *cache, const char *str,
                              struct cblock_range *result)
 {
-       char dummy;
+       const char *dash = strchr(str, '-');
        uint64_t b, e;
        int r;
 
-       /*
-        * Try and parse form (ii) first.
-        */
-       r = sscanf(str, "%llu-%llu%c", &b, &e, &dummy);
+       if (dash) {
+               r = parse_cblock(str, dash - str, &b);
+               if (r)
+                       goto bad;
+
+               r = parse_cblock(dash + 1, strlen(dash + 1), &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 = parse_cblock(str, strlen(str), &b);
+       if (!r) {
                result->begin = to_cblock(b);
                result->end = to_cblock(from_cblock(result->begin) + 1u);
                return 0;
        }
 
+bad:
+       if (r == -ENOMEM)
+               return r;
+
        DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), str);
        return -EINVAL;
 }
-- 
2.43.0


Reply via email to