value flow: replacing executionpath checking of null pointers
This commit is contained in:
parent
43db1ee797
commit
f3f7e6d302
|
@ -593,13 +593,17 @@ void CheckNullPointer::nullPointerByDeRefAndChec()
|
||||||
// Pointer dereference.
|
// Pointer dereference.
|
||||||
bool unknown = false;
|
bool unknown = false;
|
||||||
if (!isPointerDeRef(tok,unknown)) {
|
if (!isPointerDeRef(tok,unknown)) {
|
||||||
if (_settings->inconclusive && unknown && value->condition)
|
if (_settings->inconclusive && unknown) {
|
||||||
nullPointerError(tok, tok->str(), value->condition, true);
|
if (value->condition == NULL)
|
||||||
|
nullPointerError(tok, tok->str(), true);
|
||||||
|
else
|
||||||
|
nullPointerError(tok, tok->str(), value->condition, true);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value->condition == NULL)
|
if (value->condition == NULL)
|
||||||
nullPointerError(tok, tok->str());
|
nullPointerError(tok, tok->str(), value->inconclusive);
|
||||||
else if (_settings->isEnabled("warning"))
|
else if (_settings->isEnabled("warning"))
|
||||||
nullPointerError(tok, tok->str(), value->condition, value->inconclusive);
|
nullPointerError(tok, tok->str(), value->condition, value->inconclusive);
|
||||||
}
|
}
|
||||||
|
@ -1245,9 +1249,9 @@ void CheckNullPointer::nullPointerError(const Token *tok)
|
||||||
reportError(tok, Severity::error, "nullPointer", "Null pointer dereference");
|
reportError(tok, Severity::error, "nullPointer", "Null pointer dereference");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname)
|
void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, bool inconclusive)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::error, "nullPointer", "Possible null pointer dereference: " + varname);
|
reportError(tok, Severity::error, "nullPointer", "Possible null pointer dereference: " + varname, inconclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const Token* nullCheck, bool inconclusive)
|
void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const Token* nullCheck, bool inconclusive)
|
||||||
|
|
|
@ -99,7 +99,7 @@ public:
|
||||||
void executionPaths();
|
void executionPaths();
|
||||||
|
|
||||||
void nullPointerError(const Token *tok); // variable name unknown / doesn't exist
|
void nullPointerError(const Token *tok); // variable name unknown / doesn't exist
|
||||||
void nullPointerError(const Token *tok, const std::string &varname);
|
void nullPointerError(const Token *tok, const std::string &varname, bool inconclusive=false);
|
||||||
void nullPointerError(const Token *tok, const std::string &varname, const Token* nullcheck, bool inconclusive = false);
|
void nullPointerError(const Token *tok, const std::string &varname, const Token* nullcheck, bool inconclusive = false);
|
||||||
void nullPointerDefaultArgError(const Token *tok, const std::string &varname);
|
void nullPointerDefaultArgError(const Token *tok, const std::string &varname);
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -452,17 +452,54 @@ static void valueFlowAfterAssign(TokenList *tokenlist, ErrorLogger *errorLogger,
|
||||||
const Variable *var = tok->astOperand1()->variable();
|
const Variable *var = tok->astOperand1()->variable();
|
||||||
if (!var || !var->isLocal())
|
if (!var || !var->isLocal())
|
||||||
continue;
|
continue;
|
||||||
|
const Token * endToken = 0;
|
||||||
|
for (const Token *tok2 = var->typeStartToken(); tok2; tok2 = tok2->previous()) {
|
||||||
|
if (tok2->str() == "{") {
|
||||||
|
endToken = tok2->link();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lhs values..
|
// Lhs values..
|
||||||
if (!tok->astOperand2() || tok->astOperand2()->values.empty())
|
if (!tok->astOperand2() || tok->astOperand2()->values.empty())
|
||||||
continue;
|
continue;
|
||||||
std::list<ValueFlow::Value> values = tok->astOperand2()->values;
|
std::list<ValueFlow::Value> values = tok->astOperand2()->values;
|
||||||
|
|
||||||
for (Token *tok2 = tok; tok2; tok2 = tok2->next()) {
|
unsigned int number_of_if = 0;
|
||||||
if (Token::Match(tok2, "[{}]"))
|
|
||||||
break;
|
for (Token *tok2 = tok; tok2 && tok2 != endToken; tok2 = tok2->next()) {
|
||||||
if (Token::Match(tok2, "sizeof|typeof|typeid ("))
|
if (Token::Match(tok2, "sizeof|typeof|typeid ("))
|
||||||
tok2 = tok2->linkAt(1);
|
tok2 = tok2->linkAt(1);
|
||||||
|
|
||||||
|
// conditional block of code that assigns variable..
|
||||||
|
if (Token::Match(tok2, "%var% (") && Token::Match(tok2->linkAt(1), ") {")) {
|
||||||
|
Token * const start = tok2->linkAt(1)->next();
|
||||||
|
Token * const end = start->link();
|
||||||
|
// TODO: don't check noreturn scopes
|
||||||
|
if (number_of_if > 0U && Token::findmatch(start, "%varid%", end, varid)) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in conditional code");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (Token::findmatch(start, "++|--| %varid% ++|--|=", end, varid)) {
|
||||||
|
if (number_of_if == 0 &&
|
||||||
|
Token::simpleMatch(tok2, "if (") &&
|
||||||
|
!(Token::simpleMatch(end, "} else {") &&
|
||||||
|
(Token::findmatch(end, "%varid%", end->linkAt(2), varid) ||
|
||||||
|
Token::findmatch(end, "return|continue|break", end->linkAt(2))))) {
|
||||||
|
++number_of_if;
|
||||||
|
tok2 = end;
|
||||||
|
} else {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist, errorLogger, tok2, "variable " + var->nameToken()->str() + " is assigned in conditional code");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (tok2->str() == "}")
|
||||||
|
++number_of_if;
|
||||||
|
|
||||||
if (tok2->varId() == varid) {
|
if (tok2->varId() == varid) {
|
||||||
// bailout: assignment
|
// bailout: assignment
|
||||||
if (Token::Match(tok2->previous(), "!!* %var% =")) {
|
if (Token::Match(tok2->previous(), "!!* %var% =")) {
|
||||||
|
@ -471,6 +508,16 @@ static void valueFlowAfterAssign(TokenList *tokenlist, ErrorLogger *errorLogger,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// skip if variable is conditionally used in ?: expression
|
||||||
|
if (const Token *parent = skipValueInConditionalExpression(tok2)) {
|
||||||
|
if (settings->debugwarnings)
|
||||||
|
bailout(tokenlist,
|
||||||
|
errorLogger,
|
||||||
|
tok2,
|
||||||
|
"no simplification of " + tok2->str() + " within " + (Token::Match(parent,"[?:]") ? "?:" : parent->str()) + " expression");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
std::list<ValueFlow::Value>::const_iterator it;
|
std::list<ValueFlow::Value>::const_iterator it;
|
||||||
for (it = values.begin(); it != values.end(); ++it)
|
for (it = values.begin(); it != values.end(); ++it)
|
||||||
|
|
|
@ -113,7 +113,7 @@ private:
|
||||||
"TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str());
|
"TestSimplifyTokens instead.\nstr1="+str1+"\nstr2="+str2).c_str());
|
||||||
|
|
||||||
checkNullPointer.nullConstantDereference();
|
checkNullPointer.nullConstantDereference();
|
||||||
checkNullPointer.executionPaths();
|
//checkNullPointer.executionPaths();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -881,8 +881,7 @@ private:
|
||||||
ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout.str());
|
||||||
|
|
||||||
{
|
{
|
||||||
const char code[] = "static void foo(int x)\n"
|
const char code[] = "static void foo() {\n"
|
||||||
"{\n"
|
|
||||||
" Foo<int> *abc = 0;\n"
|
" Foo<int> *abc = 0;\n"
|
||||||
" abc->a();\n"
|
" abc->a();\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
|
@ -893,7 +892,7 @@ private:
|
||||||
|
|
||||||
// inconclusive=true => error
|
// inconclusive=true => error
|
||||||
check(code, true);
|
check(code, true);
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: abc\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3]: (error, inconclusive) Possible null pointer dereference: abc\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
check("static void foo() {\n"
|
check("static void foo() {\n"
|
||||||
|
@ -1058,7 +1057,7 @@ private:
|
||||||
// No false positive:
|
// No false positive:
|
||||||
check("void foo() {\n"
|
check("void foo() {\n"
|
||||||
" int n;\n"
|
" int n;\n"
|
||||||
" int *argv32;\n"
|
" int *argv32 = p;\n"
|
||||||
" if (x) {\n"
|
" if (x) {\n"
|
||||||
" n = 0;\n"
|
" n = 0;\n"
|
||||||
" argv32 = 0;\n"
|
" argv32 = 0;\n"
|
||||||
|
@ -1099,7 +1098,7 @@ private:
|
||||||
"\n"
|
"\n"
|
||||||
" *p = 0;\n"
|
" *p = 0;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:11]: (error) Possible null pointer dereference: p\n", errout.str());
|
TODO_ASSERT_EQUALS("[test.cpp:11]: (error) Possible null pointer dereference: p\n", "", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void nullpointer7() {
|
void nullpointer7() {
|
||||||
|
@ -1255,7 +1254,7 @@ private:
|
||||||
" if (x) p = q;\n"
|
" if (x) p = q;\n"
|
||||||
" if (y ? p->x : p->y) { }\n"
|
" if (y ? p->x : p->y) { }\n"
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("error", "", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Possible null pointer dereference: p\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void nullpointer21() { // #4038 - fp: if (x) p=q; else return;
|
void nullpointer21() { // #4038 - fp: if (x) p=q; else return;
|
||||||
|
@ -1875,7 +1874,7 @@ private:
|
||||||
" int* iVal = 0;\n"
|
" int* iVal = 0;\n"
|
||||||
" sscanf(dummy, \"%d%d\", foo(iVal), iVal);\n"
|
" sscanf(dummy, \"%d%d\", foo(iVal), iVal);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: iVal\n", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
check("void f(char* dummy) {\n"
|
check("void f(char* dummy) {\n"
|
||||||
" sscanf(dummy, \"%*d%u\", 0);\n"
|
" sscanf(dummy, \"%*d%u\", 0);\n"
|
||||||
|
@ -1979,8 +1978,11 @@ private:
|
||||||
"[test.cpp:4]: (error) Null pointer dereference\n"
|
"[test.cpp:4]: (error) Null pointer dereference\n"
|
||||||
"[test.cpp:5]: (error) Null pointer dereference\n"
|
"[test.cpp:5]: (error) Null pointer dereference\n"
|
||||||
"[test.cpp:6]: (error) Null pointer dereference\n"
|
"[test.cpp:6]: (error) Null pointer dereference\n"
|
||||||
|
/* TODO: handle std::string
|
||||||
"[test.cpp:9]: (error) Possible null pointer dereference: p\n"
|
"[test.cpp:9]: (error) Possible null pointer dereference: p\n"
|
||||||
"[test.cpp:10]: (error) Possible null pointer dereference: p\n", errout.str());
|
"[test.cpp:10]: (error) Possible null pointer dereference: p\n"
|
||||||
|
*/
|
||||||
|
, errout.str());
|
||||||
|
|
||||||
check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n"
|
check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n"
|
||||||
" void* p = 0;\n"
|
" void* p = 0;\n"
|
||||||
|
|
|
@ -476,6 +476,37 @@ private:
|
||||||
" a = 2 + x;\n"
|
" a = 2 + x;\n"
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
|
ASSERT_EQUALS(true, testValueOfX(code, 3U, 123));
|
||||||
|
|
||||||
|
// if/else
|
||||||
|
code = "void f() {\n"
|
||||||
|
" int x = 123;\n"
|
||||||
|
" if (condition) return;\n"
|
||||||
|
" a = 2 + x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 123));
|
||||||
|
|
||||||
|
code = "void f() {\n"
|
||||||
|
" int x = 1;\n"
|
||||||
|
" if (condition) x = 2;\n"
|
||||||
|
" a = 2 + x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(true, testValueOfX(code, 4U, 1));
|
||||||
|
|
||||||
|
code = "void f() {\n"
|
||||||
|
" int x = 123;\n"
|
||||||
|
" if (condition1) x = 456;\n"
|
||||||
|
" if (condition2) x = 789;\n"
|
||||||
|
" a = 2 + x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(false, testValueOfX(code, 4U, 123));
|
||||||
|
|
||||||
|
code = "void f() {\n"
|
||||||
|
" int x = 1;\n"
|
||||||
|
" if (condition1) x = 2;\n"
|
||||||
|
" else return;\n"
|
||||||
|
" a = 2 + x;\n"
|
||||||
|
"}";
|
||||||
|
ASSERT_EQUALS(false, testValueOfX(code, 5U, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowForLoop() {
|
void valueFlowForLoop() {
|
||||||
|
|
Loading…
Reference in New Issue