Dead pointer: Added checking for dead pointer usage when pointer alias local variable that has gone out of scope.
This commit is contained in:
parent
eac337b955
commit
a2f776b1b7
|
@ -1930,3 +1930,38 @@ void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string
|
|||
"uninitStructMember",
|
||||
"Uninitialized struct member: " + membername);
|
||||
}
|
||||
|
||||
void CheckUninitVar::deadPointer()
|
||||
{
|
||||
const bool cpp = _tokenizer->isCPP();
|
||||
const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase();
|
||||
std::list<Scope>::const_iterator scope;
|
||||
|
||||
// check every executable scope
|
||||
for (scope = symbolDatabase->scopeList.begin(); scope != symbolDatabase->scopeList.end(); ++scope) {
|
||||
if (!scope->isExecutable())
|
||||
continue;
|
||||
// Dead pointers..
|
||||
for (const Token* tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) {
|
||||
if (tok->variable() &&
|
||||
tok->variable()->isPointer() &&
|
||||
isVariableUsage(tok, true, false, cpp)) {
|
||||
const Token *alias = tok->getValueTokenDeadPointer();
|
||||
if (alias) {
|
||||
deadPointerError(tok,alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheckUninitVar::deadPointerError(const Token *pointer, const Token *alias)
|
||||
{
|
||||
const std::string strpointer(pointer ? pointer->str() : std::string("pointer"));
|
||||
const std::string stralias(alias ? alias->expressionString() : std::string("&x"));
|
||||
|
||||
reportError(pointer,
|
||||
Severity::error,
|
||||
"deadpointer",
|
||||
"Dead pointer usage. Pointer '" + strpointer + "' is dead if it has been assigned '" + stralias + "' at line " + MathLib::toString(alias ? alias->linenr() : 0U) + ".");
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger);
|
||||
checkUninitVar.executionPaths();
|
||||
checkUninitVar.check();
|
||||
checkUninitVar.deadPointer();
|
||||
}
|
||||
|
||||
/** Check for uninitialized variables */
|
||||
|
@ -64,6 +65,10 @@ public:
|
|||
static bool isMemberVariableAssignment(const Token *tok, const std::string &membervar);
|
||||
bool isMemberVariableUsage(const Token *tok, bool isPointer, bool alloc, const std::string &membervar) const;
|
||||
|
||||
/** ValueFlow-based checking for dead pointer usage */
|
||||
void deadPointer();
|
||||
void deadPointerError(const Token *pointer, const Token *alias);
|
||||
|
||||
/**
|
||||
* @brief Uninitialized variables: analyse functions to see how they work with uninitialized variables
|
||||
* @param tokens [in] the token list
|
||||
|
@ -94,6 +99,7 @@ private:
|
|||
c.uninitdataError(0, "varname");
|
||||
c.uninitvarError(0, "varname");
|
||||
c.uninitStructMemberError(0, "a.b");
|
||||
c.deadPointerError(0,0);
|
||||
}
|
||||
|
||||
static std::string myName() {
|
||||
|
@ -102,7 +108,8 @@ private:
|
|||
|
||||
std::string classInfo() const {
|
||||
return "Uninitialized variables\n"
|
||||
"* using uninitialized variables and data\n";
|
||||
"* using uninitialized variables and data\n"
|
||||
"* using dead pointer\n";
|
||||
}
|
||||
};
|
||||
/// @}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "errorlogger.h"
|
||||
#include "check.h"
|
||||
#include "settings.h"
|
||||
#include "symboldatabase.h"
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
@ -1317,6 +1318,29 @@ const Token *Token::getValueTokenMaxStrLength() const
|
|||
return ret;
|
||||
}
|
||||
|
||||
const Token *Token::getValueTokenDeadPointer() const
|
||||
{
|
||||
std::list<ValueFlow::Value>::const_iterator it;
|
||||
for (it = values.begin(); it != values.end(); ++it) {
|
||||
// Is this a pointer alias?
|
||||
if (!it->tokvalue || it->tokvalue->str() != "&")
|
||||
continue;
|
||||
// Get variable
|
||||
const Token *vartok = it->tokvalue->astOperand1();
|
||||
if (!vartok || !vartok->isName() || !vartok->variable())
|
||||
continue;
|
||||
const Variable * const var = vartok->variable();
|
||||
if (var->isStatic())
|
||||
continue;
|
||||
const Scope *s = this->scope();
|
||||
while ((s != nullptr) && (s != var->scope()))
|
||||
s = s->nestedIn;
|
||||
if (!s)
|
||||
return it->tokvalue;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Token::assignProgressValues(Token *tok)
|
||||
{
|
||||
unsigned int total_count = 0;
|
||||
|
|
|
@ -673,6 +673,8 @@ public:
|
|||
const Token *getValueTokenMaxStrLength() const;
|
||||
const Token *getValueTokenMinStrSize() const;
|
||||
|
||||
const Token *getValueTokenDeadPointer() const;
|
||||
|
||||
private:
|
||||
|
||||
void next(Token *nextToken) {
|
||||
|
|
|
@ -364,7 +364,7 @@ static void valueFlowPointerAlias(TokenList *tokenlist)
|
|||
continue;
|
||||
|
||||
// child should be some buffer or variable
|
||||
if (!Token::Match(tok->astOperand1(), "%var%|.|["))
|
||||
if (!Token::Match(tok->astOperand1(), "%var%|.|[|;"))
|
||||
continue;
|
||||
|
||||
ValueFlow::Value value;
|
||||
|
|
|
@ -73,6 +73,9 @@ private:
|
|||
|
||||
// Test that std.cfg is configured correctly
|
||||
TEST_CASE(stdcfg);
|
||||
|
||||
// dead pointer
|
||||
TEST_CASE(deadPointer);
|
||||
}
|
||||
|
||||
void checkUninitVar(const char code[], const char filename[] = "test.cpp") {
|
||||
|
@ -3701,6 +3704,44 @@ private:
|
|||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void checkDeadPointer(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..
|
||||
CheckUninitVar check(&tokenizer, &settings, this);
|
||||
check.deadPointer();
|
||||
}
|
||||
|
||||
void deadPointer() {
|
||||
checkDeadPointer("void f() {\n"
|
||||
" int *p = p1;\n"
|
||||
" if (cond) {\n"
|
||||
" int x;\n"
|
||||
" p = &x;\n"
|
||||
" }\n"
|
||||
" *p = 0;\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:7]: (error) Dead pointer usage. Pointer 'p' is dead if it has been assigned '&x' at line 5.\n", errout.str());
|
||||
|
||||
checkDeadPointer("void a(const int *p) {\n"
|
||||
" *p = 0;\n"
|
||||
"}\n"
|
||||
"\n"
|
||||
"void b() {\n"
|
||||
" int x;\n"
|
||||
" int *p = &x;"
|
||||
" a(p);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TEST(TestUninitVar)
|
||||
|
|
|
@ -186,6 +186,14 @@ private:
|
|||
" return x;\n"
|
||||
"}";
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 5, "& ret [ 0 ]"));
|
||||
|
||||
// dead pointer
|
||||
code = "void f() {\n"
|
||||
" int *x;\n"
|
||||
" if (cond) { int i; x = &i; }\n"
|
||||
" *x = 0;\n" // <- x can point at i
|
||||
"}";
|
||||
ASSERT_EQUALS(true, testValueOfX(code, 4, "& i"));
|
||||
}
|
||||
|
||||
void valueFlowCalculations() {
|
||||
|
|
Loading…
Reference in New Issue