Skip to content

Commit 03b952d

Browse files
authored
Fix 11579: false negative: knownConditionTrueFalse with non-bool as bool parameter (#5349)
This adds a new checker to check for pointer to bool conversions that are always known. I removed the previous knownConditionTrueFalse checks since this was too noisy.
1 parent a5cfa85 commit 03b952d

14 files changed

Lines changed: 181 additions & 65 deletions

lib/astutils.cpp

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -696,9 +696,10 @@ std::vector<ValueType> getParentValueTypes(const Token* tok, const Settings* set
696696
return {*tok->astParent()->astOperand1()->valueType()};
697697
return {};
698698
}
699+
const Token* ftok = nullptr;
699700
if (Token::Match(tok->astParent(), "(|{|,")) {
700701
int argn = -1;
701-
const Token* ftok = getTokenArgumentFunction(tok, argn);
702+
ftok = getTokenArgumentFunction(tok, argn);
702703
const Token* typeTok = nullptr;
703704
if (ftok && argn >= 0) {
704705
if (ftok->function()) {
@@ -741,6 +742,9 @@ std::vector<ValueType> getParentValueTypes(const Token* tok, const Settings* set
741742
ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, *settings);
742743
return {std::move(vtParent)};
743744
}
745+
// The return type of a function is not the parent valuetype
746+
if (Token::simpleMatch(tok->astParent(), "(") && ftok && !tok->isCast() && ftok->tokType() != Token::eType)
747+
return {};
744748
if (Token::Match(tok->astParent(), "return|(|{|%assign%") && parent) {
745749
*parent = tok->astParent();
746750
}
@@ -1422,7 +1426,7 @@ static bool isForLoopIncrement(const Token* const tok)
14221426
parent->astParent()->astParent()->astOperand1()->str() == "for";
14231427
}
14241428

1425-
bool isUsedAsBool(const Token* const tok)
1429+
bool isUsedAsBool(const Token* const tok, const Settings* settings)
14261430
{
14271431
if (!tok)
14281432
return false;
@@ -1435,6 +1439,15 @@ bool isUsedAsBool(const Token* const tok)
14351439
const Token* parent = tok->astParent();
14361440
if (!parent)
14371441
return false;
1442+
if (Token::simpleMatch(parent, "["))
1443+
return false;
1444+
if (parent->isUnaryOp("*"))
1445+
return false;
1446+
if (Token::simpleMatch(parent, ".")) {
1447+
if (astIsRHS(tok))
1448+
return isUsedAsBool(parent, settings);
1449+
return false;
1450+
}
14381451
if (Token::Match(parent, "&&|!|%oror%"))
14391452
return true;
14401453
if (parent->isCast())
@@ -1451,7 +1464,7 @@ bool isUsedAsBool(const Token* const tok)
14511464
if (isForLoopCondition(tok))
14521465
return true;
14531466
if (!Token::Match(parent, "%cop%")) {
1454-
std::vector<ValueType> vtParents = getParentValueTypes(tok);
1467+
std::vector<ValueType> vtParents = getParentValueTypes(tok, settings);
14551468
return std::any_of(vtParents.cbegin(), vtParents.cend(), [&](const ValueType& vt) {
14561469
return vt.pointer == 0 && vt.type == ValueType::BOOL;
14571470
});

lib/astutils.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ const Token* isInLoopCondition(const Token* tok);
254254
/**
255255
* Is token used as boolean, that is to say cast to a bool, or used as a condition in a if/while/for
256256
*/
257-
CPPCHECKLIB bool isUsedAsBool(const Token * const tok);
257+
CPPCHECKLIB bool isUsedAsBool(const Token* const tok, const Settings* settings = nullptr);
258258

259259
/**
260260
* Is token passed to a function taking a bool argument

lib/checkcondition.cpp

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ bool CheckCondition::diag(const Token* tok, bool insert)
6060
return false;
6161
const Token* parent = tok->astParent();
6262
bool hasParent = false;
63-
while (Token::Match(parent, "&&|%oror%")) {
63+
while (Token::Match(parent, "!|&&|%oror%")) {
6464
if (mCondDiags.count(parent) != 0) {
6565
hasParent = true;
6666
break;
@@ -410,6 +410,8 @@ void CheckCondition::comparison()
410410

411411
void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result)
412412
{
413+
if (tok && (diag(tok) | diag(tok->astParent())))
414+
return;
413415
std::ostringstream expression;
414416
expression << std::hex << "(X " << bitop << " 0x" << value1 << ") " << op << " 0x" << value2;
415417

@@ -1370,6 +1372,8 @@ void CheckCondition::checkModuloAlwaysTrueFalse()
13701372

13711373
void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal)
13721374
{
1375+
if (diag(tok))
1376+
return;
13731377
reportError(tok, Severity::warning, "moduloAlwaysTrueFalse",
13741378
"Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".", CWE398, Certainty::normal);
13751379
}
@@ -1466,7 +1470,7 @@ void CheckCondition::alwaysTrueFalse()
14661470
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
14671471
if (Token::simpleMatch(tok, "<") && tok->link()) // don't write false positives when templates are used
14681472
continue;
1469-
if (!tok->hasKnownBoolValue())
1473+
if (!tok->hasKnownIntValue())
14701474
continue;
14711475
if (Token::Match(tok->previous(), "%name% (") && tok->previous()->function()) {
14721476
const Function* f = tok->previous()->function();
@@ -1490,7 +1494,7 @@ void CheckCondition::alwaysTrueFalse()
14901494
else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() &&
14911495
Token::simpleMatch(parent->astParent()->astParent()->previous(), "for ("))
14921496
condition = parent->astParent()->astParent()->previous();
1493-
else if (isBooleanFuncArg(tok))
1497+
else if (Token::Match(tok, "%comp%"))
14941498
condition = tok;
14951499
else
14961500
continue;
@@ -1582,10 +1586,7 @@ void CheckCondition::alwaysTrueFalse()
15821586
if (hasSizeof)
15831587
continue;
15841588

1585-
auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) {
1586-
return v.isIntValue();
1587-
});
1588-
alwaysTrueFalseError(tok, condition, &*it);
1589+
alwaysTrueFalseError(tok, condition, &tok->values().front());
15891590
}
15901591
}
15911592
}

lib/checkcondition.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,12 @@ class CPPCHECKLIB CheckCondition : public Check {
6767
checkCondition.checkPointerAdditionResultNotNull();
6868
checkCondition.checkDuplicateConditionalAssign();
6969
checkCondition.assignIf();
70-
checkCondition.alwaysTrueFalse();
7170
checkCondition.checkBadBitmaskCheck();
7271
checkCondition.comparison();
7372
checkCondition.checkModuloAlwaysTrueFalse();
7473
checkCondition.checkAssignmentInCondition();
7574
checkCondition.checkCompareValueOutOfTypeRange();
75+
checkCondition.alwaysTrueFalse();
7676
}
7777

7878
/** mismatching assignment / comparison */

lib/checkother.cpp

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3640,6 +3640,21 @@ static bool isVariableExpression(const Token* tok)
36403640
return false;
36413641
}
36423642

3643+
static bool isVariableExprHidden(const Token* tok)
3644+
{
3645+
if (!tok)
3646+
return false;
3647+
if (!tok->astParent())
3648+
return false;
3649+
if (Token::simpleMatch(tok->astParent(), "*") && Token::simpleMatch(tok->astSibling(), "0"))
3650+
return true;
3651+
if (Token::simpleMatch(tok->astParent(), "&&") && Token::simpleMatch(tok->astSibling(), "false"))
3652+
return true;
3653+
if (Token::simpleMatch(tok->astParent(), "||") && Token::simpleMatch(tok->astSibling(), "true"))
3654+
return true;
3655+
return false;
3656+
}
3657+
36433658
void CheckOther::checkKnownArgument()
36443659
{
36453660
if (!mSettings->severity.isEnabled(Severity::style))
@@ -3679,44 +3694,25 @@ void CheckOther::checkKnownArgument()
36793694
mTokenizer->isCPP(), true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true))
36803695
continue;
36813696
// ensure that there is a integer variable in expression with unknown value
3682-
std::string varexpr;
3683-
bool isVariableExprHidden = false; // Is variable expression explicitly hidden
3684-
auto setVarExpr = [&varexpr, &isVariableExprHidden](const Token *child) {
3697+
const Token* vartok = findAstNode(tok, [](const Token* child) {
36853698
if (Token::Match(child, "%var%|.|[")) {
3686-
if (child->valueType() && child->valueType()->pointer == 0 && child->valueType()->isIntegral() && child->values().empty()) {
3687-
varexpr = child->expressionString();
3688-
return ChildrenToVisit::done;
3689-
}
3690-
return ChildrenToVisit::none;
3699+
return astIsIntegral(child, false) && !astIsPointer(child) && child->values().empty();
36913700
}
3692-
if (Token::simpleMatch(child->previous(), "sizeof ("))
3693-
return ChildrenToVisit::none;
3694-
3695-
// hide variable explicitly with 'x * 0' etc
3696-
if (!isVariableExprHidden) {
3697-
if (Token::simpleMatch(child, "*") && (Token::simpleMatch(child->astOperand1(), "0") || Token::simpleMatch(child->astOperand2(), "0")))
3698-
return ChildrenToVisit::none;
3699-
if (Token::simpleMatch(child, "&&") && (Token::simpleMatch(child->astOperand1(), "false") || Token::simpleMatch(child->astOperand2(), "false")))
3700-
return ChildrenToVisit::none;
3701-
if (Token::simpleMatch(child, "||") && (Token::simpleMatch(child->astOperand1(), "true") || Token::simpleMatch(child->astOperand2(), "true")))
3702-
return ChildrenToVisit::none;
3703-
}
3704-
3705-
return ChildrenToVisit::op1_and_op2;
3706-
};
3707-
visitAstNodes(tok, setVarExpr);
3708-
if (varexpr.empty()) {
3709-
isVariableExprHidden = true;
3710-
visitAstNodes(tok, setVarExpr);
3711-
}
3712-
if (varexpr.empty())
3701+
return false;
3702+
});
3703+
if (!vartok)
3704+
continue;
3705+
if (vartok->astSibling() &&
3706+
findAstNode(vartok->astSibling(), [](const Token* child) {
3707+
return Token::simpleMatch(child, "sizeof");
3708+
}))
37133709
continue;
37143710
// ensure that function name does not contain "assert"
3715-
std::string funcname = tok->astParent()->previous()->str();
3711+
std::string funcname = ftok->str();
37163712
strTolower(funcname);
37173713
if (funcname.find("assert") != std::string::npos)
37183714
continue;
3719-
knownArgumentError(tok, ftok, &tok->values().front(), varexpr, isVariableExprHidden);
3715+
knownArgumentError(tok, ftok, &tok->values().front(), vartok->expressionString(), isVariableExprHidden(vartok));
37203716
}
37213717
}
37223718
}
@@ -3747,6 +3743,48 @@ void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const V
37473743
reportError(errorPath, Severity::style, id, errmsg, CWE570, Certainty::normal);
37483744
}
37493745

3746+
void CheckOther::checkKnownPointerToBool()
3747+
{
3748+
if (!mSettings->severity.isEnabled(Severity::style))
3749+
return;
3750+
const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
3751+
for (const Scope* functionScope : symbolDatabase->functionScopes) {
3752+
for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) {
3753+
if (!tok->hasKnownIntValue())
3754+
continue;
3755+
if (!astIsPointer(tok))
3756+
continue;
3757+
if (Token::Match(tok->astParent(), "?|!|&&|%oror%|%comp%"))
3758+
continue;
3759+
if (tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|switch|sizeof ("))
3760+
continue;
3761+
if (tok->isExpandedMacro())
3762+
continue;
3763+
if (findParent(tok, [](const Token* parent) {
3764+
return parent->isExpandedMacro();
3765+
}))
3766+
continue;
3767+
if (!isUsedAsBool(tok, mSettings))
3768+
continue;
3769+
const ValueFlow::Value& value = tok->values().front();
3770+
knownPointerToBoolError(tok, &value);
3771+
}
3772+
}
3773+
}
3774+
3775+
void CheckOther::knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value)
3776+
{
3777+
if (!tok) {
3778+
reportError(tok, Severity::style, "knownPointerToBool", "Pointer expression 'p' converted to bool is always true.");
3779+
return;
3780+
}
3781+
std::string cond = value->intvalue ? "true" : "false";
3782+
const std::string& expr = tok->expressionString();
3783+
std::string errmsg = "Pointer expression '" + expr + "' converted to bool is always " + cond + ".";
3784+
const ErrorPath errorPath = getErrorPath(tok, value, errmsg);
3785+
reportError(errorPath, Severity::style, "knownPointerToBool", errmsg, CWE570, Certainty::normal);
3786+
}
3787+
37503788
void CheckOther::checkComparePointers()
37513789
{
37523790
const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase();

lib/checkother.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ class CPPCHECKLIB CheckOther : public Check {
8585
checkOther.checkFuncArgNamesDifferent();
8686
checkOther.checkShadowVariables();
8787
checkOther.checkKnownArgument();
88+
checkOther.checkKnownPointerToBool();
8889
checkOther.checkComparePointers();
8990
checkOther.checkIncompleteStatement();
9091
checkOther.checkRedundantCopy();
@@ -218,6 +219,8 @@ class CPPCHECKLIB CheckOther : public Check {
218219

219220
void checkKnownArgument();
220221

222+
void checkKnownPointerToBool();
223+
221224
void checkComparePointers();
222225

223226
void checkModuloOfOne();
@@ -279,6 +282,7 @@ class CPPCHECKLIB CheckOther : public Check {
279282
void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector<const Token*> & declarations, const std::vector<const Token*> & definitions);
280283
void shadowError(const Token *var, const Token *shadowed, std::string type);
281284
void knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden);
285+
void knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value);
282286
void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2);
283287
void checkModuloOfOneError(const Token *tok);
284288

