From 694d1470976a35636c520ebfa29beb8329723029 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Wed, 30 Oct 2019 11:57:46 -0500 Subject: [PATCH] Refactor ProgramMemory and PathAnalysis (#2311) * Traverse conditions for container size * Move program memory to seperate file * Revert "Traverse conditions for container size" This reverts commit 914783769f25391a3c49fab73be478383ec8f920. * Move pathanalysis to seperate files --- lib/astutils.cpp | 159 ---------------- lib/astutils.h | 56 ------ lib/checkstl.cpp | 1 + lib/pathanalysis.cpp | 165 ++++++++++++++++ lib/pathanalysis.h | 71 +++++++ lib/programmemory.cpp | 413 ++++++++++++++++++++++++++++++++++++++++ lib/programmemory.h | 59 ++++++ lib/valueflow.cpp | 433 +----------------------------------------- lib/valueflow.h | 2 + 9 files changed, 713 insertions(+), 646 deletions(-) create mode 100644 lib/pathanalysis.cpp create mode 100644 lib/pathanalysis.h create mode 100644 lib/programmemory.cpp create mode 100644 lib/programmemory.h diff --git a/lib/astutils.cpp b/lib/astutils.cpp index b7d841018..c359051ae 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1468,165 +1468,6 @@ static bool hasFunctionCall(const Token *tok) return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2()); } -const Scope* PathAnalysis::findOuterScope(const Scope * scope) -{ - if (!scope) - return nullptr; - if (scope->isLocal() && scope->type != Scope::eSwitch) - return findOuterScope(scope->nestedIn); - return scope; -} - -static const Token* getCondTok(const Token* tok) -{ - if (!tok) - return nullptr; - if (Token::simpleMatch(tok, "(")) - return getCondTok(tok->previous()); - if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && tok->next()->astOperand2()->astOperand2()) - return tok->next()->astOperand2()->astOperand2()->astOperand1(); - if (Token::simpleMatch(tok->next()->astOperand2(), ";")) - return tok->next()->astOperand2()->astOperand1(); - return tok->next()->astOperand2(); -} - -std::pair PathAnalysis::checkCond(const Token * tok, bool& known) -{ - if (tok->hasKnownIntValue()) { - known = true; - return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue); - } - auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) { - return v.isIntValue(); - }); - // If all possible values are the same, then assume all paths have the same value - if (it != tok->values().end() && std::all_of(it, tok->values().end(), [&](const ValueFlow::Value& v) { - if (v.isIntValue()) - return v.intvalue == it->intvalue; - return true; - })) { - known = false; - return std::make_pair(it->intvalue, !it->intvalue); - } - return std::make_pair(true, true); -} - -PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function& f) const -{ - if (!tok) - return Progress::Continue; - if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break) - return Progress::Break; - info.tok = tok; - if (f(info) == Progress::Break) - return Progress::Break; - if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break) - return Progress::Break; - return Progress::Continue; -} - -PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const -{ - for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { - if (Token::Match(tok, "asm|goto|break|continue")) - return Progress::Break; - if (Token::Match(tok, "return|throw")) { - forwardRecursive(tok, info, f); - return Progress::Break; - } - if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) { - const Token * blockStart = tok->link()->linkAt(-1)->previous(); - const Token * condTok = getCondTok(blockStart); - if (!condTok) - continue; - info.errorPath.emplace_back(condTok, "Assuming condition is true."); - // Traverse a loop a second time - if (Token::Match(blockStart, "for|while (")) { - const Token* endCond = blockStart->linkAt(1); - bool traverseLoop = true; - // Only traverse simple for loops - if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {")) - traverseLoop = false; - // Traverse loop a second time - if (traverseLoop) { - // Traverse condition - if (forwardRecursive(condTok, info, f) == Progress::Break) - return Progress::Break; - // TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)? - } - } - } - if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) { - const Token * endCond = tok->next()->link(); - const Token * endBlock = endCond->next()->link(); - const Token * condTok = getCondTok(tok); - if (!condTok) - continue; - // Traverse condition - if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break) - return Progress::Break; - Info i = info; - i.known = false; - i.errorPath.emplace_back(condTok, "Assuming condition is true."); - - // Check if condition is true or false - bool checkThen = false; - bool checkElse = false; - std::tie(checkThen, checkElse) = checkCond(condTok, i.known); - - // Traverse then block - if (checkThen) { - if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break) - return Progress::Break; - } - // Traverse else block - if (Token::simpleMatch(endBlock, "} else {")) { - if (checkElse) { - i.errorPath.back().second = "Assuming condition is false."; - Progress result = forwardRange(endCond->next(), endBlock, i, f); - if (result == Progress::Break) - return Progress::Break; - } - tok = endBlock->linkAt(2); - } else { - tok = endBlock; - } - } else if (Token::simpleMatch(tok, "} else {")) { - tok = tok->linkAt(2); - } else { - info.tok = tok; - if (f(info) == Progress::Break) - return Progress::Break; - } - // Prevent infinite recursion - if (tok->next() == start) - break; - } - return Progress::Continue; -} - -void PathAnalysis::forward(const std::function& f) const -{ - const Scope * endScope = findOuterScope(start->scope()); - if (!endScope) - return; - const Token * endToken = endScope->bodyEnd; - Info info{start, ErrorPath{}, true}; - forwardRange(start, endToken, info, f); -} - -bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath) -{ - PathAnalysis::Info info = PathAnalysis{start, library} .forwardFind([&](const PathAnalysis::Info& i) { - return (i.tok == dest); - }); - if (!info.tok) - return false; - if (errorPath) - errorPath->insert(errorPath->end(), info.errorPath.begin(), info.errorPath.end()); - return true; -} - static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local) { for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { diff --git a/lib/astutils.h b/lib/astutils.h index 111e96d1e..e29f2908e 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -209,62 +209,6 @@ const Variable *getLHSVariable(const Token *tok); bool isScopeBracket(const Token* tok); -struct PathAnalysis { - enum class Progress { - Continue, - Break - }; - PathAnalysis(const Token* start, const Library& library) - : start(start), library(&library) - {} - const Token * start; - const Library * library; - - struct Info { - const Token* tok; - ErrorPath errorPath; - bool known; - }; - - void forward(const std::function& f) const; - template - void forwardAll(F f) { - forward([&](const Info& info) { - f(info); - return Progress::Continue; - }); - } - template - Info forwardFind(Predicate pred) { - Info result{}; - forward([&](const Info& info) { - if (pred(info)) { - result = info; - return Progress::Break; - } - return Progress::Continue; - }); - return result; - } -private: - - Progress forwardRecursive(const Token* tok, Info info, const std::function& f) const; - Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const; - - static const Scope* findOuterScope(const Scope * scope); - - static std::pair checkCond(const Token * tok, bool& known); -}; - -/** - * @brief Returns true if there is a path between the two tokens - * - * @param start Starting point of the path - * @param dest The path destination - * @param errorPath Adds the path traversal to the errorPath - */ -bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath); - /** * Forward data flow analysis for checks * - unused value diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 5bbbf70a0..3af61dacc 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -26,6 +26,7 @@ #include "token.h" #include "utils.h" #include "astutils.h" +#include "pathanalysis.h" #include #include diff --git a/lib/pathanalysis.cpp b/lib/pathanalysis.cpp new file mode 100644 index 000000000..4957367d7 --- /dev/null +++ b/lib/pathanalysis.cpp @@ -0,0 +1,165 @@ +#include "pathanalysis.h" +#include "library.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" + +const Scope* PathAnalysis::findOuterScope(const Scope * scope) +{ + if (!scope) + return nullptr; + if (scope->isLocal() && scope->type != Scope::eSwitch) + return findOuterScope(scope->nestedIn); + return scope; +} + +static const Token* getCondTok(const Token* tok) +{ + if (!tok) + return nullptr; + if (Token::simpleMatch(tok, "(")) + return getCondTok(tok->previous()); + if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && tok->next()->astOperand2()->astOperand2()) + return tok->next()->astOperand2()->astOperand2()->astOperand1(); + if (Token::simpleMatch(tok->next()->astOperand2(), ";")) + return tok->next()->astOperand2()->astOperand1(); + return tok->next()->astOperand2(); +} + +std::pair PathAnalysis::checkCond(const Token * tok, bool& known) +{ + if (tok->hasKnownIntValue()) { + known = true; + return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue); + } + auto it = std::find_if(tok->values().begin(), tok->values().end(), [](const ValueFlow::Value& v) { + return v.isIntValue(); + }); + // If all possible values are the same, then assume all paths have the same value + if (it != tok->values().end() && std::all_of(it, tok->values().end(), [&](const ValueFlow::Value& v) { + if (v.isIntValue()) + return v.intvalue == it->intvalue; + return true; + })) { + known = false; + return std::make_pair(it->intvalue, !it->intvalue); + } + return std::make_pair(true, true); +} + +PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function& f) const +{ + if (!tok) + return Progress::Continue; + if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break) + return Progress::Break; + info.tok = tok; + if (f(info) == Progress::Break) + return Progress::Break; + if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), info, f) == Progress::Break) + return Progress::Break; + return Progress::Continue; +} + +PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const +{ + for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { + if (Token::Match(tok, "asm|goto|break|continue")) + return Progress::Break; + if (Token::Match(tok, "return|throw")) { + forwardRecursive(tok, info, f); + return Progress::Break; + } + if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) { + const Token * blockStart = tok->link()->linkAt(-1)->previous(); + const Token * condTok = getCondTok(blockStart); + if (!condTok) + continue; + info.errorPath.emplace_back(condTok, "Assuming condition is true."); + // Traverse a loop a second time + if (Token::Match(blockStart, "for|while (")) { + const Token* endCond = blockStart->linkAt(1); + bool traverseLoop = true; + // Only traverse simple for loops + if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {")) + traverseLoop = false; + // Traverse loop a second time + if (traverseLoop) { + // Traverse condition + if (forwardRecursive(condTok, info, f) == Progress::Break) + return Progress::Break; + // TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)? + } + } + } + if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) { + const Token * endCond = tok->next()->link(); + const Token * endBlock = endCond->next()->link(); + const Token * condTok = getCondTok(tok); + if (!condTok) + continue; + // Traverse condition + if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break) + return Progress::Break; + Info i = info; + i.known = false; + i.errorPath.emplace_back(condTok, "Assuming condition is true."); + + // Check if condition is true or false + bool checkThen = false; + bool checkElse = false; + std::tie(checkThen, checkElse) = checkCond(condTok, i.known); + + // Traverse then block + if (checkThen) { + if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break) + return Progress::Break; + } + // Traverse else block + if (Token::simpleMatch(endBlock, "} else {")) { + if (checkElse) { + i.errorPath.back().second = "Assuming condition is false."; + Progress result = forwardRange(endCond->next(), endBlock, i, f); + if (result == Progress::Break) + return Progress::Break; + } + tok = endBlock->linkAt(2); + } else { + tok = endBlock; + } + } else if (Token::simpleMatch(tok, "} else {")) { + tok = tok->linkAt(2); + } else { + info.tok = tok; + if (f(info) == Progress::Break) + return Progress::Break; + } + // Prevent infinite recursion + if (tok->next() == start) + break; + } + return Progress::Continue; +} + +void PathAnalysis::forward(const std::function& f) const +{ + const Scope * endScope = findOuterScope(start->scope()); + if (!endScope) + return; + const Token * endToken = endScope->bodyEnd; + Info info{start, ErrorPath{}, true}; + forwardRange(start, endToken, info, f); +} + +bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath) +{ + PathAnalysis::Info info = PathAnalysis{start, library} .forwardFind([&](const PathAnalysis::Info& i) { + return (i.tok == dest); + }); + if (!info.tok) + return false; + if (errorPath) + errorPath->insert(errorPath->end(), info.errorPath.begin(), info.errorPath.end()); + return true; +} diff --git a/lib/pathanalysis.h b/lib/pathanalysis.h new file mode 100644 index 000000000..3353c7b84 --- /dev/null +++ b/lib/pathanalysis.h @@ -0,0 +1,71 @@ +#ifndef GUARD_PATHANALYSIS_H +#define GUARD_PATHANALYSIS_H + +#include + +#include "errorlogger.h" +#include "utils.h" + +class Library; +class Settings; +class Scope; +class Token; + +struct PathAnalysis { + enum class Progress { + Continue, + Break + }; + PathAnalysis(const Token* start, const Library& library) + : start(start), library(&library) + {} + const Token * start; + const Library * library; + + struct Info { + const Token* tok; + ErrorPath errorPath; + bool known; + }; + + void forward(const std::function& f) const; + template + void forwardAll(F f) { + forward([&](const Info& info) { + f(info); + return Progress::Continue; + }); + } + template + Info forwardFind(Predicate pred) { + Info result{}; + forward([&](const Info& info) { + if (pred(info)) { + result = info; + return Progress::Break; + } + return Progress::Continue; + }); + return result; + } +private: + + Progress forwardRecursive(const Token* tok, Info info, const std::function& f) const; + Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const; + + static const Scope* findOuterScope(const Scope * scope); + + static std::pair checkCond(const Token * tok, bool& known); +}; + +/** + * @brief Returns true if there is a path between the two tokens + * + * @param start Starting point of the path + * @param dest The path destination + * @param errorPath Adds the path traversal to the errorPath + */ +bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath); + +#endif + diff --git a/lib/programmemory.cpp b/lib/programmemory.cpp new file mode 100644 index 000000000..b2993b1be --- /dev/null +++ b/lib/programmemory.cpp @@ -0,0 +1,413 @@ + +#include "programmemory.h" +#include "token.h" +#include "astutils.h" +#include "symboldatabase.h" +#include + +void ProgramMemory::setValue(nonneg int varid, const ValueFlow::Value &value) { + values[varid] = value; +} + +bool ProgramMemory::getIntValue(nonneg int varid, MathLib::bigint* result) const { + const std::map::const_iterator it = values.find(varid); + const bool found = it != values.end() && it->second.isIntValue(); + if (found) + *result = it->second.intvalue; + return found; +} + +void ProgramMemory::setIntValue(nonneg int varid, MathLib::bigint value) { + values[varid] = ValueFlow::Value(value); +} + +bool ProgramMemory::getTokValue(nonneg int varid, const Token** result) const { + const std::map::const_iterator it = values.find(varid); + const bool found = it != values.end() && it->second.isTokValue(); + if (found) + *result = it->second.tokvalue; + return found; +} + +bool ProgramMemory::hasValue(nonneg int varid) { + return values.find(varid) != values.end(); +} + +void ProgramMemory::swap(ProgramMemory &pm) { + values.swap(pm.values); +} + +void ProgramMemory::clear() { + values.clear(); +} + +bool ProgramMemory::empty() const { + return values.empty(); +} + +void ProgramMemory::replace(const ProgramMemory &pm) { + for (auto&& p:pm.values) + values[p.first] = p.second; +} + +void ProgramMemory::insert(const ProgramMemory &pm) { + for (auto&& p:pm.values) + values.insert(p); +} + + +bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory) +{ + if (!condition) + return false; + if (condition->str() == "&&") { + return conditionIsFalse(condition->astOperand1(), programMemory) || + conditionIsFalse(condition->astOperand2(), programMemory); + } + ProgramMemory progmem(programMemory); + MathLib::bigint result = 0; + bool error = false; + execute(condition, &progmem, &result, &error); + return !error && result == 0; +} + +bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory) +{ + if (!condition) + return false; + if (condition->str() == "||") { + return conditionIsTrue(condition->astOperand1(), programMemory) || + conditionIsTrue(condition->astOperand2(), programMemory); + } + ProgramMemory progmem(programMemory); + bool error = false; + MathLib::bigint result = 0; + execute(condition, &progmem, &result, &error); + return !error && result == 1; +} + +static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then) +{ + if (Token::Match(tok, "==|>=|<=|<|>|!=")) { + if (then && !Token::Match(tok, "==|>=|<=")) + return; + if (!then && !Token::Match(tok, "<|>|!=")) + return; + ValueFlow::Value truevalue; + ValueFlow::Value falsevalue; + const Token* vartok = parseCompareInt(tok, truevalue, falsevalue); + if (!vartok) + return; + if (vartok->varId() == 0) + return; + if (!truevalue.isIntValue()) + return; + if (isVariableChanged(tok->next(), endTok, vartok->varId(), false, settings, true)) + return; + pm.setIntValue(vartok->varId(), then ? truevalue.intvalue : falsevalue.intvalue); + } else if (Token::Match(tok, "%var%")) { + if (tok->varId() == 0) + return; + if (then && !astIsPointer(tok) && !astIsBool(tok)) + return; + if (isVariableChanged(tok->next(), endTok, tok->varId(), false, settings, true)) + return; + pm.setIntValue(tok->varId(), then); + } else if (Token::simpleMatch(tok, "!")) { + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then); + } else if (then && Token::simpleMatch(tok, "&&")) { + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); + programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); + } else if (!then && Token::simpleMatch(tok, "||")) { + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); + programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); + } +} + +static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings) +{ + if (!scope) + return; + if (!scope->isLocal()) + return; + assert(scope != scope->nestedIn); + fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings); + if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse) { + const Token * bodyStart = scope->bodyStart; + if (scope->type == Scope::eElse) { + if (!Token::simpleMatch(bodyStart->tokAt(-2), "} else {")) + return; + bodyStart = bodyStart->linkAt(-2); + } + const Token * condEndTok = bodyStart->previous(); + if (!Token::simpleMatch(condEndTok, ") {")) + return; + const Token * condStartTok = condEndTok->link(); + if (!condStartTok) + return; + if (!Token::Match(condStartTok->previous(), "if|while (")) + return; + const Token * condTok = condStartTok->astOperand2(); + programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); + } +} + +static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings) +{ + fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings); +} + +static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, std::unordered_map vars) +{ + int indentlevel = 0; + for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { + bool setvar = false; + if (Token::Match(tok2, "[;{}] %var% = %var% ;")) { + for (auto&& p:vars) { + if (p.first != tok2->next()->varId()) + continue; + const Token *vartok = tok2->tokAt(3); + pm.setValue(vartok->varId(), p.second); + setvar = true; + } + } + if (!setvar && (Token::Match(tok2, "[;{}] %var% =") || + Token::Match(tok2, "[;{}] const| %type% %var% ("))) { + const Token *vartok = tok2->next(); + while (vartok->next()->isName()) + vartok = vartok->next(); + if (!pm.hasValue(vartok->varId())) { + MathLib::bigint result = 0; + bool error = false; + execute(vartok->next()->astOperand2(), &pm, &result, &error); + if (!error) + pm.setIntValue(vartok->varId(), result); + } + } + + if (tok2->str() == "{") { + if (indentlevel <= 0) + break; + --indentlevel; + } + if (tok2->str() == "}") { + const Token *cond = tok2->link(); + cond = Token::simpleMatch(cond->previous(), ") {") ? cond->linkAt(-1) : nullptr; + if (cond && conditionIsFalse(cond->astOperand2(), state)) + tok2 = cond->previous(); + else if (cond && conditionIsTrue(cond->astOperand2(), state)) { + ++indentlevel; + continue; + } else + break; + } + } +} + +ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value) +{ + ProgramMemory programMemory; + if (value.tokvalue) + fillProgramMemoryFromConditions(programMemory, value.tokvalue, nullptr); + if (value.condition) + fillProgramMemoryFromConditions(programMemory, value.condition, nullptr); + programMemory.setValue(varid, value); + if (value.varId) + programMemory.setIntValue(value.varId, value.varvalue); + const ProgramMemory state = programMemory; + fillProgramMemoryFromAssignments(programMemory, tok, state, {{varid, value}}); + return programMemory; +} + +void execute(const Token *expr, + ProgramMemory * const programMemory, + MathLib::bigint *result, + bool *error) +{ + if (!expr) + *error = true; + + else if (expr->hasKnownIntValue()) { + *result = expr->values().front().intvalue; + } + + else if (expr->isNumber()) { + *result = MathLib::toLongNumber(expr->str()); + if (MathLib::isFloat(expr->str())) + *error = true; + } + + else if (expr->varId() > 0) { + if (!programMemory->getIntValue(expr->varId(), result)) + *error = true; + } + + 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; + } + + else if (expr->isAssignmentOp()) { + execute(expr->astOperand2(), programMemory, result, error); + if (!expr->astOperand1() || !expr->astOperand1()->varId()) + *error = true; + if (*error) + return; + + if (expr->str() == "=") { + programMemory->setIntValue(expr->astOperand1()->varId(), *result); + return; + } + + long long intValue; + if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) { + *error = true; + return; + } + if (expr->str() == "+=") + programMemory->setIntValue(expr->astOperand1()->varId(), intValue + *result); + else if (expr->str() == "-=") + programMemory->setIntValue(expr->astOperand1()->varId(), intValue - *result); + else if (expr->str() == "*=") + programMemory->setIntValue(expr->astOperand1()->varId(), intValue * *result); + else if (expr->str() == "/=" && *result != 0) + programMemory->setIntValue(expr->astOperand1()->varId(), intValue / *result); + else if (expr->str() == "%=" && *result != 0) + programMemory->setIntValue(expr->astOperand1()->varId(), intValue % *result); + else if (expr->str() == "&=") + programMemory->setIntValue(expr->astOperand1()->varId(), intValue & *result); + else if (expr->str() == "|=") + programMemory->setIntValue(expr->astOperand1()->varId(), intValue | *result); + else if (expr->str() == "^=") + programMemory->setIntValue(expr->astOperand1()->varId(), intValue ^ *result); + } + + else if (Token::Match(expr, "++|--")) { + if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U) + *error = true; + else { + long long intValue; + if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) + *error = true; + else { + if (intValue == 0 && + expr->str() == "--" && + expr->astOperand1()->variable() && + expr->astOperand1()->variable()->typeStartToken()->isUnsigned()) + *error = true; // overflow + *result = intValue + (expr->str() == "++" ? 1 : -1); + programMemory->setIntValue(expr->astOperand1()->varId(), *result); + } + } + } + + else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) { + 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() == "*") { + if (result2 && (result1 > std::numeric_limits::max()/result2)) + *error = true; + else + *result = result1 * result2; + } else if (result2 == 0) + *error = true; + else if (expr->str() == "/") + *result = result1 / result2; + else if (expr->str() == "%") + *result = result1 % result2; + else if (expr->str() == "<<") { + if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB + *error= true; + } else { + *result = result1 << result2; + } + } else if (expr->str() == ">>") { + if (result2 < 0) { // don't perform UB + *error=true; + } else { + *result = result1 >> result2; + } + } + } + + else if (expr->str() == "&&") { + bool error1 = false; + execute(expr->astOperand1(), programMemory, result, &error1); + if (!error1 && *result == 0) + *result = 0; + else { + bool error2 = false; + execute(expr->astOperand2(), programMemory, result, &error2); + if (error1 && error2) + *error = true; + if (error2) + *result = 1; + else + *result = !!*result; + } + } + + else if (expr->str() == "||") { + execute(expr->astOperand1(), programMemory, result, error); + if (*result == 0 && *error == false) + execute(expr->astOperand2(), programMemory, result, error); + } + + else if (expr->str() == "!") { + execute(expr->astOperand1(), programMemory, result, error); + *result = !(*result); + } + + else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { + execute(expr->astOperand1(), programMemory, result, error); + execute(expr->astOperand2(), programMemory, result, error); + } + + else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { + const Token *tokvalue = nullptr; + if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) { + auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(), + expr->astOperand1()->values().end(), + std::mem_fn(&ValueFlow::Value::isTokValue)); + if (tokvalue_it == expr->astOperand1()->values().end()) { + *error = true; + return; + } + tokvalue = tokvalue_it->tokvalue; + } + if (!tokvalue || !tokvalue->isLiteral()) { + *error = true; + return; + } + const std::string strValue = tokvalue->strValue(); + MathLib::bigint index = 0; + execute(expr->astOperand2(), programMemory, &index, error); + if (index >= 0 && index < strValue.size()) + *result = strValue[index]; + else if (index == strValue.size()) + *result = 0; + else + *error = true; + } + + else + *error = true; +} diff --git a/lib/programmemory.h b/lib/programmemory.h new file mode 100644 index 000000000..b6dd1ab29 --- /dev/null +++ b/lib/programmemory.h @@ -0,0 +1,59 @@ +#ifndef GUARD_PROGRAMMEMORY_H +#define GUARD_PROGRAMMEMORY_H + +#include "config.h" +#include "utils.h" +#include "valueflow.h" +#include "mathlib.h" +#include + +struct ProgramMemory { + std::map values; + + void setValue(nonneg int varid, const ValueFlow::Value &value); + + bool getIntValue(nonneg int varid, MathLib::bigint* result) const; + void setIntValue(nonneg int varid, MathLib::bigint value); + + bool getTokValue(nonneg int varid, const Token** result) const; + bool hasValue(nonneg int varid); + + void swap(ProgramMemory &pm); + + void clear(); + + bool empty() const; + + void replace(const ProgramMemory &pm); + + void insert(const ProgramMemory &pm); +}; + +void execute(const Token *expr, + ProgramMemory * const programMemory, + MathLib::bigint *result, + bool *error); + +/** + * Is condition always false when variable has given value? + * \param condition top ast token in condition + * \param programMemory program memory + */ +bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory); + +/** + * Is condition always true when variable has given value? + * \param condition top ast token in condition + * \param programMemory program memory + */ +bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory); + +/** + * Get program memory by looking backwards from given token. + */ +ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value); + +#endif + + + diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 985b7a06d..c8c60c77c 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -82,6 +82,7 @@ #include "library.h" #include "mathlib.h" #include "platform.h" +#include "programmemory.h" #include "settings.h" #include "standards.h" #include "symboldatabase.h" @@ -103,67 +104,6 @@ static const int TIMEOUT = 10; // Do not repeat ValueFlow analysis more than 10 seconds -namespace { - struct ProgramMemory { - std::map values; - - void setValue(nonneg int varid, const ValueFlow::Value &value) { - values[varid] = value; - } - - bool getIntValue(nonneg int varid, MathLib::bigint* result) const { - const std::map::const_iterator it = values.find(varid); - const bool found = it != values.end() && it->second.isIntValue(); - if (found) - *result = it->second.intvalue; - return found; - } - - void setIntValue(nonneg int varid, MathLib::bigint value) { - values[varid] = ValueFlow::Value(value); - } - - bool getTokValue(nonneg int varid, const Token** result) const { - const std::map::const_iterator it = values.find(varid); - const bool found = it != values.end() && it->second.isTokValue(); - if (found) - *result = it->second.tokvalue; - return found; - } - - bool hasValue(nonneg int varid) { - return values.find(varid) != values.end(); - } - - void swap(ProgramMemory &pm) { - values.swap(pm.values); - } - - void clear() { - values.clear(); - } - - bool empty() const { - return values.empty(); - } - - void replace(const ProgramMemory &pm) { - for (auto&& p:pm.values) - values[p.first] = p.second; - } - - void insert(const ProgramMemory &pm) { - for (auto&& p:pm.values) - values.insert(p); - } - }; -} - -static void execute(const Token *expr, - ProgramMemory * const programMemory, - MathLib::bigint *result, - bool *error); - static void bailoutInternal(TokenList *tokenlist, ErrorLogger *errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, const std::string &function) { std::list callstack(1, ErrorLogger::ErrorMessage::FileLocation(tok, tokenlist)); @@ -236,46 +176,6 @@ static void changePossibleToKnown(std::list& values, int indir } } -/** - * Is condition always false when variable has given value? - * \param condition top ast token in condition - * \param programMemory program memory - */ -static bool conditionIsFalse(const Token *condition, const ProgramMemory &programMemory) -{ - if (!condition) - return false; - if (condition->str() == "&&") { - return conditionIsFalse(condition->astOperand1(), programMemory) || - conditionIsFalse(condition->astOperand2(), programMemory); - } - ProgramMemory progmem(programMemory); - MathLib::bigint result = 0; - bool error = false; - execute(condition, &progmem, &result, &error); - return !error && result == 0; -} - -/** - * Is condition always true when variable has given value? - * \param condition top ast token in condition - * \param programMemory program memory - */ -static bool conditionIsTrue(const Token *condition, const ProgramMemory &programMemory) -{ - if (!condition) - return false; - if (condition->str() == "||") { - return conditionIsTrue(condition->astOperand1(), programMemory) || - conditionIsTrue(condition->astOperand2(), programMemory); - } - ProgramMemory progmem(programMemory); - bool error = false; - MathLib::bigint result = 0; - execute(condition, &progmem, &result, &error); - return !error && result == 1; -} - static void setValueUpperBound(ValueFlow::Value& value, bool upper) { if (upper) @@ -334,7 +234,7 @@ static bool isSaturated(MathLib::bigint value) return value == std::numeric_limits::max() || value == std::numeric_limits::min(); } -static const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value) +const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value) { if (!tok->astOperand1() || !tok->astOperand2()) return nullptr; @@ -356,142 +256,6 @@ static const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_val return nullptr; } -static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then) -{ - if (Token::Match(tok, "==|>=|<=|<|>|!=")) { - if (then && !Token::Match(tok, "==|>=|<=")) - return; - if (!then && !Token::Match(tok, "<|>|!=")) - return; - ValueFlow::Value truevalue; - ValueFlow::Value falsevalue; - const Token* vartok = parseCompareInt(tok, truevalue, falsevalue); - if (!vartok) - return; - if (vartok->varId() == 0) - return; - if (!truevalue.isIntValue()) - return; - if (isVariableChanged(tok->next(), endTok, vartok->varId(), false, settings, true)) - return; - pm.setIntValue(vartok->varId(), then ? truevalue.intvalue : falsevalue.intvalue); - } else if (Token::Match(tok, "%var%")) { - if (tok->varId() == 0) - return; - if (then && !astIsPointer(tok) && !astIsBool(tok)) - return; - if (isVariableChanged(tok->next(), endTok, tok->varId(), false, settings, true)) - return; - pm.setIntValue(tok->varId(), then); - } else if (Token::simpleMatch(tok, "!")) { - programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then); - } else if (then && Token::simpleMatch(tok, "&&")) { - programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); - programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); - } else if (!then && Token::simpleMatch(tok, "||")) { - programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); - programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); - } -} - -static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings) -{ - if (!scope) - return; - if (!scope->isLocal()) - return; - assert(scope != scope->nestedIn); - fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings); - if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse) { - const Token * bodyStart = scope->bodyStart; - if (scope->type == Scope::eElse) { - if (!Token::simpleMatch(bodyStart->tokAt(-2), "} else {")) - return; - bodyStart = bodyStart->linkAt(-2); - } - const Token * condEndTok = bodyStart->previous(); - if (!Token::simpleMatch(condEndTok, ") {")) - return; - const Token * condStartTok = condEndTok->link(); - if (!condStartTok) - return; - if (!Token::Match(condStartTok->previous(), "if|while (")) - return; - const Token * condTok = condStartTok->astOperand2(); - programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); - } -} - -static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings) -{ - fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings); -} - -static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const ProgramMemory& state, std::unordered_map vars) -{ - int indentlevel = 0; - for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { - bool setvar = false; - if (Token::Match(tok2, "[;{}] %var% = %var% ;")) { - for (auto&& p:vars) { - if (p.first != tok2->next()->varId()) - continue; - const Token *vartok = tok2->tokAt(3); - pm.setValue(vartok->varId(), p.second); - setvar = true; - } - } - if (!setvar && (Token::Match(tok2, "[;{}] %var% =") || - Token::Match(tok2, "[;{}] const| %type% %var% ("))) { - const Token *vartok = tok2->next(); - while (vartok->next()->isName()) - vartok = vartok->next(); - if (!pm.hasValue(vartok->varId())) { - MathLib::bigint result = 0; - bool error = false; - execute(vartok->next()->astOperand2(), &pm, &result, &error); - if (!error) - pm.setIntValue(vartok->varId(), result); - } - } - - if (tok2->str() == "{") { - if (indentlevel <= 0) - break; - --indentlevel; - } - if (tok2->str() == "}") { - const Token *cond = tok2->link(); - cond = Token::simpleMatch(cond->previous(), ") {") ? cond->linkAt(-1) : nullptr; - if (cond && conditionIsFalse(cond->astOperand2(), state)) - tok2 = cond->previous(); - else if (cond && conditionIsTrue(cond->astOperand2(), state)) { - ++indentlevel; - continue; - } else - break; - } - } -} - -/** - * Get program memory by looking backwards from given token. - */ -static ProgramMemory getProgramMemory(const Token *tok, nonneg int varid, const ValueFlow::Value &value) -{ - ProgramMemory programMemory; - if (value.tokvalue) - fillProgramMemoryFromConditions(programMemory, value.tokvalue, nullptr); - if (value.condition) - fillProgramMemoryFromConditions(programMemory, value.condition, nullptr); - programMemory.setValue(varid, value); - if (value.varId) - programMemory.setIntValue(value.varId, value.varvalue); - const ProgramMemory state = programMemory; - fillProgramMemoryFromAssignments(programMemory, tok, state, {{varid, value}}); - return programMemory; -} - /** * Should value be skipped because it's hidden inside && || or ?: expression. * Example: ((x!=NULL) && (*x == 123)) @@ -4698,199 +4462,6 @@ static void valueFlowAfterCondition(TokenList *tokenlist, handler.afterCondition(tokenlist, symboldatabase, errorLogger, settings); } -static void execute(const Token *expr, - ProgramMemory * const programMemory, - MathLib::bigint *result, - bool *error) -{ - if (!expr) - *error = true; - - else if (expr->hasKnownIntValue()) { - *result = expr->values().front().intvalue; - } - - else if (expr->isNumber()) { - *result = MathLib::toLongNumber(expr->str()); - if (MathLib::isFloat(expr->str())) - *error = true; - } - - else if (expr->varId() > 0) { - if (!programMemory->getIntValue(expr->varId(), result)) - *error = true; - } - - 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; - } - - else if (expr->isAssignmentOp()) { - execute(expr->astOperand2(), programMemory, result, error); - if (!expr->astOperand1() || !expr->astOperand1()->varId()) - *error = true; - if (*error) - return; - - if (expr->str() == "=") { - programMemory->setIntValue(expr->astOperand1()->varId(), *result); - return; - } - - long long intValue; - if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) { - *error = true; - return; - } - if (expr->str() == "+=") - programMemory->setIntValue(expr->astOperand1()->varId(), intValue + *result); - else if (expr->str() == "-=") - programMemory->setIntValue(expr->astOperand1()->varId(), intValue - *result); - else if (expr->str() == "*=") - programMemory->setIntValue(expr->astOperand1()->varId(), intValue * *result); - else if (expr->str() == "/=" && *result != 0) - programMemory->setIntValue(expr->astOperand1()->varId(), intValue / *result); - else if (expr->str() == "%=" && *result != 0) - programMemory->setIntValue(expr->astOperand1()->varId(), intValue % *result); - else if (expr->str() == "&=") - programMemory->setIntValue(expr->astOperand1()->varId(), intValue & *result); - else if (expr->str() == "|=") - programMemory->setIntValue(expr->astOperand1()->varId(), intValue | *result); - else if (expr->str() == "^=") - programMemory->setIntValue(expr->astOperand1()->varId(), intValue ^ *result); - } - - else if (Token::Match(expr, "++|--")) { - if (!expr->astOperand1() || expr->astOperand1()->varId() == 0U) - *error = true; - else { - long long intValue; - if (!programMemory->getIntValue(expr->astOperand1()->varId(), &intValue)) - *error = true; - else { - if (intValue == 0 && - expr->str() == "--" && - expr->astOperand1()->variable() && - expr->astOperand1()->variable()->typeStartToken()->isUnsigned()) - *error = true; // overflow - *result = intValue + (expr->str() == "++" ? 1 : -1); - programMemory->setIntValue(expr->astOperand1()->varId(), *result); - } - } - } - - else if (expr->isArithmeticalOp() && expr->astOperand1() && expr->astOperand2()) { - 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() == "*") { - if (result2 && (result1 > std::numeric_limits::max()/result2)) - *error = true; - else - *result = result1 * result2; - } else if (result2 == 0) - *error = true; - else if (expr->str() == "/") - *result = result1 / result2; - else if (expr->str() == "%") - *result = result1 % result2; - else if (expr->str() == "<<") { - if (result2 < 0 || result1 < 0 || result2 >= MathLib::bigint_bits) { // don't perform UB - *error= true; - } else { - *result = result1 << result2; - } - } else if (expr->str() == ">>") { - if (result2 < 0) { // don't perform UB - *error=true; - } else { - *result = result1 >> result2; - } - } - } - - else if (expr->str() == "&&") { - bool error1 = false; - execute(expr->astOperand1(), programMemory, result, &error1); - if (!error1 && *result == 0) - *result = 0; - else { - bool error2 = false; - execute(expr->astOperand2(), programMemory, result, &error2); - if (error1 && error2) - *error = true; - if (error2) - *result = 1; - else - *result = !!*result; - } - } - - else if (expr->str() == "||") { - execute(expr->astOperand1(), programMemory, result, error); - if (*result == 0 && *error == false) - execute(expr->astOperand2(), programMemory, result, error); - } - - else if (expr->str() == "!") { - execute(expr->astOperand1(), programMemory, result, error); - *result = !(*result); - } - - else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { - execute(expr->astOperand1(), programMemory, result, error); - execute(expr->astOperand2(), programMemory, result, error); - } - - else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { - const Token *tokvalue = nullptr; - if (!programMemory->getTokValue(expr->astOperand1()->varId(), &tokvalue)) { - auto tokvalue_it = std::find_if(expr->astOperand1()->values().begin(), - expr->astOperand1()->values().end(), - std::mem_fn(&ValueFlow::Value::isTokValue)); - if (tokvalue_it == expr->astOperand1()->values().end()) { - *error = true; - return; - } - tokvalue = tokvalue_it->tokvalue; - } - if (!tokvalue || !tokvalue->isLiteral()) { - *error = true; - return; - } - const std::string strValue = tokvalue->strValue(); - MathLib::bigint index = 0; - execute(expr->astOperand2(), programMemory, &index, error); - if (index >= 0 && index < strValue.size()) - *result = strValue[index]; - else if (index == strValue.size()) - *result = 0; - else - *error = true; - } - - else - *error = true; -} - static bool isInBounds(const ValueFlow::Value& value, MathLib::bigint x) { if (value.intvalue == x) diff --git a/lib/valueflow.h b/lib/valueflow.h index fdb81de93..b7b4ea21f 100644 --- a/lib/valueflow.h +++ b/lib/valueflow.h @@ -354,6 +354,8 @@ struct LifetimeToken { } }; +const Token *parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value); + std::vector getLifetimeTokens(const Token* tok, ValueFlow::Value::ErrorPath errorPath = ValueFlow::Value::ErrorPath{}, int depth = 20); const Variable* getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr);