Null pointers: Fixed false positives when running whole program analysis. Copied the fix from the CheckUninitVar::isUnsafeFunction.
This commit is contained in:
parent
a61f21d1b6
commit
4998248501
|
@ -20,6 +20,7 @@
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
#include "checknullpointer.h"
|
#include "checknullpointer.h"
|
||||||
|
|
||||||
|
#include "astutils.h"
|
||||||
#include "errorlogger.h"
|
#include "errorlogger.h"
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
@ -548,12 +549,19 @@ void CheckNullPointer::arithmeticError(const Token *tok, const ValueFlow::Value
|
||||||
value && value->isInconclusive());
|
value && value->isInconclusive());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CheckNullPointer::isUnsafeFunction(const Scope *scope, int argnr, const Token **tok)
|
bool CheckNullPointer::isUnsafeFunction(const Scope *scope, int argnr, const Token **tok) const
|
||||||
{
|
{
|
||||||
const Variable * const argvar = scope->function->getArgumentVar(argnr);
|
const Variable * const argvar = scope->function->getArgumentVar(argnr);
|
||||||
if (!argvar->isPointer())
|
if (!argvar->isPointer())
|
||||||
return false;
|
return false;
|
||||||
for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) {
|
for (const Token *tok2 = scope->classStart; tok2 != scope->classEnd; tok2 = tok2->next()) {
|
||||||
|
if (Token::simpleMatch(tok2, ") {")) {
|
||||||
|
tok2 = tok2->linkAt(1);
|
||||||
|
if (Token::findmatch(tok2->link(), "return|throw", tok2))
|
||||||
|
return false;
|
||||||
|
if (isVariableChanged(tok2->link(), tok2, argvar->declarationId(), false, _settings))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (tok2->variable() != argvar)
|
if (tok2->variable() != argvar)
|
||||||
continue;
|
continue;
|
||||||
if (!Token::Match(tok2->astParent(), "*|["))
|
if (!Token::Match(tok2->astParent(), "*|["))
|
||||||
|
|
|
@ -100,7 +100,7 @@ public:
|
||||||
}
|
}
|
||||||
void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive);
|
void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive);
|
||||||
|
|
||||||
static bool isUnsafeFunction(const Scope *scope, int argnr, const Token **tok);
|
bool isUnsafeFunction(const Scope *scope, int argnr, const Token **tok) const;
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/** Get error messages. Used by --errorlist */
|
/** Get error messages. Used by --errorlist */
|
||||||
|
|
|
@ -1425,11 +1425,12 @@ Check::FileInfo *CheckUninitVar::getFileInfo() const
|
||||||
}
|
}
|
||||||
|
|
||||||
// "Unsafe" functions unconditionally reads data before it is written..
|
// "Unsafe" functions unconditionally reads data before it is written..
|
||||||
|
CheckNullPointer checkNullPointer(_tokenizer, _settings, _errorLogger);
|
||||||
for (int argnr = 0; argnr < function->argCount(); ++argnr) {
|
for (int argnr = 0; argnr < function->argCount(); ++argnr) {
|
||||||
const Token *tok;
|
const Token *tok;
|
||||||
if (isUnsafeFunction(&*scope, argnr, &tok))
|
if (isUnsafeFunction(&*scope, argnr, &tok))
|
||||||
fileInfo->readData.push_back(MyFileInfo::FunctionArg(_tokenizer, &*scope, argnr+1, tok));
|
fileInfo->readData.push_back(MyFileInfo::FunctionArg(_tokenizer, &*scope, argnr+1, tok));
|
||||||
if (CheckNullPointer::isUnsafeFunction(&*scope, argnr, &tok))
|
if (checkNullPointer.isUnsafeFunction(&*scope, argnr, &tok))
|
||||||
fileInfo->dereferenced.push_back(MyFileInfo::FunctionArg(_tokenizer, &*scope, argnr+1, tok));
|
fileInfo->dereferenced.push_back(MyFileInfo::FunctionArg(_tokenizer, &*scope, argnr+1, tok));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "checknullpointer.h"
|
#include "checknullpointer.h"
|
||||||
|
#include "checkuninitvar.h"
|
||||||
#include "library.h"
|
#include "library.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "testsuite.h"
|
#include "testsuite.h"
|
||||||
|
@ -105,6 +106,7 @@ private:
|
||||||
TEST_CASE(nullpointer_internal_error); // #5080
|
TEST_CASE(nullpointer_internal_error); // #5080
|
||||||
TEST_CASE(ticket6505);
|
TEST_CASE(ticket6505);
|
||||||
TEST_CASE(subtract);
|
TEST_CASE(subtract);
|
||||||
|
TEST_CASE(ctu);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[], bool inconclusive = false, const char filename[] = "test.cpp") {
|
void check(const char code[], bool inconclusive = false, const char filename[] = "test.cpp") {
|
||||||
|
@ -2572,6 +2574,67 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ctu(const char code[]) {
|
||||||
|
// Clear the error buffer..
|
||||||
|
errout.str("");
|
||||||
|
|
||||||
|
// Tokenize..
|
||||||
|
Tokenizer tokenizer(&settings, this);
|
||||||
|
std::istringstream istr(code);
|
||||||
|
tokenizer.tokenize(istr, "test.cpp");
|
||||||
|
tokenizer.simplifyTokenList2();
|
||||||
|
|
||||||
|
// Check code..
|
||||||
|
std::list<Check::FileInfo*> fileInfo;
|
||||||
|
CheckUninitVar check(&tokenizer, &settings, this);
|
||||||
|
fileInfo.push_back(check.getFileInfo(&tokenizer, &settings));
|
||||||
|
check.analyseWholeProgram(fileInfo, settings, *this);
|
||||||
|
while (!fileInfo.empty()) {
|
||||||
|
delete fileInfo.back();
|
||||||
|
fileInfo.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ctu() {
|
||||||
|
ctu("void f(int *fp) {\n"
|
||||||
|
" a = *fp;\n"
|
||||||
|
"}\n"
|
||||||
|
"int main() {\n"
|
||||||
|
" int *p = 0;\n"
|
||||||
|
" f(p);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Null pointer dereference: fp\n", errout.str());
|
||||||
|
|
||||||
|
ctu("void use(int *p) { a = *p + 3; }\n"
|
||||||
|
"void call(int x, int *p) { x++; use(p); }\n"
|
||||||
|
"int main() {\n"
|
||||||
|
" call(4,0);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (error) Null pointer dereference: p\n", errout.str());
|
||||||
|
|
||||||
|
ctu("void dostuff(int *x, int *y) {\n"
|
||||||
|
" if (!var)\n"
|
||||||
|
" return -1;\n" // <- early return
|
||||||
|
" *x = *y;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" dostuff(a, 0);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
ctu("void dostuff(int *x, int *y) {\n"
|
||||||
|
" if (cond)\n"
|
||||||
|
" *y = -1;\n" // <- conditionally written
|
||||||
|
" *x = *y;\n"
|
||||||
|
"}\n"
|
||||||
|
"\n"
|
||||||
|
"void f() {\n"
|
||||||
|
" dostuff(a, 0);\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestNullPointer)
|
REGISTER_TEST(TestNullPointer)
|
||||||
|
|
Loading…
Reference in New Issue