diff --git a/lib/bughuntingchecks.cpp b/lib/bughuntingchecks.cpp index c01e64bfc..4f4e18904 100644 --- a/lib/bughuntingchecks.cpp +++ b/lib/bughuntingchecks.cpp @@ -227,7 +227,8 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine: int count = 0; if (Token::simpleMatch(parent, ",")) { if (tok == parent->astOperand2()) - ++count; + count = 1; + parent = parent->astParent(); while (Token::simpleMatch(parent, ",")) { count++; parent = parent->astParent(); diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index 188ec42d8..bbaf64ce6 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -61,7 +61,7 @@ * ================== * * The function: - * static void execute(const Token *start, const Token *end, Data &data) + * static std::string execute(const Token *start, const Token *end, Data &data) * * Perform abstract execution of the code from `start` to `end`. The * `data` is modified during the abstract execution. @@ -156,6 +156,27 @@ namespace { }; } +static std::string str(ExprEngine::ValuePtr val) +{ + const char * const valueTypeStr[] = { + "UninitValue", + "IntRange", + "FloatRange", + "ConditionalValue", + "ArrayValue", + "StringLiteralValue", + "StructValue", + "AddressOfValue", + "BinOpResult", + "IntegerTruncation", + "BailoutValue" + }; + + std::ostringstream ret; + ret << val->name << "=" << valueTypeStr[(int)val->type] << "(" << val->getRange() << ")"; + return ret.str(); +} + std::string ExprEngine::str(int128_t value) { std::ostringstream ostr; @@ -510,9 +531,159 @@ namespace { errorLogger->reportErr(errmsg); } + std::string str() const { + std::ostringstream ret; + std::map vars; + for (const auto mem: memory) { + const Variable *var = tokenizer->getSymbolDatabase()->getVariableFromVarId(mem.first); + if (var && var->isLocal()) + continue; + ret << " @" << mem.first << ":" << mem.second->name; + getSymbols(vars, mem.second); + } + for (const auto var: vars) { + if (var.second->name[0] == '$') + ret << " " << ::str(var.second); + } + for (const auto c: constraints) + ret << " (" << c->getSymbolicExpression() << ")"; + ret << std::endl; + return ret.str(); + } + + void load(const std::string &s) { + std::vector importData; + parsestr(s, &importData); + //simplify(importData); + + if (importData.empty()) + return; + + std::map symbols; + for (auto mem: memory) { + getSymbols(symbols, mem.second); + } + + // TODO: combined symbolvalue + std::map combinedMemory; + for (const ImportData &d: importData) { + for (const auto &mem: d.mem) { + auto c = combinedMemory.find(mem.first); + if (c == combinedMemory.end()) { + combinedMemory[mem.first] = mem.second; + continue; + } + if (c->second == mem.second) + continue; + if (c->second == "?" || mem.second == "?") + c->second = "?"; + else + c->second.clear(); + } + } + + for (const auto &mem: combinedMemory) { + int varid = mem.first; + const std::string &name = mem.second; + auto it = memory.find(varid); + if (it != memory.end() && it->second->name == name) + continue; + if (name.empty()) { + if (it != memory.end()) + memory.erase(it); + continue; + } + auto it2 = symbols.find(name); + if (it2 != symbols.end()) { + memory[varid] = it2->second; + continue; + } + if (name == "?") { + auto uninitValue = std::make_shared(); + symbols[name] = uninitValue; + memory[varid] = uninitValue; + continue; + } + if (std::isdigit(name[0])) { + long long v = std::stoi(name); + auto intRange = std::make_shared(name, v, v); + symbols[name] = intRange; + memory[varid] = intRange; + continue; + } + // TODO: handle this value.. + if (it != memory.end()) + memory.erase(it); + } + } + private: TrackExecution * const mTrackExecution; const int mDataIndex; + + struct ImportData { + std::map mem; + std::map sym; + std::vector constraints; + }; + + void parsestr(const std::string &s, std::vector *importData) const { + std::string line; + std::istringstream istr(s); + while (std::getline(istr, line)) { + if (line.empty()) + continue; + line += " "; + ImportData d; + for (std::string::size_type pos = 0; pos < line.size();) { + pos = line.find_first_not_of(" ", pos); + if (pos == std::string::npos) + break; + if (line[pos] == '@') { + ++pos; + std::string::size_type colon = line.find(":", pos); + std::string::size_type end = line.find(" ", colon); + const std::string lhs = line.substr(pos, colon-pos); + pos = colon + 1; + const std::string rhs = line.substr(pos, end-pos); + d.mem[std::stoi(lhs)] = rhs; + pos = end; + } else if (line[pos] == '$') { + const std::string::size_type eq = line.find("=", pos); + const std::string lhs = line.substr(pos, eq-pos); + pos = eq + 1; + const std::string::size_type end = line.find(" ", pos); + const std::string rhs = line.substr(pos, end-pos); + pos = end; + d.sym[lhs] = rhs; + } else if (line[pos] == '(') { + const std::string::size_type end = line.find(" ", pos); + const std::string c = line.substr(pos, end-pos); + pos = end; + d.constraints.push_back(c); + } + } + importData->push_back(d); + } + } + + void getSymbols(std::map &symbols, ExprEngine::ValuePtr val) const { + if (!val) + return; + symbols[val->name] = val; + if (auto arrayValue = std::dynamic_pointer_cast(val)) { + for (auto sizeValue: arrayValue->size) + getSymbols(symbols, sizeValue); + for (auto indexValue: arrayValue->data) { + getSymbols(symbols, indexValue.index); + getSymbols(symbols, indexValue.value); + } + } + if (auto structValue = std::dynamic_pointer_cast(val)) { + for (auto memberNameValue: structValue->member) + getSymbols(symbols, memberNameValue.second); + } + } }; } @@ -1271,7 +1442,7 @@ static void call(const std::vector &callbacks, const Token static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data); static ExprEngine::ValuePtr executeExpression1(const Token *tok, Data &data); -static void execute(const Token *start, const Token *end, Data &data); +static std::string execute(const Token *start, const Token *end, Data &data); static ExprEngine::ValuePtr calculateArrayIndex(const Token *tok, Data &data, const ExprEngine::ArrayValue &arrayValue) { @@ -1549,8 +1720,9 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) const bool hasBody = tok->astOperand1()->function() && tok->astOperand1()->function()->hasBody(); + const std::vector &argTokens = getArguments(tok); std::vector argValues; - for (const Token *argtok : getArguments(tok)) { + for (const Token *argtok : argTokens) { auto val = hasBody ? executeExpression1(argtok, data) : executeExpression(argtok, data); argValues.push_back(val); if (hasBody) @@ -1590,9 +1762,12 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) if (function->hasBody()) { const Scope *functionScope = function->functionScope; int argnr = 0; + std::map refs; for (const Variable &arg: function->argumentList) { - if (argnr < argValues.size()) { - if (!arg.isReference()) + if (argnr < argValues.size() && arg.declarationId() > 0) { + if (arg.isReference()) + refs[argTokens[argnr]] = arg.declarationId(); + else argValues[argnr] = translateUninitValueToRange(argValues[argnr], arg.valueType(), data); data.assignValue(function->functionScope->bodyStart, arg.declarationId(), argValues[argnr]); } @@ -1602,7 +1777,11 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) data.contractConstraints(function, executeExpression1); data.errorPath.push_back(ErrorPathItem(tok, "Calling " + function->name())); try { - execute(functionScope->bodyStart, functionScope->bodyEnd, data); + data.load(execute(functionScope->bodyStart, functionScope->bodyEnd, data)); + for (auto ref: refs) { + auto v = data.getValue(ref.second, nullptr, nullptr); + assignExprValue(ref.first, v, data); + } } catch (BugHuntingException &e) { e.tok = tok; throw e; // cppcheck-suppress exceptRethrowCopy @@ -1901,11 +2080,11 @@ static ExprEngine::ValuePtr executeExpression(const Token *tok, Data &data) static ExprEngine::ValuePtr createVariableValue(const Variable &var, Data &data); -static void execute(const Token *start, const Token *end, Data &data) +static std::string execute(const Token *start, const Token *end, Data &data) { if (data.recursion > 20) // FIXME - return; + return data.str(); // Update data.recursion struct Recursion { @@ -1935,7 +2114,7 @@ static void execute(const Token *start, const Token *end, Data &data) scope = scope->nestedIn; tok = scope->bodyEnd; if (!precedes(tok,end)) - return; + return data.str(); } if (Token::simpleMatch(tok, "try")) @@ -1968,13 +2147,13 @@ static void execute(const Token *start, const Token *end, Data &data) } else if (!tok->astParent() && (tok->astOperand1() || tok->astOperand2())) { executeExpression(tok, data); if (Token::Match(tok, "throw|return")) - return; + return data.str(); } else if (Token::simpleMatch(tok, "if (")) { const Token *cond = tok->next()->astOperand2(); // TODO: C++17 condition const ExprEngine::ValuePtr condValue = executeExpression(cond, data); - Data thenData(data); + Data &thenData(data); Data elseData(data); thenData.addConstraint(condValue, true); elseData.addConstraint(condValue, false); @@ -2006,7 +2185,8 @@ static void execute(const Token *start, const Token *end, Data &data) if (exceptionToken) throw BugHuntingException(exceptionToken, exceptionMessage); - return; + + return thenData.str() + elseData.str(); } else if (Token::simpleMatch(tok, "switch (")) { @@ -2017,9 +2197,11 @@ static void execute(const Token *start, const Token *end, Data &data) Data defaultData(data); const Token *exceptionToken = nullptr; std::string exceptionMessage; + std::ostringstream ret; auto exec = [&](const Token *tok1, const Token *tok2, Data& data) { try { execute(tok1, tok2, data); + ret << data.str(); } catch (BugHuntingException &e) { if (!exceptionToken || (e.tok && precedes(e.tok, exceptionToken))) { exceptionToken = e.tok; @@ -2046,7 +2228,7 @@ static void execute(const Token *start, const Token *end, Data &data) exec(defaultStart ? defaultStart : bodyEnd, end, defaultData); if (exceptionToken) throw BugHuntingException(exceptionToken, exceptionMessage); - return; + return ret.str(); } if (Token::Match(tok, "for|while (") && Token::simpleMatch(tok->linkAt(1), ") {")) { @@ -2136,6 +2318,8 @@ static void execute(const Token *start, const Token *end, Data &data) if (Token::simpleMatch(tok, "} else {")) tok = tok->linkAt(2); } + + return data.str(); } void ExprEngine::executeAllFunctions(ErrorLogger *errorLogger, const Tokenizer *tokenizer, const Settings *settings, const std::vector &callbacks, std::ostream &report) diff --git a/test/testbughuntingchecks.cpp b/test/testbughuntingchecks.cpp index 077381a99..fc1f11cd4 100644 --- a/test/testbughuntingchecks.cpp +++ b/test/testbughuntingchecks.cpp @@ -31,6 +31,7 @@ private: void run() OVERRIDE { #ifdef USE_Z3 TEST_CASE(uninit); + TEST_CASE(ctu); #endif } @@ -54,6 +55,45 @@ private: check("void foo() { int x; x++; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str()); } + + void ctu() { + check("void init(int &x) {\n" + " x = 1;\n" + "}\n" + "\n" + "void foo() {\n" + " int x;\n" + " init(x);\n" + " x++;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + + check("void init(int a, int &x) {\n" + " if (a < 10)\n" + " x = 1;\n" + "}\n" + "\n" + "void foo(int a) {\n" + " int x;\n" + " init(a, x);\n" + " x++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Cannot determine that 'x' is initialized\n", errout.str()); + + check("void init(int a, int &x) {\n" + " if (a < 10)\n" + " x = 1;\n" + " else\n" + " x = 3;\n" + "}\n" + "\n" + "void foo(int a) {\n" + " int x;\n" + " init(a, x);\n" + " x++;\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } }; REGISTER_TEST(TestBughuntingChecks)