CheckUninitVar: Removed ExecutionPath based checker
This commit is contained in:
parent
995c4196b5
commit
ea933e9873
|
@ -20,7 +20,6 @@
|
|||
//---------------------------------------------------------------------------
|
||||
#include "checkuninitvar.h"
|
||||
#include "mathlib.h"
|
||||
#include "executionpath.h"
|
||||
#include "checknullpointer.h" // CheckNullPointer::parseFunctionCall
|
||||
#include "symboldatabase.h"
|
||||
#include <algorithm>
|
||||
|
@ -36,891 +35,9 @@ namespace {
|
|||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Skip [ .. ]
|
||||
static const Token * skipBrackets(const Token *tok)
|
||||
{
|
||||
while (tok && tok->str() == "[")
|
||||
tok = tok->link()->next();
|
||||
return tok;
|
||||
}
|
||||
|
||||
|
||||
/// @addtogroup Checks
|
||||
/// @{
|
||||
|
||||
/**
|
||||
* @brief %Check that uninitialized variables aren't used (using ExecutionPath)
|
||||
* */
|
||||
class UninitVar : public ExecutionPath {
|
||||
public:
|
||||
/** Startup constructor */
|
||||
explicit UninitVar(Check *c, const SymbolDatabase* db, const Library *lib, bool isc)
|
||||
: ExecutionPath(c, 0), symbolDatabase(db), library(lib), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) {
|
||||
}
|
||||
|
||||
private:
|
||||
/** Create a copy of this check */
|
||||
ExecutionPath *copy() {
|
||||
return new UninitVar(*this);
|
||||
}
|
||||
|
||||
/** internal constructor for creating extra checks */
|
||||
UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, const Library *lib, bool isc)
|
||||
: ExecutionPath(c, v->declarationId()), symbolDatabase(db), library(lib), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) {
|
||||
}
|
||||
|
||||
/** is other execution path equal? */
|
||||
bool is_equal(const ExecutionPath *e) const {
|
||||
const UninitVar *c = static_cast<const UninitVar *>(e);
|
||||
return (var == c->var && alloc == c->alloc && strncpy_ == c->strncpy_ && memset_nonzero == c->memset_nonzero);
|
||||
}
|
||||
|
||||
/** pointer to symbol database */
|
||||
const SymbolDatabase* symbolDatabase;
|
||||
|
||||
/** pointer to library */
|
||||
const Library *library;
|
||||
|
||||
const bool isC;
|
||||
|
||||
/** variable for this check */
|
||||
const Variable* var;
|
||||
|
||||
/** is this variable allocated? */
|
||||
bool alloc;
|
||||
|
||||
/** is this variable initialized with strncpy (not always zero-terminated) */
|
||||
bool strncpy_;
|
||||
|
||||
/** is this variable initialized but not zero-terminated (memset) */
|
||||
bool memset_nonzero;
|
||||
|
||||
/** allocating pointer. For example : p = malloc(10); */
|
||||
static void alloc_pointer(std::list<ExecutionPath *> &checks, unsigned int varid) {
|
||||
// loop through the checks and perform a allocation if the
|
||||
// variable id matches
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid) {
|
||||
if (c->var->isPointer() && !c->var->isArray())
|
||||
c->alloc = true;
|
||||
else
|
||||
bailOutVar(checks, varid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Initializing a pointer value. For example: *p = 0; */
|
||||
static void init_pointer(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
const unsigned int varid(tok->varId());
|
||||
if (!varid)
|
||||
return;
|
||||
|
||||
// loop through the checks and perform a initialization if the
|
||||
// variable id matches
|
||||
std::list<ExecutionPath *>::iterator it = checks.begin();
|
||||
while (it != checks.end()) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid) {
|
||||
if (c->alloc || c->var->isArray()) {
|
||||
delete c;
|
||||
checks.erase(it++);
|
||||
continue;
|
||||
} else {
|
||||
use_pointer(checks, tok);
|
||||
}
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
/** Deallocate a pointer. For example: free(p); */
|
||||
static void dealloc_pointer(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
const unsigned int varid(tok->varId());
|
||||
if (!varid)
|
||||
return;
|
||||
|
||||
// loop through the checks and perform a deallocation if the
|
||||
// variable id matches
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid) {
|
||||
// unallocated pointer variable => error
|
||||
if (c->var->isPointer() && !c->var->isArray() && !c->alloc) {
|
||||
CheckUninitVar *checkUninitVar = dynamic_cast<CheckUninitVar *>(c->owner);
|
||||
if (checkUninitVar) {
|
||||
checkUninitVar->uninitvarError(tok, c->var->name());
|
||||
break;
|
||||
}
|
||||
}
|
||||
c->alloc = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pointer assignment: p = x;
|
||||
* if p is a pointer and x is an array/pointer then bail out
|
||||
* \param checks all available checks
|
||||
* \param tok1 the "p" token
|
||||
* \param tok2 the "x" token
|
||||
*/
|
||||
static void pointer_assignment(std::list<ExecutionPath *> &checks, const Token *tok1, const Token *tok2) {
|
||||
// Variable id for "left hand side" variable
|
||||
const unsigned int varid1(tok1->varId());
|
||||
if (varid1 == 0)
|
||||
return;
|
||||
|
||||
// Variable id for "right hand side" variable
|
||||
const unsigned int varid2(tok2->varId());
|
||||
if (varid2 == 0)
|
||||
return;
|
||||
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
|
||||
// bail out if first variable is a pointer
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid1 && c->var->isPointer() && !c->var->isArray()) {
|
||||
bailOutVar(checks, varid1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// bail out if second variable is a array/pointer
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid2 && (c->var->isPointer() || c->var->isArray())) {
|
||||
bailOutVar(checks, varid2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Initialize an array with strncpy. */
|
||||
static void init_strncpy(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
const unsigned int varid(tok->varId());
|
||||
if (!varid)
|
||||
return;
|
||||
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid) {
|
||||
c->strncpy_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Initialize an array with memset (not zero). */
|
||||
static void init_memset_nonzero(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
const unsigned int varid(tok->varId());
|
||||
if (!varid)
|
||||
return;
|
||||
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid) {
|
||||
c->memset_nonzero = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* use - called from the use* functions below.
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
* @param mode specific behaviour
|
||||
* @return if error is found, true is returned
|
||||
*/
|
||||
static bool use(std::list<ExecutionPath *> &checks, const Token *tok, const int mode) {
|
||||
const unsigned int varid(tok->varId());
|
||||
if (varid == 0)
|
||||
return false;
|
||||
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == varid) {
|
||||
// mode 0 : the variable is used "directly"
|
||||
// example: .. = var;
|
||||
// it is ok to read the address of an uninitialized array.
|
||||
// it is ok to read the address of an allocated pointer
|
||||
if (mode == 0 && (c->var->isArray() || (c->var->isPointer() && c->alloc)))
|
||||
continue;
|
||||
|
||||
// mode 2 : reading array data with mem.. function. It's ok if the
|
||||
// array is not null-terminated
|
||||
if (mode == 2 && c->strncpy_)
|
||||
continue;
|
||||
|
||||
// mode 3 : bad usage of pointer. if it's not a pointer then the usage is ok.
|
||||
// example: ptr->foo();
|
||||
if (mode == 3 && (!c->var->isPointer() || c->var->isArray()))
|
||||
continue;
|
||||
|
||||
// mode 4 : using dead pointer is invalid.
|
||||
if (mode == 4 && (!c->var->isPointer() || c->var->isArray() || c->alloc))
|
||||
continue;
|
||||
|
||||
// mode 5 : reading uninitialized array or pointer is invalid.
|
||||
if (mode == 5 && (!c->var->isArray() && !c->var->isPointer()))
|
||||
continue;
|
||||
|
||||
CheckUninitVar *checkUninitVar = dynamic_cast<CheckUninitVar *>(c->owner);
|
||||
if (checkUninitVar) {
|
||||
if (c->strncpy_ || c->memset_nonzero) {
|
||||
if (!Token::Match(c->var->typeStartToken(), "char|wchar_t")) {
|
||||
continue;
|
||||
}
|
||||
if (Token::Match(tok->next(), "[")) { // Check if it's not being accessed like: 'str[1]'
|
||||
continue;
|
||||
}
|
||||
checkUninitVar->uninitstringError(tok, c->var->name(), c->strncpy_);
|
||||
} else if (c->var->isPointer() && !c->var->isArray() && c->alloc)
|
||||
checkUninitVar->uninitdataError(tok, c->var->name());
|
||||
else
|
||||
checkUninitVar->uninitvarError(tok, c->var->name());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No error found
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading variable. Use this function in situations when it is
|
||||
* invalid to read the data of the variable but not the address.
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
* @return if error is found, true is returned
|
||||
*/
|
||||
static bool use(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
return use(checks, tok, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading array elements. If the variable is not an array then the usage is ok.
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
*/
|
||||
static void use_array(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
use(checks, tok, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reading array elements with a "mem.." function. It's ok if the array is not null-terminated.
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
*/
|
||||
static void use_array_mem(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
use(checks, tok, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bad pointer usage. If the variable is not a pointer then the usage is ok.
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
* @return if error is found, true is returned
|
||||
*/
|
||||
static bool use_pointer(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
return use(checks, tok, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using variable.. if it's a dead pointer the usage is invalid.
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
* @return if error is found, true is returned
|
||||
*/
|
||||
static bool use_dead_pointer(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
return use(checks, tok, 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Using variable.. reading from uninitialized array or pointer data is invalid.
|
||||
* Example: = x[0];
|
||||
* @param checks all available checks
|
||||
* @param tok variable token
|
||||
* @return if error is found, true is returned
|
||||
*/
|
||||
static bool use_array_or_pointer_data(std::list<ExecutionPath *> &checks, const Token *tok) {
|
||||
return use(checks, tok, 5);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse right hand side expression in statement
|
||||
* @param tok2 start token of rhs
|
||||
* @param checks the execution paths
|
||||
*/
|
||||
static void parserhs(const Token *tok2, std::list<ExecutionPath *> &checks) {
|
||||
// check variable usages in rhs/index
|
||||
while (nullptr != (tok2 = tok2->next())) {
|
||||
if (Token::Match(tok2, "[;)=]"))
|
||||
break;
|
||||
if (Token::Match(tok2, "%name% ("))
|
||||
break;
|
||||
if (Token::Match(tok2, "%name% <") && Token::simpleMatch(tok2->linkAt(1), "> ("))
|
||||
break;
|
||||
if (tok2->varId() &&
|
||||
!Token::Match(tok2->previous(), "&|::") &&
|
||||
!Token::simpleMatch(tok2->tokAt(-2), "& (") &&
|
||||
!Token::Match(tok2->tokAt(1), ")| =")) {
|
||||
// Multiple assignments..
|
||||
if (Token::Match(tok2->next(), ".|[")) {
|
||||
const Token * tok3 = tok2;
|
||||
while (tok3) {
|
||||
if (Token::Match(tok3->next(), ". %name%"))
|
||||
tok3 = tok3->tokAt(2);
|
||||
else if (tok3->strAt(1) == "[")
|
||||
tok3 = tok3->next()->link();
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (tok3 && tok3->strAt(1) == "=")
|
||||
continue;
|
||||
}
|
||||
bool foundError;
|
||||
if (tok2->previous()->str() == "*" || tok2->next()->str() == "[")
|
||||
foundError = use_array_or_pointer_data(checks, tok2);
|
||||
else
|
||||
foundError = use(checks, tok2);
|
||||
|
||||
// prevent duplicate error messages
|
||||
if (foundError) {
|
||||
bailOutVar(checks, tok2->varId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** parse tokens. @sa ExecutionPath::parse */
|
||||
const Token *parse(const Token &tok, std::list<ExecutionPath *> &checks) const {
|
||||
// Variable declaration..
|
||||
if (Token::Match(&tok, "%var% [[;]")) {
|
||||
const Variable* var2 = tok.variable();
|
||||
if (var2 && var2->nameToken() == &tok && !var2->isStatic() && !var2->isExtern() && !Token::simpleMatch(tok.linkAt(1), "] [")) {
|
||||
if (tok.linkAt(1)) { // array
|
||||
const Token* endtok = tok.next();
|
||||
while (endtok->link())
|
||||
endtok = endtok->link()->next();
|
||||
if (endtok->str() != ";")
|
||||
return &tok;
|
||||
}
|
||||
const Scope* parent = var2->scope()->nestedIn;
|
||||
while (parent) {
|
||||
for (std::list<Variable>::const_iterator j = parent->varlist.begin(); j != parent->varlist.end(); ++j) {
|
||||
if (j->name() == var2->name()) {
|
||||
ExecutionPath::bailOutVar(checks, j->declarationId()); // If there is a variable with the same name in other scopes, this might cause false positives, if there are unexpanded macros
|
||||
break;
|
||||
}
|
||||
}
|
||||
parent = parent->nestedIn;
|
||||
}
|
||||
|
||||
if (var2->isPointer())
|
||||
checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC));
|
||||
else if (var2->typeEndToken()->str() != ">") {
|
||||
const bool stdtype = var2->typeStartToken()->isStandardType(); // TODO: change to isC to handle unknown types better
|
||||
if (stdtype && (!var2->isArray() || var2->nameToken()->linkAt(1)->strAt(1) == ";"))
|
||||
checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC));
|
||||
}
|
||||
return &tok;
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.str() == "return") {
|
||||
// is there assignment or ternary operator in the return statement?
|
||||
bool assignment = false;
|
||||
for (const Token *tok2 = tok.next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
|
||||
if (tok2->str() == "=" || (!isC && tok2->str() == ">>") || Token::Match(tok2, "(|, &")) {
|
||||
assignment = true;
|
||||
break;
|
||||
}
|
||||
if (Token::Match(tok2, "[(,] &| %var% [,)]")) {
|
||||
tok2 = tok2->next();
|
||||
if (!tok2->isName())
|
||||
tok2 = tok2->next();
|
||||
ExecutionPath::bailOutVar(checks, tok2->varId());
|
||||
}
|
||||
}
|
||||
|
||||
if (!assignment) {
|
||||
for (const Token *tok2 = tok.next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) {
|
||||
if (tok2->isName() && tok2->strAt(1) == "(")
|
||||
tok2 = tok2->next()->link();
|
||||
|
||||
else if (tok2->varId())
|
||||
use(checks, tok2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.varId()) {
|
||||
// array variable passed as function parameter..
|
||||
if (Token::Match(tok.previous(), "[(,] %var% [+-,)]")) {
|
||||
// #4896 : This checking was removed because of FP,
|
||||
// the new uninitvar checking is used instead to catch
|
||||
// these errors.
|
||||
ExecutionPath::bailOutVar(checks, tok.varId());
|
||||
return &tok;
|
||||
}
|
||||
|
||||
// Used..
|
||||
if (Token::Match(tok.previous(), "[[(,+-*/|=] %var% ]|)|,|;|%op%") && !tok.next()->isAssignmentOp()) {
|
||||
// Taking address of array..
|
||||
std::list<ExecutionPath *>::const_iterator it;
|
||||
for (it = checks.begin(); it != checks.end(); ++it) {
|
||||
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||
if (c && c->varId == tok.varId()) {
|
||||
if (c->var->isArray() || c->alloc)
|
||||
bailOutVar(checks, tok.varId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// initialize reference variable
|
||||
if (Token::Match(tok.tokAt(-3), "& %var% ="))
|
||||
bailOutVar(checks, tok.varId());
|
||||
else
|
||||
use(checks, &tok);
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if ((tok.previous() && tok.previous()->type() == Token::eIncDecOp) || (tok.next() && tok.next()->type() == Token::eIncDecOp)) {
|
||||
use(checks, &tok);
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (Token::Match(tok.previous(), "[;{}] %name% [=[.]")) {
|
||||
if (tok.next()->str() == ".") {
|
||||
if (Token::Match(&tok, "%name% . %name% (")) {
|
||||
const Function *function = tok.tokAt(2)->function();
|
||||
if (function && function->isStatic())
|
||||
return &tok;
|
||||
}
|
||||
if (use_dead_pointer(checks, &tok)) {
|
||||
return &tok;
|
||||
}
|
||||
} else {
|
||||
const Token *tok2 = tok.next();
|
||||
|
||||
if (tok2->str() == "[") {
|
||||
const Token *tok3 = tok2->link();
|
||||
while (Token::simpleMatch(tok3, "] ["))
|
||||
tok3 = tok3->next()->link();
|
||||
|
||||
// Possible initialization
|
||||
if (Token::simpleMatch(tok3, "] >>"))
|
||||
return &tok;
|
||||
|
||||
if (Token::simpleMatch(tok3, "] =")) {
|
||||
if (use_dead_pointer(checks, &tok)) {
|
||||
return &tok;
|
||||
}
|
||||
|
||||
parserhs(tok2, checks);
|
||||
tok2 = tok3->next();
|
||||
}
|
||||
}
|
||||
|
||||
parserhs(tok2, checks);
|
||||
}
|
||||
|
||||
// pointer aliasing?
|
||||
if (Token::Match(tok.tokAt(2), "%name% ;")) {
|
||||
pointer_assignment(checks, &tok, tok.tokAt(2));
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.strAt(1) == "(") {
|
||||
use_pointer(checks, &tok);
|
||||
}
|
||||
|
||||
if (Token::Match(tok.tokAt(-2), "[;{}] *")) {
|
||||
if (tok.strAt(1) == "=") {
|
||||
// is the pointer used in the rhs?
|
||||
bool used = false;
|
||||
for (const Token *tok2 = tok.tokAt(2); tok2; tok2 = tok2->next()) {
|
||||
if (Token::Match(tok2, "[,;=(]"))
|
||||
break;
|
||||
else if (Token::Match(tok2, "* %varid%", tok.varId())) {
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (used)
|
||||
use_pointer(checks, &tok);
|
||||
else
|
||||
init_pointer(checks, &tok);
|
||||
} else {
|
||||
use_pointer(checks, &tok);
|
||||
}
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (Token::Match(tok.next(), "= malloc|kmalloc") || (!isC && Token::simpleMatch(tok.next(), "= new char [")) ||
|
||||
(Token::Match(tok.next(), "= %name% (") && library->returnuninitdata.find(tok.strAt(2)) != library->returnuninitdata.end())) {
|
||||
alloc_pointer(checks, tok.varId());
|
||||
if (tok.strAt(3) == "(")
|
||||
return tok.tokAt(3);
|
||||
}
|
||||
|
||||
else if ((!isC && (Token::Match(tok.previous(), "<<|>>") || Token::Match(tok.previous(), "[;{}] %name% <<"))) ||
|
||||
tok.strAt(1) == "=") {
|
||||
// TODO: Don't bail out for "<<" and ">>" if these are
|
||||
// just computations
|
||||
ExecutionPath::bailOutVar(checks, tok.varId());
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (tok.strAt(1) == "[" && tok.next()->link()) {
|
||||
const Token *tok2 = tok.next()->link();
|
||||
if (tok2->strAt(1) == "=") {
|
||||
ExecutionPath::bailOutVar(checks, tok.varId());
|
||||
return &tok;
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.strAt(-1) == "delete" ||
|
||||
Token::simpleMatch(tok.tokAt(-3), "delete [ ]")) {
|
||||
dealloc_pointer(checks, &tok);
|
||||
return &tok;
|
||||
}
|
||||
}
|
||||
|
||||
if (Token::Match(&tok, "%name% (")) {
|
||||
// sizeof/typeof doesn't dereference. A function name that is all uppercase
|
||||
// might be an unexpanded macro that uses sizeof/typeof
|
||||
if (Token::Match(&tok, "sizeof|typeof ("))
|
||||
return tok.next()->link();
|
||||
|
||||
// deallocate pointer
|
||||
if (Token::Match(&tok, "free|kfree|fclose ( %var% )") ||
|
||||
Token::Match(&tok, "realloc ( %name%")) {
|
||||
dealloc_pointer(checks, tok.tokAt(2));
|
||||
if (tok.str() == "realloc")
|
||||
ExecutionPath::bailOutVar(checks, tok.tokAt(2)->varId());
|
||||
return tok.tokAt(3);
|
||||
}
|
||||
|
||||
// parse usage..
|
||||
{
|
||||
std::list<const Token *> var1;
|
||||
CheckNullPointer::parseFunctionCall(tok, var1, library, 1);
|
||||
for (std::list<const Token *>::const_iterator it = var1.begin(); it != var1.end(); ++it) {
|
||||
// does iterator point at first function parameter?
|
||||
const bool firstPar(*it == tok.tokAt(2));
|
||||
|
||||
// is function memset/memcpy/etc?
|
||||
if (tok.str().compare(0,3,"mem") == 0)
|
||||
use_array_mem(checks, *it);
|
||||
|
||||
// second parameter for strncpy/strncat/etc
|
||||
else if (!firstPar && tok.str().compare(0,4,"strn") == 0)
|
||||
use_array_mem(checks, *it);
|
||||
|
||||
else
|
||||
use_array(checks, *it);
|
||||
|
||||
use_dead_pointer(checks, *it);
|
||||
}
|
||||
|
||||
// Using uninitialized pointer is bad if using null pointer is bad
|
||||
std::list<const Token *> var2;
|
||||
CheckNullPointer::parseFunctionCall(tok, var2, library, 0);
|
||||
for (std::list<const Token *>::const_iterator it = var2.begin(); it != var2.end(); ++it) {
|
||||
if (std::find(var1.begin(), var1.end(), *it) == var1.end())
|
||||
use_dead_pointer(checks, *it);
|
||||
}
|
||||
}
|
||||
|
||||
// strncpy doesn't null-terminate first parameter
|
||||
if (Token::Match(&tok, "strncpy ( %name% ,")) {
|
||||
if (Token::Match(tok.tokAt(4), "%str% ,")) {
|
||||
if (Token::Match(tok.tokAt(6), "%num% )")) {
|
||||
const std::size_t len = Token::getStrLength(tok.tokAt(4));
|
||||
const MathLib::bigint sz = MathLib::toLongNumber(tok.strAt(6));
|
||||
if (sz >= 0 && len >= static_cast<unsigned long>(sz)) {
|
||||
init_strncpy(checks, tok.tokAt(2));
|
||||
return tok.next()->link();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
init_strncpy(checks, tok.tokAt(2));
|
||||
return tok.next()->link();
|
||||
}
|
||||
}
|
||||
|
||||
// memset (not zero terminated)..
|
||||
if (Token::Match(&tok, "memset ( %name% , !!0 , %num% )")) {
|
||||
init_memset_nonzero(checks, tok.tokAt(2));
|
||||
return tok.next()->link();
|
||||
}
|
||||
|
||||
if (Token::Match(&tok, "asm ( %str% )")) {
|
||||
ExecutionPath::bailOut(checks);
|
||||
return &tok;
|
||||
}
|
||||
|
||||
// is the variable passed as a parameter to some function?
|
||||
unsigned int parlevel = 0;
|
||||
std::set<unsigned int> bailouts;
|
||||
for (const Token *tok2 = tok.next(); tok2; tok2 = tok2->next()) {
|
||||
if (tok2->str() == "(")
|
||||
++parlevel;
|
||||
|
||||
else if (tok2->str() == ")") {
|
||||
if (parlevel <= 1)
|
||||
break;
|
||||
--parlevel;
|
||||
}
|
||||
|
||||
else if (Token::Match(tok2, "sizeof|typeof (")) {
|
||||
tok2 = tok2->next()->link();
|
||||
if (!tok2)
|
||||
break;
|
||||
}
|
||||
|
||||
// ticket #2367 : unexpanded macro that uses sizeof|typeof?
|
||||
else if (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) {
|
||||
tok2 = tok2->next()->link();
|
||||
if (!tok2)
|
||||
break;
|
||||
}
|
||||
|
||||
else if (tok2->varId()) {
|
||||
if (Token::Match(tok2->tokAt(-2), "[(,] *") || Token::Match(tok2->next(), ". %name%")) {
|
||||
// find function call..
|
||||
const Token *functionCall = tok2;
|
||||
while (nullptr != (functionCall = functionCall ? functionCall->previous() : 0)) {
|
||||
if (functionCall->str() == "(")
|
||||
break;
|
||||
if (functionCall->str() == ")")
|
||||
functionCall = functionCall->link();
|
||||
}
|
||||
|
||||
functionCall = functionCall ? functionCall->previous() : 0;
|
||||
if (functionCall) {
|
||||
if (functionCall->isName() && !functionCall->isUpperCaseName() && use_dead_pointer(checks, tok2))
|
||||
ExecutionPath::bailOutVar(checks, tok2->varId());
|
||||
}
|
||||
}
|
||||
|
||||
// it is possible that the variable is initialized here
|
||||
if (Token::Match(tok2->previous(), "[(,] %var% [,)]"))
|
||||
bailouts.insert(tok2->varId());
|
||||
|
||||
// array initialization..
|
||||
if (Token::Match(tok2->previous(), "[,(] %var% [+-]")) {
|
||||
// if var is array, bailout
|
||||
for (std::list<ExecutionPath *>::const_iterator it = checks.begin(); it != checks.end(); ++it) {
|
||||
if ((*it)->varId == tok2->varId()) {
|
||||
const UninitVar *c = dynamic_cast<const UninitVar *>(*it);
|
||||
if (c && (c->var->isArray() || (c->var->isPointer() && c->alloc)))
|
||||
bailouts.insert(tok2->varId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (std::set<unsigned int>::const_iterator it = bailouts.begin(); it != bailouts.end(); ++it)
|
||||
ExecutionPath::bailOutVar(checks, *it);
|
||||
}
|
||||
|
||||
// function call via function pointer
|
||||
if (Token::Match(&tok, "( * %name% ) (") ||
|
||||
(Token::Match(&tok, "( *| %name% .|::") && Token::Match(tok.link()->tokAt(-2), ".|:: %name% ) ("))) {
|
||||
// is the variable passed as a parameter to some function?
|
||||
const Token *tok2 = tok.link()->next();
|
||||
for (const Token* const end2 = tok2->link(); tok2 != end2; tok2 = tok2->next()) {
|
||||
if (tok2->varId()) {
|
||||
// it is possible that the variable is initialized here
|
||||
ExecutionPath::bailOutVar(checks, tok2->varId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.str() == "return") {
|
||||
// Todo: if (!array && ..
|
||||
if (Token::Match(tok.next(), "%name% ;")) {
|
||||
use(checks, tok.next());
|
||||
} else if (Token::Match(tok.next(), "%name% [")) {
|
||||
use_array_or_pointer_data(checks, tok.next());
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.varId()) {
|
||||
if (tok.strAt(-1) == "=") {
|
||||
if (Token::Match(tok.tokAt(-3), "& %var% =")) {
|
||||
bailOutVar(checks, tok.varId());
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (!Token::Match(tok.tokAt(-3), ". %name% =")) {
|
||||
if (!Token::Match(tok.tokAt(-3), "[;{}] %name% =")) {
|
||||
use(checks, &tok);
|
||||
return &tok;
|
||||
}
|
||||
|
||||
const unsigned int varid2 = tok.tokAt(-2)->varId();
|
||||
if (varid2) {
|
||||
{
|
||||
use(checks, &tok);
|
||||
return &tok;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.strAt(1) == ".") {
|
||||
bailOutVar(checks, tok.varId());
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (tok.strAt(1) == "[") {
|
||||
ExecutionPath::bailOutVar(checks, tok.varId());
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (Token::Match(tok.tokAt(-2), "[,(=] *")) {
|
||||
use_pointer(checks, &tok);
|
||||
return &tok;
|
||||
}
|
||||
|
||||
if (tok.strAt(-1) == "&") {
|
||||
ExecutionPath::bailOutVar(checks, tok.varId());
|
||||
}
|
||||
}
|
||||
|
||||
// Parse "for"
|
||||
if (Token::Match(&tok, "[;{}] for (")) {
|
||||
// initialized variables
|
||||
std::set<unsigned int> varid1;
|
||||
varid1.insert(0);
|
||||
|
||||
// Parse token
|
||||
const Token *tok2;
|
||||
|
||||
// parse setup
|
||||
for (tok2 = tok.tokAt(3); tok2 != tok.link(); tok2 = tok2->next()) {
|
||||
if (tok2->str() == ";")
|
||||
break;
|
||||
if (tok2->varId())
|
||||
varid1.insert(tok2->varId());
|
||||
}
|
||||
if (tok2 == tok.link())
|
||||
return &tok;
|
||||
|
||||
// parse condition
|
||||
if (Token::Match(tok2, "; %var% <|<=|>=|> %num% ;")) {
|
||||
// If the variable hasn't been initialized then call "use"
|
||||
if (varid1.find(tok2->next()->varId()) == varid1.end())
|
||||
use(checks, tok2->next());
|
||||
}
|
||||
|
||||
// goto stepcode
|
||||
tok2 = tok2->next();
|
||||
while (tok2 && tok2->str() != ";")
|
||||
tok2 = tok2->next();
|
||||
|
||||
// parse the stepcode
|
||||
if (Token::Match(tok2, "; ++|-- %var% ) {") ||
|
||||
Token::Match(tok2, "; %var% ++|-- ) {")) {
|
||||
// get id of variable..
|
||||
unsigned int varid = tok2->next()->varId();
|
||||
if (!varid)
|
||||
varid = tok2->tokAt(2)->varId();
|
||||
|
||||
// Check that the variable hasn't been initialized and
|
||||
// that it isn't initialized in the body..
|
||||
if (varid1.find(varid) == varid1.end()) {
|
||||
for (const Token *tok3 = tok2->tokAt(5); tok3 && tok3 != tok2->linkAt(4); tok3 = tok3->next()) {
|
||||
if (tok3->varId() == varid) {
|
||||
varid = 0; // variable is used.. maybe it's initialized. clear the variable id.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If the variable isn't initialized in the body call "use"
|
||||
if (varid != 0) {
|
||||
// goto variable
|
||||
tok2 = tok2->next();
|
||||
if (!tok2->varId())
|
||||
tok2 = tok2->next();
|
||||
|
||||
// call "use"
|
||||
use(checks, tok2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &tok;
|
||||
}
|
||||
|
||||
bool parseCondition(const Token &tok, std::list<ExecutionPath *> &checks) {
|
||||
if (tok.varId() && Token::Match(&tok, "%name% <|<=|==|!=|)"))
|
||||
use(checks, &tok);
|
||||
|
||||
else if (Token::Match(&tok, "!| %name% [") && !Token::simpleMatch(skipBrackets(tok.next()), "="))
|
||||
use_array_or_pointer_data(checks, tok.str() == "!" ? tok.next() : &tok);
|
||||
|
||||
else if (Token::Match(&tok, "!| %name% (")) {
|
||||
const Token * const ftok = (tok.str() == "!") ? tok.next() : &tok;
|
||||
std::list<const Token *> var1;
|
||||
CheckNullPointer::parseFunctionCall(*ftok, var1, library, 1);
|
||||
for (std::list<const Token *>::const_iterator it = var1.begin(); it != var1.end(); ++it) {
|
||||
// is function memset/memcpy/etc?
|
||||
if (ftok->str().compare(0,3,"mem") == 0)
|
||||
use_array_mem(checks, *it);
|
||||
else
|
||||
use_array(checks, *it);
|
||||
}
|
||||
}
|
||||
|
||||
else if (Token::Match(&tok, "! %name% )")) {
|
||||
use(checks, &tok);
|
||||
return false;
|
||||
}
|
||||
|
||||
return ExecutionPath::parseCondition(tok, checks);
|
||||
}
|
||||
|
||||
void parseLoopBody(const Token *tok, std::list<ExecutionPath *> &checks) const {
|
||||
while (tok) {
|
||||
if (tok->str() == "{" || tok->str() == "}" || tok->str() == "for")
|
||||
return;
|
||||
if (Token::simpleMatch(tok, "if (")) {
|
||||
// bail out all variables that are used in the condition
|
||||
const Token* const end2 = tok->linkAt(1);
|
||||
for (const Token *tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) {
|
||||
if (tok2->varId())
|
||||
ExecutionPath::bailOutVar(checks, tok2->varId());
|
||||
}
|
||||
}
|
||||
const Token *next = parse(*tok, checks);
|
||||
tok = next->next();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static void analyseFunctions(const Token * const tokens, std::set<std::string> &func) {
|
||||
namespace UninitVar {
|
||||
void analyseFunctions(const Token * const tokens, std::set<std::string> &func)
|
||||
{
|
||||
for (const Token *tok = tokens; tok; tok = tok->next()) {
|
||||
if (tok->str() == "{") {
|
||||
tok = tok->link();
|
||||
|
@ -1008,9 +125,8 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// @}
|
||||
|
||||
|
||||
Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const
|
||||
|
@ -1033,13 +149,6 @@ void CheckUninitVar::analyseFunctions(const Tokenizer *tokenizer, std::set<std::
|
|||
UninitVar::analyseFunctions(tokenizer->tokens(), f);
|
||||
}
|
||||
|
||||
void CheckUninitVar::executionPaths()
|
||||
{
|
||||
// check if variable is accessed uninitialized..
|
||||
UninitVar c(this, _tokenizer->getSymbolDatabase(), &_settings->library, _tokenizer->isC());
|
||||
checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c);
|
||||
}
|
||||
|
||||
|
||||
void CheckUninitVar::check()
|
||||
{
|
||||
|
|
|
@ -48,7 +48,6 @@ public:
|
|||
/** @brief Run checks against the simplified token list */
|
||||
void runSimplifiedChecks(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) {
|
||||
CheckUninitVar checkUninitVar(tokenizer, settings, errorLogger);
|
||||
checkUninitVar.executionPaths();
|
||||
checkUninitVar.check();
|
||||
checkUninitVar.deadPointer();
|
||||
}
|
||||
|
@ -89,9 +88,6 @@ public:
|
|||
|
||||
void analyseFunctions(const Tokenizer *tokenizer, std::set<std::string> &f) const;
|
||||
|
||||
/** @brief new type of check: check execution paths */
|
||||
void executionPaths();
|
||||
|
||||
void uninitstringError(const Token *tok, const std::string &varname, bool strncpy_);
|
||||
void uninitdataError(const Token *tok, const std::string &varname);
|
||||
void uninitvarError(const Token *tok, const std::string &varname);
|
||||
|
|
|
@ -117,7 +117,6 @@ private:
|
|||
// Check code..
|
||||
CheckUninitVar check(&tokenizer, &settings, this);
|
||||
check.check();
|
||||
check.executionPaths();
|
||||
}
|
||||
|
||||
void uninitvar1() {
|
||||
|
@ -1516,7 +1515,7 @@ private:
|
|||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkUninitVarB("struct StgStrm {\n"
|
||||
checkUninitVar2("struct StgStrm {\n"
|
||||
" StgIo& rIo;\n"
|
||||
" StgStrm(StgIo&);\n"
|
||||
" virtual sal_Int32 Write();\n"
|
||||
|
@ -1527,7 +1526,7 @@ private:
|
|||
" pNewStrm = new StgStrm(rIo);\n"
|
||||
" pNewStrm->Write();\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("[test.cpp:10]: (error) Uninitialized variable: pNewStrm\n", errout.str());
|
||||
TODO_ASSERT_EQUALS("[test.cpp:10]: (error) Uninitialized variable: pNewStrm\n", "", errout.str());
|
||||
|
||||
// #6450 - calling a member function is allowed if memory was allocated by new
|
||||
checkUninitVarB("struct EMFPFont {\n"
|
||||
|
|
Loading…
Reference in New Issue