diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 537178df21d..4313376b3f7 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2855,6 +2855,15 @@ 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) { + 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(); const int var2 = tok->tokAt(varIndex + 2)->varId(); const auto alloc1 = utils::as_const(inconclusive).find(var1); 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); diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index c021d1d296b..47127463da6 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3741,6 +3741,27 @@ 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.isKnown()) + continue; + if (!value.tokvalue) + 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 = add ? newValue.intvalue + constant->intvalue : newValue.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) 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) diff --git a/test/testother.cpp b/test/testother.cpp index d5d6036718e..e25036dce12 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -10083,6 +10083,41 @@ 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()); + + 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()); + + 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() { 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()