Skip to content
9 changes: 9 additions & 0 deletions lib/checkother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Comment thread
ludviggunne marked this conversation as resolved.
}))
continue;
Comment thread
ludviggunne marked this conversation as resolved.
const int var1 = tok->tokAt(varIndex)->varId();
const int var2 = tok->tokAt(varIndex + 2)->varId();
const auto alloc1 = utils::as_const(inconclusive).find(var1);
Expand Down
14 changes: 14 additions & 0 deletions lib/mathlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<MathLib::bigint>::max() - y;
return std::numeric_limits<MathLib::bigint>::min() - y > x;
}

bool MathLib::subtractionWouldOverflow(MathLib::bigint x, MathLib::bigint y)
{
if (y == std::numeric_limits<MathLib::bigint>::min())
return x >= 0;
return additionWouldOverflow(x, -y);
}
3 changes: 3 additions & 0 deletions lib/mathlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 28 additions & 0 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3741,6 +3741,34 @@ static void valueFlowSymbolicOperators(const SymbolDatabase& symboldatabase, con
continue;
if (Token::Match(tok, "<<|>>|/|-") && !astIsLHS(vartok))
continue;
if (Token::Match(tok, "+|-") && constant->intvalue != 0) {
Comment thread
ludviggunne marked this conversation as resolved.
const ValueType *varVt = vartok->valueType();
const ValueType *tokVt = tok->valueType();
if (varVt && tokVt &&
varVt->sign == tokVt->sign &&
varVt->type == tokVt->type &&
varVt->pointer == tokVt->pointer) {
std::unordered_set<nonneg int> 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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

For unsigned non-pointer operands this offset is only correct modulo 2^N. The condition code still uses it for ordering, which gives new false positives. Example: size_t i = n; if (i - 1 < n) is now reported as Condition 'i-1<n' is always true, but the condition is false for n == 0. Main reports nothing. unsigned u = a; if (u + 1 > a) has the same problem: it is reported as always true, but it is false for a == UINT_MAX. Unless the value can be marked as valid only for equality, consider skipping this when varVt->pointer == 0 && varVt->sign == ValueType::Sign::UNSIGNED. Pointers (the invalidFree case) and signed integers are fine, because overflow there is UB.

}
}
}
if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0)
continue;
if (Token::Match(tok, "*|/") && constant->intvalue != 1)
Expand Down
24 changes: 24 additions & 0 deletions test/testcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ class TestCondition : public TestFixture {
TEST_CASE(knownConditionAfterBailout); // #12526
TEST_CASE(knownConditionIncDecOperator);
TEST_CASE(knownConditionFloating);
TEST_CASE(knownConditionPromotion);
TEST_CASE(knownConditionWiderConstant);
}

struct CheckOptions
Expand Down Expand Up @@ -6904,6 +6906,28 @@ class TestCondition : public TestFixture {
"",
errout_str());
}

void knownConditionPromotion() {
check("int wrapped(uint8_t last) {\n"
" uint8_t seq = last;\n"
" seq++;\n"
" if (seq - 1 != last)\n"
" return 1;\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}

void knownConditionWiderConstant() {
check("int wrapped(unsigned int last) {\n"
" unsigned int seq = last;\n"
" seq++;\n"
" if (seq - 1UL != last)\n"
" return 1;\n"
" return 0;\n"
"}\n");
ASSERT_EQUALS("", errout_str());
}
};

REGISTER_TEST(TestCondition)
30 changes: 30 additions & 0 deletions test/testmathlib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1511,6 +1513,34 @@ class TestMathLib : public TestFixture {
ASSERT_EQUALS("2.22507385851e-308", MathLib::toString(std::numeric_limits<double>::min()));
ASSERT_EQUALS("1.79769313486e+308", MathLib::toString(std::numeric_limits<double>::max()));
}

void additionWouldOverflow() const {
const auto max = std::numeric_limits<MathLib::bigint>::max();
const auto min = std::numeric_limits<MathLib::bigint>::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<MathLib::bigint>::max();
const auto min = std::numeric_limits<MathLib::bigint>::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)
35 changes: 35 additions & 0 deletions test/testother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
45 changes: 45 additions & 0 deletions test/testvalueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading