Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue 9979: false positive: containerOutOfBounds with conditional resize #3136

Merged
merged 27 commits into from
Mar 30, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
82810ed
Fix issue 9979: false positive: containerOutOfBounds with conditional…
pfultz2 Feb 18, 2021
e1375b8
Remove commented out code
pfultz2 Feb 18, 2021
bc9ebee
Add more tests
pfultz2 Feb 18, 2021
2a3e9ba
Remove adjacent values
pfultz2 Feb 19, 2021
b0f298b
Dont truncate impossible values
pfultz2 Feb 19, 2021
07ee205
Fix some tests
pfultz2 Feb 19, 2021
75e8916
Fix bug in removing contradictions
pfultz2 Feb 19, 2021
d7d4c44
Fix more bugs in removing contradictions
pfultz2 Feb 20, 2021
56a1408
Skip literals
pfultz2 Feb 20, 2021
c9a2d1a
Flip assert
pfultz2 Feb 20, 2021
680a4c6
Fix valueflow tests
pfultz2 Feb 20, 2021
64e3b01
Dont set literals
pfultz2 Feb 20, 2021
31b7067
Format
pfultz2 Feb 20, 2021
9a22bc8
Remove unnecessary headers
pfultz2 Feb 20, 2021
0ef3651
Fix uninitialized variables
pfultz2 Feb 22, 2021
649cb59
Merge
pfultz2 Feb 22, 2021
a53726d
Prevent UB in calculate function
pfultz2 Feb 22, 2021
64fd616
Fix cppcheck warning
pfultz2 Feb 22, 2021
305f03a
Merge branch 'main' into valueflow-infer-unsigned-compare
pfultz2 Feb 24, 2021
ad89530
Check value type
pfultz2 Feb 24, 2021
65f4519
Format
pfultz2 Feb 24, 2021
87e38aa
Merge branch 'main' into valueflow-infer-unsigned-compare
pfultz2 Feb 25, 2021
b3af86c
Make test todo
pfultz2 Feb 28, 2021
62d7377
Merge branch 'main' into valueflow-infer-unsigned-compare
pfultz2 Mar 1, 2021
0377a41
Remove else
pfultz2 Mar 30, 2021
c8fcd31
Merge branch 'main' into valueflow-infer-unsigned-compare
pfultz2 Mar 30, 2021
0ccd789
Rerun dmake
pfultz2 Mar 30, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix issue 9979: false positive: containerOutOfBounds with conditional…
… resize
  • Loading branch information
