ExprEngine; Better handling of function calls

This commit is contained in:
Daniel Marjamäki 2020-06-20 23:00:39 +02:00
parent 6743971112
commit f270ca1909
3 changed files with 239 additions and 14 deletions

View File

@ -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();

View File

@ -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<std::string, ExprEngine::ValuePtr> 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> importData;
parsestr(s, &importData);
//simplify(importData);
if (importData.empty())
return;
std::map<std::string, ExprEngine::ValuePtr> symbols;
for (auto mem: memory) {
getSymbols(symbols, mem.second);
}
// TODO: combined symbolvalue
std::map<int, std::string> 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<ExprEngine::UninitValue>();
symbols[name] = uninitValue;
memory[varid] = uninitValue;
continue;
}
if (std::isdigit(name[0])) {
long long v = std::stoi(name);
auto intRange = std::make_shared<ExprEngine::IntRange>(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<int, std::string> mem;
std::map<std::string, std::string> sym;
std::vector<std::string> constraints;
};
void parsestr(const std::string &s, std::vector<ImportData> *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<std::string, ExprEngine::ValuePtr> &symbols, ExprEngine::ValuePtr val) const {
if (!val)
return;
symbols[val->name] = val;
if (auto arrayValue = std::dynamic_pointer_cast<ExprEngine::ArrayValue>(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<ExprEngine::StructValue>(val)) {
for (auto memberNameValue: structValue->member)
getSymbols(symbols, memberNameValue.second);
}
}
};
}
@ -1271,7 +1442,7 @@ static void call(const std::vector<ExprEngine::Callback> &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<const Token *> &argTokens = getArguments(tok);
std::vector<ExprEngine::ValuePtr> 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<const Token *, nonneg int> 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<ExprEngine::Callback> &callbacks, std::ostream &report)

View File

@ -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)