From 5049fe0804b950dd1363450d6e35429ba5db0b63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 21 Sep 2026 09:23:16 +0200 Subject: [PATCH 01/10] test --- test/testother.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index d5d6036718e..286510213f8 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -10083,6 +10083,22 @@ class TestOther : public TestFixture { ASSERT_EQUALS( "[test.cpp:2:9]: (style) Variable 'ptr' can be declared as pointer to const [constVariablePointer]\n", errout_str()); + + check("void f() {\n" + " char *p = new char[1];\n" + " ++p;\n" + " delete [] (p - 1);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:11]: (style) Variable 'p' can be declared as pointer to const [constVariablePointer]\n", + errout_str()); + + check("void f() {\n" + " char *p = new char[1];\n" + " delete [] (p - 1);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:11]: (style) Variable 'p' can be declared as pointer to const [constVariablePointer]\n" + "[test.cpp:3:5]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset. [invalidFree]\n" + , errout_str()); } void checkRedundantCopy() { From 9932a2d807766bd33a7f119485284c5c2bd36791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 21 Sep 2026 09:23:20 +0200 Subject: [PATCH 02/10] fix --- lib/checkother.cpp | 7 +++++++ lib/valueflow.cpp | 14 ++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 537178df21d..dda29ddf92a 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2855,6 +2855,13 @@ void CheckOtherImpl::checkInvalidFree() const int varIndex = tok->strAt(1) == "(" ? 2 : tok->strAt(3) == "(" ? 4 : 1; + const Token *op = tok->tokAt(varIndex + 1); + if (std::any_of(op->values().cbegin(), + op->values().cend(), + [&](const ValueFlow::Value &value) { + return value.isSymbolicValue() && value.intvalue == 0; + })) + continue; const int var1 = tok->tokAt(varIndex)->varId(); const int var2 = tok->tokAt(varIndex + 2)->varId(); const auto alloc1 = utils::as_const(inconclusive).find(var1); diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index c021d1d296b..f96d3aee676 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3741,6 +3741,20 @@ static void valueFlowSymbolicOperators(const SymbolDatabase& symboldatabase, con continue; if (Token::Match(tok, "<<|>>|/|-") && !astIsLHS(vartok)) continue; + if (Token::Match(tok, "+|-") && constant->intvalue != 0) { + std::unordered_set ids; + for (const auto &value : vartok->values()) { + if (!value.isSymbolicValue()) + continue; + if (!value.tokvalue) + continue; + if (!ids.insert(value.tokvalue->exprId()).second) + continue; + ValueFlow::Value newValue(value); + newValue.intvalue += tok->str() == "-" ? -constant->intvalue : constant->intvalue; + setTokenValue(tok, std::move(newValue), settings); + } + } if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0) continue; if (Token::Match(tok, "*|/") && constant->intvalue != 1) From 457bc1f4b22dc7f2683d9829192700cfb418365e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 1 Oct 2026 12:44:40 +0200 Subject: [PATCH 03/10] new test --- test/testother.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 286510213f8..04cd69a22b7 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -10099,6 +10099,16 @@ class TestOther : public TestFixture { ASSERT_EQUALS("[test.cpp:2:11]: (style) Variable 'p' can be declared as pointer to const [constVariablePointer]\n" "[test.cpp:3:5]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset. [invalidFree]\n" , errout_str()); + + check("void f() {\n" + " char *p = new char[2];\n" + " char *q = p - 1;\n" + " delete [] (p - 1);\n" + " (void)q;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2:11]: (style) Variable 'p' can be declared as pointer to const [constVariablePointer]\n" + "[test.cpp:4:5]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset. [invalidFree]\n", + errout_str()); } void checkRedundantCopy() { From 060a665ab0a028a216d6a71a7db4251826593d14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Thu, 1 Oct 2026 12:44:46 +0200 Subject: [PATCH 04/10] update --- lib/checkother.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index dda29ddf92a..4313376b3f7 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2859,7 +2859,9 @@ void CheckOtherImpl::checkInvalidFree() if (std::any_of(op->values().cbegin(), op->values().cend(), [&](const ValueFlow::Value &value) { - return value.isSymbolicValue() && value.intvalue == 0; + if (!value.isSymbolicValue() || !value.isKnown() || value.intvalue != 0 || !value.tokvalue) + return false; + return value.tokvalue->str() == "new"; })) continue; const int var1 = tok->tokAt(varIndex)->varId(); From a32cd614a74e6f4a4b8d8ea186c54f42b9154fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 5 Oct 2026 09:31:15 +0200 Subject: [PATCH 05/10] add todo --- test/testother.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 04cd69a22b7..e25036dce12 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -10109,6 +10109,15 @@ class TestOther : public TestFixture { ASSERT_EQUALS("[test.cpp:2:11]: (style) Variable 'p' can be declared as pointer to const [constVariablePointer]\n" "[test.cpp:4:5]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset. [invalidFree]\n", errout_str()); + + check("void f() {\n" + " char *p = malloc(1);\n" + " ++p;\n" + " free(p - 1);\n" + "}\n"); + TODO_ASSERT_EQUALS("", + "[test.cpp:4:5]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset. [invalidFree]\n", + errout_str()); } void checkRedundantCopy() { From 192c2ee96dba9a5aab9e8b6145d8ced5da5e239f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 12 Oct 2026 08:45:29 +0200 Subject: [PATCH 06/10] restrict to known values --- lib/valueflow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index f96d3aee676..32bd537d035 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3746,6 +3746,8 @@ static void valueFlowSymbolicOperators(const SymbolDatabase& symboldatabase, con for (const auto &value : vartok->values()) { if (!value.isSymbolicValue()) continue; + if (!value.isKnown()) + continue; if (!value.tokvalue) continue; if (!ids.insert(value.tokvalue->exprId()).second) From 7be74302baf809ebd0a2479139e4d023c6d473c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 12 Oct 2026 09:59:26 +0200 Subject: [PATCH 07/10] add MathLib::{addition,subtraction}WouldOverflow --- lib/mathlib.cpp | 14 ++++++++++++++ lib/mathlib.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index c7c055899fd..f444577e2a4 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -1369,3 +1369,17 @@ MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2) { return v1.shiftRight(v2); } + +bool MathLib::additionWouldOverflow(MathLib::bigint x, MathLib::bigint y) +{ + if (y > 0) + return x > std::numeric_limits::max() - y; + return std::numeric_limits::min() - y > x; +} + +bool MathLib::subtractionWouldOverflow(MathLib::bigint x, MathLib::bigint y) +{ + if (y == std::numeric_limits::min()) + return x >= 0; + return additionWouldOverflow(x, -y); +} diff --git a/lib/mathlib.h b/lib/mathlib.h index 84338b5ce70..55f3cb074b4 100644 --- a/lib/mathlib.h +++ b/lib/mathlib.h @@ -169,6 +169,9 @@ class CPPCHECKLIB MathLib { static bool isOctalDigit(char c); static unsigned int encodeMultiChar(const std::string& str); + + static bool additionWouldOverflow(MathLib::bigint x, MathLib::bigint y); + static bool subtractionWouldOverflow(MathLib::bigint x, MathLib::bigint y); }; MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2); From e8ebeeea2515aff714eb90ca7a10a1b6eaf286b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 12 Oct 2026 10:10:11 +0200 Subject: [PATCH 08/10] add mathlib tests --- test/testmathlib.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index 8cbe5b50019..9f9e9107ef3 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -64,6 +64,8 @@ class TestMathLib : public TestFixture { TEST_CASE(tan); TEST_CASE(abs); TEST_CASE(toString); + TEST_CASE(additionWouldOverflow); + TEST_CASE(subtractionWouldOverflow); } void isGreater() const { @@ -1511,6 +1513,34 @@ class TestMathLib : public TestFixture { ASSERT_EQUALS("2.22507385851e-308", MathLib::toString(std::numeric_limits::min())); ASSERT_EQUALS("1.79769313486e+308", MathLib::toString(std::numeric_limits::max())); } + + void additionWouldOverflow() const { + const auto max = std::numeric_limits::max(); + const auto min = std::numeric_limits::min(); + + ASSERT_EQUALS(false, MathLib::additionWouldOverflow(1, 2)); + ASSERT_EQUALS(false, MathLib::additionWouldOverflow(1, -2)); + ASSERT_EQUALS(false, MathLib::additionWouldOverflow(max - 1, 1)); + ASSERT_EQUALS(true, MathLib::additionWouldOverflow(max - 1, 2)); + ASSERT_EQUALS(false, MathLib::additionWouldOverflow(0, min)); + ASSERT_EQUALS(true, MathLib::additionWouldOverflow(-1, min)); + ASSERT_EQUALS(false, MathLib::additionWouldOverflow(min + 1, -1)); + ASSERT_EQUALS(true, MathLib::additionWouldOverflow(min, -1)); + } + + void subtractionWouldOverflow() const { + const auto max = std::numeric_limits::max(); + const auto min = std::numeric_limits::min(); + + ASSERT_EQUALS(false, MathLib::subtractionWouldOverflow(1, 2)); + ASSERT_EQUALS(false, MathLib::subtractionWouldOverflow(1, -2)); + ASSERT_EQUALS(false, MathLib::subtractionWouldOverflow(min, 0)); + ASSERT_EQUALS(true, MathLib::subtractionWouldOverflow(min, 1)); + ASSERT_EQUALS(false, MathLib::subtractionWouldOverflow(max - 1, -1)); + ASSERT_EQUALS(true, MathLib::subtractionWouldOverflow(max, -1)); + ASSERT_EQUALS(true, MathLib::subtractionWouldOverflow(0, min)); + ASSERT_EQUALS(false, MathLib::subtractionWouldOverflow(-1, min)); + } }; REGISTER_TEST(TestMathLib) From cd85f60d2f35390ff764e8479e1d3a1dda5ac70d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 12 Oct 2026 11:06:16 +0200 Subject: [PATCH 09/10] bail on overflow --- lib/valueflow.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 32bd537d035..47127463da6 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3752,8 +3752,13 @@ static void valueFlowSymbolicOperators(const SymbolDatabase& symboldatabase, con continue; if (!ids.insert(value.tokvalue->exprId()).second) continue; + const bool add = tok->str() == "+"; + if (add && MathLib::additionWouldOverflow(value.intvalue, constant->intvalue)) + continue; + if (!add && MathLib::subtractionWouldOverflow(value.intvalue, constant->intvalue)) + continue; ValueFlow::Value newValue(value); - newValue.intvalue += tok->str() == "-" ? -constant->intvalue : constant->intvalue; + newValue.intvalue = add ? newValue.intvalue + constant->intvalue : newValue.intvalue - constant->intvalue; setTokenValue(tok, std::move(newValue), settings); } } From ff2b8554164337a179b9481bc78e771ab40401df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 12 Oct 2026 11:06:25 +0200 Subject: [PATCH 10/10] add valueflow tests --- test/testvalueflow.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index f84f0ea0d89..1eedf9628d7 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -9434,6 +9434,51 @@ class TestValueFlow : public TestFixture { " int a[3];\n" "};\n"; ASSERT(tokenValues(code, "* p )").empty()); + + code = "int f(int i) {\n" + " int j = i;\n" + " i++;\n" + " int x = i - 1;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, "j", 0)); + + code = "int f(int i) {\n" + " int j = i;\n" + " i++;\n" + " int x = i + 2;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, "j", 3)); + + code = "int f(int i) {\n" + " int j = i;\n" + " i++;\n" + " int x = 2 + i;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, "j", 3)); + + code = "int *f(int *q) {\n" + " int *p = q;\n" + " p += 2;\n" + " int *x = p - 2;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, "q", 0)); + + if (sizeof(MathLib::bigint) == 8) { + code = "long long f(long long i) {\n" + " long long j = i;\n" + " i++;\n" + " long long a = i + 0x7fffffffffffffffLL;\n" + " long long x = i - 2;\n" + " return a + x;\n" + "}\n"; + ASSERT_EQUALS(0U, tokenValues(code, "+ 0x7fffffffffffffffLL").size()); + ASSERT_EQUALS(1U, tokenValues(code, "- 2").size()); + ASSERT_EQUALS(true, testValueOfXKnown(code, 6U, "j", -1)); + } } void valueFlowSymbolicIdentity()