pfultz2 committed Feb 18, 2021
commit 82810edfb0c060340a0c07641ec548299526dcc8
42 changes: 28 additions & 14 deletions lib/programmemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "mathlib.h"
#include "symboldatabase.h"
#include "token.h"
#include "valueflow.h"
#include <algorithm>
#include <cassert>
#include <cstdio>
Expand Down Expand Up @@ -424,20 +425,33 @@ void execute(const Token *expr,

else if (expr->isComparisonOp()) {
MathLib::bigint result1(0), result2(0);
execute(expr->astOperand1(), programMemory, &result1, error);
execute(expr->astOperand2(), programMemory, &result2, error);
if (expr->str() == "<")
*result = result1 < result2;
else if (expr->str() == "<=")
*result = result1 <= result2;
else if (expr->str() == ">")
*result = result1 > result2;
else if (expr->str() == ">=")
*result = result1 >= result2;
else if (expr->str() == "==")
*result = result1 == result2;
else if (expr->str() == "!=")
*result = result1 != result2;
bool error1, error2;
execute(expr->astOperand1(), programMemory, &result1, &error1);
execute(expr->astOperand2(), programMemory, &result2, &error2);
if (error1 && error2) {
*error = true;
} else if (error1 && !error2) {
ValueFlow::Value v = inferCondition(expr->str(), expr->astOperand1(), result2);
*error = !v.isKnown();
*result = v.intvalue;
} else if (!error1 && error2) {
ValueFlow::Value v = inferCondition(expr->str(), result1, expr->astOperand2());
*error = !v.isKnown();
*result = v.intvalue;
} else {
if (expr->str() == "<")
*result = result1 < result2;
else if (expr->str() == "<=")
*result = result1 <= result2;
else if (expr->str() == ">")
*result = result1 > result2;
else if (expr->str() == ">=")
*result = result1 >= result2;
else if (expr->str() == "==")
*result = result1 == result2;
else if (expr->str() == "!=")
*result = result1 != result2;
}
}

else if (expr->isAssignmentOp()) {
Expand Down
194 changes: 148 additions & 46 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4692,6 +4692,93 @@ static const ValueFlow::Value* proveNotEqual(const std::list<ValueFlow::Value>&
return result;
}

static std::vector<MathLib::bigint> minUnsignedValue(const Token* tok, int depth = 8)
{
std::vector<MathLib::bigint> result = {};
if (!tok)
return result;
if (depth < 0)
return result;
if (tok->hasKnownIntValue()) {
result = {tok->values().front().intvalue};
} else if (tok->isConstOp() && tok->astOperand1() && tok->astOperand2()) {
std::vector<MathLib::bigint> op1 = minUnsignedValue(tok->astOperand1(), depth - 1);
std::vector<MathLib::bigint> op2 = minUnsignedValue(tok->astOperand2(), depth - 1);
if (!op1.empty() && !op1.empty())
result = {calculate(tok->str(), op1.front(), op2.front())};
}
if (result.empty() && tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED)
result = {0};
return result;
}

ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val)
{
if (!varTok)
return ValueFlow::Value{};
if (varTok->hasKnownIntValue())
return ValueFlow::Value{};
if (std::none_of(varTok->values().begin(), varTok->values().end(), [](const ValueFlow::Value& v) {
return v.isImpossible() && v.valueType == ValueFlow::Value::ValueType::INT;
})) {
if (varTok->valueType() && varTok->valueType()->sign == ValueType::UNSIGNED && (op == ">" || op == ">=")) {
std::vector<MathLib::bigint> minvalue = minUnsignedValue(varTok);
if (minvalue.empty())
return ValueFlow::Value{};
bool isTrue = calculate(op, std::max<MathLib::bigint>(0, minvalue.front()), val);
if (!isTrue)
return ValueFlow::Value{};
ValueFlow::Value value;
value.intvalue = 1;
value.bound = ValueFlow::Value::Bound::Point;
value.setKnown();
return value;
}
return ValueFlow::Value{};
}
const ValueFlow::Value* result = nullptr;
bool known = false;
if (op == "==" || op == "!=") {
result = proveNotEqual(varTok->values(), val);
known = op == "!=";
} else if (op == "<" || op == ">=") {
result = proveLessThan(varTok->values(), val);
known = op == "<";
if (!result && !isSaturated(val)) {
result = proveGreaterThan(varTok->values(), val - 1);
known = op == ">=";
}
} else if (op == ">" || op == "<=") {
result = proveGreaterThan(varTok->values(), val);
known = op == ">";
if (!result && !isSaturated(val)) {
result = proveLessThan(varTok->values(), val + 1);
known = op == "<=";
}
}
if (!result)
return ValueFlow::Value{};
ValueFlow::Value value = *result;
value.intvalue = known;
value.bound = ValueFlow::Value::Bound::Point;
value.setKnown();
return value;
}

ValueFlow::Value inferCondition(std::string op, MathLib::bigint val, const Token* varTok)
{
// Flip the operator
if (op == ">")
op = "<";
else if (op == "<")
op = ">";
else if (op == ">=")
op = "<=";
else if (op == "<=")
op = ">=";
return inferCondition(op, varTok, val);
}

static void valueFlowInferCondition(TokenList* tokenlist,
const Settings* settings)
{
Expand All @@ -4711,57 +4798,72 @@ static void valueFlowInferCondition(TokenList* tokenlist,
value.setKnown();
setTokenValue(tok, value, settings);
} else if (tok->isComparisonOp()) {
MathLib::bigint val = 0;
const Token* varTok = nullptr;
ValueFlow::Value value{};
std::string op = tok->str();
if (tok->astOperand1()->hasKnownIntValue()) {
val = tok->astOperand1()->values().front().intvalue;
varTok = tok->astOperand2();
// Flip the operator
if (op == ">")
op = "<";
else if (op == "<")
op = ">";
else if (op == ">=")
op = "<=";
else if (op == "<=")
op = ">=";
MathLib::bigint val = tok->astOperand1()->values().front().intvalue;
const Token* varTok = tok->astOperand2();
value = inferCondition(tok->str(), val, varTok);
} else if (tok->astOperand2()->hasKnownIntValue()) {
val = tok->astOperand2()->values().front().intvalue;
varTok = tok->astOperand1();
MathLib::bigint val = tok->astOperand2()->values().front().intvalue;
const Token* varTok = tok->astOperand1();
value = inferCondition(tok->str(), varTok, val);
}
if (!varTok)
continue;
if (varTok->hasKnownIntValue())
continue;
if (varTok->values().empty())
continue;
const ValueFlow::Value* result = nullptr;
bool known = false;
if (op == "==" || op == "!=") {
result = proveNotEqual(varTok->values(), val);
known = op == "!=";
} else if (op == "<" || op == ">=") {
result = proveLessThan(varTok->values(), val);
known = op == "<";
if (!result && !isSaturated(val)) {
result = proveGreaterThan(varTok->values(), val - 1);
known = op == ">=";
}
} else if (op == ">" || op == "<=") {
result = proveGreaterThan(varTok->values(), val);
known = op == ">";
if (!result && !isSaturated(val)) {
result = proveLessThan(varTok->values(), val + 1);
known = op == "<=";
}
}
if (!result)

if (!value.isKnown())
continue;
ValueFlow::Value value = *result;
value.intvalue = known;
value.bound = ValueFlow::Value::Bound::Point;
value.setKnown();

// MathLib::bigint val = 0;
// const Token* varTok = nullptr;
// std::string op = tok->str();
// if (tok->astOperand1()->hasKnownIntValue()) {
// val = tok->astOperand1()->values().front().intvalue;
// varTok = tok->astOperand2();
// // Flip the operator
// if (op == ">")
// op = "<";
// else if (op == "<")
// op = ">";
// else if (op == ">=")
// op = "<=";
// else if (op == "<=")
// op = ">=";
// } else if (tok->astOperand2()->hasKnownIntValue()) {
// val = tok->astOperand2()->values().front().intvalue;
// varTok = tok->astOperand1();
// }
// if (!varTok)
// continue;
// if (varTok->hasKnownIntValue())
// continue;
// if (varTok->values().empty())
// continue;
// const ValueFlow::Value* result = nullptr;
// bool known = false;
// if (op == "==" || op == "!=") {
// result = proveNotEqual(varTok->values(), val);
// known = op == "!=";
// } else if (op == "<" || op == ">=") {
// result = proveLessThan(varTok->values(), val);
// known = op == "<";
// if (!result && !isSaturated(val)) {
// result = proveGreaterThan(varTok->values(), val - 1);
// known = op == ">=";
// }
// } else if (op == ">" || op == "<=") {
// result = proveGreaterThan(varTok->values(), val);
// known = op == ">";
// if (!result && !isSaturated(val)) {
// result = proveLessThan(varTok->values(), val + 1);
// known = op == "<=";
// }
// }
// if (!result)
// continue;
// ValueFlow::Value value = *result;
// value.intvalue = known;
// value.bound = ValueFlow::Value::Bound::Point;
// value.setKnown();
setTokenValue(tok, value, settings);
}
}
Expand Down
3 changes: 3 additions & 0 deletions lib/valueflow.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,9 @@ struct LifetimeToken {
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value, const std::function<std::vector<MathLib::bigint>(const Token*)>& evaluate);
const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value);

ValueFlow::Value inferCondition(std::string op, MathLib::bigint val, const Token* varTok);
ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val);

std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
bool escape = false,
ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{},
Expand Down
2 changes: 1 addition & 1 deletion test/testcondition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2411,7 +2411,7 @@ class TestCondition : public TestFixture {
ASSERT_EQUALS("", errout.str());

check("void f1(const std::string &s) { if(s.size() >= 0) if(s.empty()) {}} ");
ASSERT_EQUALS("", errout.str());
ASSERT_EQUALS("[test.cpp:1]: (style) Condition 's.size()>=0' is always true\n", errout.str());

// TODO: These are identical condition since size cannot be negative
check("void f1(const std::string &s) { if(s.size() <= 0) if(s.empty()) {}} ");
Expand Down
8 changes: 8 additions & 0 deletions test/teststl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,14 @@ class TestStl : public TestFixture {
" Bar(0.34, result[1]);\n"
"}\n");
ASSERT_EQUALS("", errout.str());

checkNormal("void f(size_t entries) {\n"
" std::vector<uint8_t> v;\n"
" if (v.size() < entries + 2)\n"
" v.resize(entries + 2);\n"
" v[0] = 1;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}

void outOfBoundsIndexExpression() {
Expand Down