@@ -348,6 +352,7 @@ class CPPCHECKLIB CheckOther : public Check {
348352
c.shadowError(nullptr, nullptr, "function");
349353
c.shadowError(nullptr, nullptr, "argument");
350354
c.knownArgumentError(nullptr, nullptr, nullptr, "x", false);
355+
c.knownPointerToBoolError(nullptr, nullptr);
351356
c.comparePointersError(nullptr, nullptr, nullptr);
352357
c.redundantAssignmentError(nullptr, nullptr, "var", false);
353358
c.redundantInitializationError(nullptr, nullptr, "var", false);

lib/forwardanalyzer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ struct ForwardTraversal {
488488
if (allAnalysis.isIncremental())
489489
return Break(Analyzer::Terminate::Bail);
490490
} else if (allAnalysis.isModified()) {
491-
std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, /*isModified*/ true);
491+
std::vector<ForwardTraversal> ftv = tryForkScope(endBlock, allAnalysis.isModified());
492492
bool forkContinue = true;
493493
for (ForwardTraversal& ft : ftv) {
494494
if (condTok)

lib/token.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,15 +2388,6 @@ bool Token::hasKnownIntValue() const
23882388
});
23892389
}
23902390

2391-
bool Token::hasKnownBoolValue() const
2392-
{
2393-
if (!mImpl->mValues)
2394-
return false;
2395-
return std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) {
2396-
return value.isIntValue() && (value.isKnown() || (value.intvalue == 0 && value.isImpossible()));
2397-
});
2398-
}
2399-
24002391
bool Token::hasKnownValue() const
24012392
{
24022393
return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown));

lib/token.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,6 @@ class CPPCHECKLIB Token {
12161216
}
12171217

12181218
bool hasKnownIntValue() const;
1219-
bool hasKnownBoolValue() const;
12201219
bool hasKnownValue() const;
12211220
bool hasKnownValue(ValueFlow::Value::ValueType t) const;
12221221
bool hasKnownSymbolicValue(const Token* tok) const;

lib/valueflow.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6843,7 +6843,8 @@ static void valueFlowInferCondition(TokenList& tokenlist,
68436843
}
68446844
}
68456845
} else if (Token::Match(tok->astParent(), "?|&&|!|%oror%") ||
6846-
Token::Match(tok->astParent()->previous(), "if|while (")) {
6846+
Token::Match(tok->astParent()->previous(), "if|while (") ||
6847+
(astIsPointer(tok) && isUsedAsBool(tok, settings))) {
68476848
std::vector<ValueFlow::Value> result = infer(IntegralInferModel{}, "!=", tok->values(), 0);
68486849
if (result.size() != 1)
68496850
continue;

0 commit comments

Comments
 (0)