From nobody Sat Jul 25 18:53:58 2026 Received: from mail-qk1-f169.google.com (mail-qk1-f169.google.com [209.85.222.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3023942BC45 for ; Wed, 15 Jul 2026 00:21:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784074913; cv=none; b=AesTUJtXHCsmlaqcKjlMPdiOePf7Y0GRHcKM+kQ6I4iSD0NfewYxsv0oRTckj4qR3IIq0fKnQBWowBgBVkLguIu1Rw8B8LOl4dl+aCoFJ6a+hGhUSTcSqmFomhCVKm53wiDjQ2AJsKhBETNjM7J5/u212pci0PrC4lhDm0ZihI8= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784074913; c=relaxed/simple; bh=+Ej0yWAkIzxg6jR263cUw7NoNpTqTV4Jf2uhAV0+bSo=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=MFOx1/SUrM6uHvtk6zAyUsIB3LKwm+5oKrF3peyLiwu9taaSUuMEY54CcX/A9eeSXwXMEKh9gI3GgyuzL6BL53EY0Iz5oPrRmVULtHa5moQGVkco33So4AZuU3HB28X8MMI6cBXbza9kOHZJ0fZmX7ZcOWQ3+Un6FqGeM9Z5s0M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=trailofbits.com; spf=pass smtp.mailfrom=trailofbits.com; dkim=pass (2048-bit key) header.d=trailofbits.com header.i=@trailofbits.com header.b=Qzy9BO6p; arc=none smtp.client-ip=209.85.222.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=trailofbits.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=trailofbits.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=trailofbits.com header.i=@trailofbits.com header.b="Qzy9BO6p" Received: by mail-qk1-f169.google.com with SMTP id af79cd13be357-92e85499ffbso120003085a.0 for ; Tue, 14 Jul 2026 17:21:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=trailofbits.com; s=google; t=1784074911; x=1784679711; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=RCq2iFN/h3l5vh2Le11+ucgCQwP94+t4rJW/GMr3YAs=; b=Qzy9BO6p038fk+H2S2XcBDhRDsKDLWPnwN8GIJz4DkeXNIcVfexB1RZ5EqVG+gD3OY Lf4MHaSVW8XilY6sDKfG+Ff71s1lCuK5TdEa87iODfTKaZXvlTEoFAnldV5EUCXxincN oHRNu6gNJwjodkXqW9w8DGIzoC7umYI3drq/w2n3VNsbkUnxnxVh+lXMXCJXXq7JGkef OTQ4IfYma7K1wl3aBc9/wStQNSTDnyg/iVJTSNm+jlcb2Ifz5lEHXJJrcm0RgxcbNBRw NacNMzvZVFFxx5NlFtC88akyy6HNTWkTvU/6UFLoJmVFcOJMZzXUdB/xM/7WvC/nTCr+ S2Pw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784074911; x=1784679711; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=RCq2iFN/h3l5vh2Le11+ucgCQwP94+t4rJW/GMr3YAs=; b=mAhvNv2RaiXQGNr9PgOGV8ZanHoF5aXM61/ja1PqTG0TOHtoM/h0fcGB9MS/jI4eGb Vd9RREjYcxPnm5rqNDYkHmLI30zaKQO7t6uuUmilpPFBz/h4QLKlCfK9BdAKIOCdiNal HzmhwkMImssorWwilHjvqry56UnQSuNmQN5ojv0QWetz77TixGO64zdp1Wp6MSK3aaGL WxUvDcPL3w/oQxnl2OAMUsSuMUsqhN72yjEQAqPYZKEuivFXzit00GE+N+Q7/6r2WoHA y/2XLj/3sjFETNLAULTY7hAj4sdbMEEOppHZMU5+uX8Q1Pc9JoksZlo3yG9SWSzVmCkx 0pfQ== X-Forwarded-Encrypted: i=1; AHgh+Rq3ssS3rz0LM/QtioqpRvASivb2MGeW8u6RMru7ZzHcUxPN8rUbHtC1Z/d+4evjf82i8lhj1UdKmRqiqwc=@vger.kernel.org X-Gm-Message-State: AOJu0Yy7NjQhEHyJbnmNVG/JVQeXGh2n1LunZTAUYMxmEfBBqx71SlAl r7ebt4WA1hU54TpSB3AAcBQ3v4gx8eiaOUhbIn5wpw7eqponoUqTFHYreY0B46x+Ybs= X-Gm-Gg: AfdE7cmOzz7TqLq1K36DA7i67LC1x5hk1NJk0QXiCNcMOrhYai0yxz+CNHNurwrmxyG Hr1/1xsIaQJOHo5fy+S9Qhuh4/emA1Om9l2AnqjfezsSALdvKIbm/NlwbWiIkw56r3K+SPzhZre ded19QOwtjZHPdlIDCQSOk6lSH9GmjmaoU+LupH5VudQkukb69CVSyMc4RdEmH9Mh5IEJgLftTb I7VISx9jdYw0J1T99SVr+AMZwnGXfrIgi1vw2yVtEKXR91i0pOfmjALtQ3Mjxfa7oJBUtJl9TY0 rgHHIwyuRrLvJiOgtP3iHWpz+W5Py8Z3T9AXBRgI/9hqOqiQYvv9tqbr8w+d7pkK+fV/xhZOO3w uRzCQTTwuOcmB3lsokTrPldp19ZuXAd2qEq6X4IFxN3XiKR0UY/YtiW8id6Rw8Dk3QQ+5TaX+dH nc4g/W0bQSIXzvWqNH X-Received: by 2002:a05:620a:4505:b0:91c:ac0a:690b with SMTP id af79cd13be357-92ef2aec978mr1500667885a.17.1784074910958; Tue, 14 Jul 2026 17:21:50 -0700 (PDT) Received: from localhost ([161.35.96.86]) by smtp.gmail.com with UTF8SMTPSA id af79cd13be357-92ee5b4a0c0sm1704368685a.6.2026.07.14.17.21.49 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Tue, 14 Jul 2026 17:21:50 -0700 (PDT) From: Samuel Moelius To: Alasdair Kergon Cc: Samuel Moelius , Mike Snitzer , Mikulas Patocka , Benjamin Marzinski , dm-devel@lists.linux.dev (open list:DEVICE-MAPPER (LVM)), linux-kernel@vger.kernel.org (open list) Subject: [PATCH v4] dm cache: parse invalidate_cblocks with kstrtouint() Date: Wed, 15 Jul 2026 00:18:00 +0000 Message-ID: <20260715001755.123939.f196d094e808.dm-cache-invalidate-cblock-truncation@trailofbits.com> X-Mailer: git-send-email 2.43.0 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 Reviewed-by: Ming-Hung Tsai --- Changes in v4: - Make range vs. single-value case differ only in how result->end is computed Changes in v3: - Parse begin once - Keep happy path unindented - Print rejected token when parsing fails Changes in v2: - Now splits in place and parses directly as u32 drivers/md/dm-cache-target.c | 56 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/md/dm-cache-target.c b/drivers/md/dm-cache-target.c index 097315a9bf0f..33dbc71b730f 100644 --- a/drivers/md/dm-cache-target.c +++ b/drivers/md/dm-cache-target.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -3311,42 +3312,46 @@ struct cblock_range { dm_cblock_t end; }; =20 +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 *blocknr =3D strsep(&str, "-"); + unsigned int b, e; int r; =20 - /* - * Try and parse form (ii) first. - */ - r =3D sscanf(str, "%llu-%llu%c", &b, &e, &dummy); + r =3D kstrtouint(blocknr, 10, &b); + if (r) + goto bad; =20 - if (r =3D=3D 2) { - result->begin =3D to_cblock(b); - result->end =3D to_cblock(e); - return 0; - } + result->begin =3D to_cblock(b); =20 - /* - * That didn't work, try form (i). - */ - r =3D sscanf(str, "%llu%c", &b, &dummy); + if (str) { + blocknr =3D str; =20 - if (r =3D=3D 1) { - result->begin =3D to_cblock(b); - result->end =3D to_cblock(from_cblock(result->begin) + 1u); - return 0; + r =3D kstrtouint(blocknr, 10, &e); + if (r) + goto bad; + + result->end =3D to_cblock(e); + } else { + result->end =3D cblock_succ(result->begin); } =20 - DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), str); + return 0; + +bad: + DMERR("%s: invalid cblock range '%s'", cache_device_name(cache), blocknr); return -EINVAL; } =20 @@ -3377,11 +3382,6 @@ static int validate_cblock_range(struct cache *cache= , struct cblock_range *range return 0; } =20 -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 =3D 0; @@ -3405,7 +3405,7 @@ static int request_invalidation(struct cache *cache, = struct cblock_range *range) } =20 static int process_invalidate_cblocks_message(struct cache *cache, unsigne= d int count, - const char **cblock_ranges) + char **cblock_ranges) { int r =3D 0; unsigned int i; @@ -3460,7 +3460,7 @@ static int cache_message(struct dm_target *ti, unsign= ed int argc, char **argv, } =20 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); =20 if (argc !=3D 2) return -EINVAL; --=20 2.43.0