On Sun, Jun 28, 2026 at 11:19 PM Samuel Moelius
<[email protected]> wrote:
>
> 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]>
> ---

The kstrndup is unnecessary. strsep() can split the range in place,
and kstrtouint() handles both u32 and u64 overflow in one call. No
manual range check or heap allocation is needed.

result->end for the single-value case can use the existing cblock_succ().

>  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