void CheckNullPointer::nullPointerByDeRefAndChec() { const bool printInconclusive = (mSettings->inconclusive); for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) { tok = tok->next()->link(); continue; } const Variable *var = tok->variable(); if (!var || !var->isPointer() || tok == var->nameToken()) continue; // Can pointer be NULL? const ValueFlow::Value *value = tok->getValue(0); if (!value) continue; if (!printInconclusive && value->isInconclusive()) continue; // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (unknown) nullPointerError(tok, tok->str(), value, true); continue; } nullPointerError(tok, tok->str(), value, value->isInconclusive()); } }
void CheckNullPointer::nullPointerAfterLoop() { // Locate insufficient null-pointer handling after loop for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // only interested in while ( %var% ) // TODO: Aren't there false negatives. Shouldn't other loops be handled such as: // - while ( ! %var% ) // - while ( %var% && .. ) if (! Token::Match(tok, "while ( %var% )")) continue; // Get variable id for the loop variable const unsigned int varid(tok->tokAt(2)->varId()); if (varid == 0) continue; // Is variable a pointer? if (!isPointer(varid)) continue; // Get variable name for the loop variable const std::string varname(tok->strAt(2)); // Locate the end of the while loop body.. const Token *tok2 = tok->tokAt(4)->link(); // Check if the variable is dereferenced after the while loop while (0 != (tok2 = tok2 ? tok2->next() : 0)) { // Don't check into inner scopes or outer scopes. Stop checking if "break" is found if (tok2->str() == "{" || tok2->str() == "}" || tok2->str() == "break") break; // loop variable is found.. if (tok2->varId() == varid) { // dummy variable.. is it unknown if pointer is dereferenced or not? bool unknown = false; // Is the loop variable dereferenced? if (CheckNullPointer::isPointerDeRef(tok2, unknown)) { nullPointerError(tok2, varname, tok->linenr()); } break; } } } }
void CheckNullPointer::nullPointerByDeRefAndChec() { for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { const Variable *var = tok->variable(); if (!var || !var->isPointer() || tok == var->nameToken()) continue; // Can pointer be NULL? const ValueFlow::Value *value = tok->getValue(0); if (!value) continue; if (!_settings->inconclusive && value->inconclusive) continue; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %var% [,)]")) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (!ftok || !ftok->previous()) continue; std::list<const Token *> varlist; parseFunctionCall(*ftok->previous(), varlist, &_settings->library, 0); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { if (value->condition == nullptr) nullPointerError(tok, tok->str()); else if (_settings->isEnabled("warning")) nullPointerError(tok, tok->str(), value->condition, value->inconclusive); } continue; } // Pointer dereference. bool unknown = false; if (!isPointerDeRef(tok,unknown)) { if (_settings->inconclusive && unknown) { if (value->condition == nullptr) nullPointerError(tok, tok->str(), true); else nullPointerError(tok, tok->str(), value->condition, true); } continue; } if (value->condition == nullptr) nullPointerError(tok, tok->str(), value->inconclusive); else if (_settings->isEnabled("warning")) nullPointerError(tok, tok->str(), value->condition, value->inconclusive); } }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { // this is kept at 0 for all scopes that are not executing unsigned int indentlevel = 0; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // start of executable scope.. if (indentlevel == 0 && Token::Match(tok, ") const| {")) indentlevel = 1; else if (indentlevel >= 1) { if (tok->str() == "{") ++indentlevel; else if (tok->str() == "}") { if (indentlevel <= 2) indentlevel = 0; else --indentlevel; } if (tok->str() == "(" && Token::Match(tok->previous(), "sizeof|decltype")) tok = tok->link(); else if (Token::simpleMatch(tok, "exit ( )")) { // Goto end of scope while (tok && tok->str() != "}") { if (tok->str() == "{") tok = tok->link(); tok = tok->next(); } if (!tok) break; } else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|;|{|}|=|(|,|%op%")) { nullPointerError(tok); } } else if (indentlevel > 0 && Token::Match(tok, "%var% (")) { std::list<const Token *> var; parseFunctionCall(*tok, var, 0); // is one of the var items a NULL pointer? for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) { if ((*it)->str() == "0") { nullPointerError(*it); } } } } } }
void CheckNullPointer::nullPointerByCheckAndDeRef() { // Check if pointer is NULL and then dereference it.. // used to check if a variable is a pointer. // TODO: Use isPointer? std::set<unsigned int> pointerVariables; for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "* %var% [;,)=]")) pointerVariables.insert(tok->next()->varId()); else if (Token::simpleMatch(tok, "if (")) { // TODO: investigate false negatives: // - handle "while"? // - if there are logical operators // - if (x) { } else { ... } // If the if-body ends with a unknown macro then bailout { // goto the end parenthesis const Token *endpar = tok->next()->link(); const Token *endbody = Token::simpleMatch(endpar, ") {") ? endpar->next()->link() : 0; if (endbody && Token::Match(endbody->tokAt(-3), "[;{}] %var% ;") && isUpper(endbody->tokAt(-2)->str())) continue; } // vartok : token for the variable const Token *vartok = 0; if (Token::Match(tok, "if ( ! %var% )|&&")) vartok = tok->tokAt(3); else if (Token::Match(tok, "if|while ( %var% )|&&")) vartok = tok->tokAt(2); else continue; // variable id for pointer const unsigned int varid(vartok->varId()); if (varid == 0) continue; const unsigned int linenr = vartok->linenr(); // Check if variable is a pointer. TODO: Use isPointer? if (pointerVariables.find(varid) == pointerVariables.end()) continue; // if this is true then it is known that the pointer is null bool null = true; // start token = inside the if-body const Token *tok1 = tok->next()->link()->tokAt(2); // indentlevel inside the if-body is 1 unsigned int indentlevel = 1; if (Token::Match(tok, "if|while ( %var% )|&&")) { // pointer might be null null = false; // start token = first token after the if/while body tok1 = tok1->previous()->link(); tok1 = tok1 ? tok1->next() : NULL; if (!tok1) continue; // indentlevel at the base level is 0 indentlevel = 0; } // Name of the pointer const std::string &pointerName = vartok->str(); // Count { and } for tok2 for (const Token *tok2 = tok1; tok2; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { if (indentlevel == 0) break; --indentlevel; if (null && indentlevel == 0) { // skip all "else" blocks because they are not executed in this execution path while (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->tokAt(2)->link(); null = false; } } if (Token::Match(tok2, "goto|return|continue|break|throw|if|switch")) { if (Token::Match(tok2, "return * %varid%", varid)) nullPointerError(tok2, tok->strAt(3), linenr); break; } // parameters to sizeof are not dereferenced if (Token::Match(tok2, "decltype|sizeof (")) { tok2 = tok2->next()->link(); continue; } // calling unknown function (abort/init).. if (Token::simpleMatch(tok2, ") ;") && (Token::Match(tok2->link()->tokAt(-2), "[;{}] %var% (") || Token::Match(tok2->link()->tokAt(-5), "[;{}] ( * %var% ) ("))) { break; } if (tok2->varId() == varid) { // unknown: this is set to true by isPointerDeRef if // the function fails to determine if there // is a dereference or not bool unknown = false; if (Token::Match(tok2->previous(), "[;{}=] %var% = 0 ;")) ; else if (CheckNullPointer::isPointerDeRef(tok2, unknown)) nullPointerError(tok2, pointerName, linenr); else break; } } } } }
void CheckNullPointer::nullPointerByDeRefAndChec() { // Dereferencing a pointer and then checking if it's NULL.. // This check will first scan for the check. And then scan backwards // from the check, searching for dereferencing. for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next()) { // TODO: false negatives. // - logical operators // - while if (tok->str() == "if" && Token::Match(tok->previous(), "; if ( ! %var% )")) { // Variable id for pointer const unsigned int varid(tok->tokAt(3)->varId()); if (varid == 0) continue; // Name of pointer const std::string varname(tok->strAt(3)); // Check that variable is a pointer.. if (!isPointer(varid)) continue; // Token where pointer is declared const Token * const decltok = Token::findmatch(_tokenizer->tokens(), "%varid%", varid); for (const Token *tok1 = tok->previous(); tok1 && tok1 != decltok; tok1 = tok1->previous()) { if (tok1->str() == ")" && Token::Match(tok1->link()->previous(), "%var% (")) { const Token *tok2 = tok1->link(); while (tok2 && !Token::Match(tok2, "[;{}]")) tok2 = tok2->previous(); if (Token::Match(tok2, "[;{}] %varid% = %var%", varid)) break; } if (tok1->str() == ")" && Token::Match(tok1->link()->previous(), "while ( %varid%", varid)) { break; } if (tok1->str() == ")" && Token::simpleMatch(tok1->link()->previous(), "sizeof (")) { tok1 = tok1->link()->previous(); continue; } if (tok1->str() == "break") break; if (tok1->varId() == varid && !Token::Match(tok1->previous(), "[?:]")) { // unknown : this is set by isPointerDeRef if it is // uncertain bool unknown = false; if (Token::Match(tok1->tokAt(-2), "%varid% = %varid% .", varid)) { break; } else if (CheckNullPointer::isPointerDeRef(tok1, unknown)) { nullPointerError(tok1, varname, tok->linenr()); break; } else if (Token::simpleMatch(tok1->previous(), "&")) { break; } else if (Token::simpleMatch(tok1->next(), "=")) { break; } } else if (tok1->str() == "{" || tok1->str() == "}") break; // label.. else if (Token::Match(tok1, "%type% :")) break; } } } }
void CheckNullPointer::nullPointerStructByDeRefAndChec() { // Dereferencing a struct pointer and then checking if it's NULL.. // skipvar: don't check vars that has been tested against null already std::set<unsigned int> skipvar; skipvar.insert(0); // Scan through all tokens for (const Token *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next()) { // Checking if some pointer is null. // then add the pointer to skipvar => is it known that it isn't NULL if (Token::Match(tok1, "if|while ( !| %var% )")) { tok1 = tok1->tokAt(2); if (tok1->str() == "!") tok1 = tok1->next(); skipvar.insert(tok1->varId()); continue; } /** * @todo There are lots of false negatives here. A dereference * is only investigated if a few specific conditions are met. */ // dereference in assignment if (Token::Match(tok1, "[;{}] %var% . %var%")) { tok1 = tok1->next(); } // dereference in assignment else if (Token::Match(tok1, "[{};] %var% = %var% . %var%")) { if (std::string(tok1->strAt(1)) == tok1->strAt(3)) continue; tok1 = tok1->tokAt(3); } // dereference in function call (but not sizeof|decltype) else if ((Token::Match(tok1->tokAt(-2), "%var% ( %var% . %var%") && !Token::Match(tok1->tokAt(-2), "sizeof|decltype ( %var% . %var%")) || Token::Match(tok1->previous(), ", %var% . %var%")) { // Is the function return value taken by the pointer? bool assignment = false; const unsigned int varid1(tok1->varId()); if (varid1 == 0) continue; const Token *tok2 = tok1->previous(); while (tok2 && !Token::Match(tok2, "[;{}]")) { if (Token::Match(tok2, "%varid% =", varid1)) { assignment = true; break; } tok2 = tok2->previous(); } if (assignment) continue; } // Goto next token else { continue; } // struct dereference was found - investigate if it is later // checked that it is not NULL const unsigned int varid1(tok1->varId()); if (skipvar.find(varid1) != skipvar.end()) continue; // name of struct pointer const std::string varname(tok1->str()); // is pointer local? bool isLocal = false; const Variable * var = _tokenizer->getSymbolDatabase()->getVariableFromVarId(tok1->varId()); if (var && (var->isLocal() || var->isArgument())) isLocal = true; // member function may or may not nullify the pointer if it's global (#2647) if (!isLocal) { const Token *tok2 = tok1; while (Token::Match(tok2, "%var% .")) tok2 = tok2->tokAt(2); if (Token::Match(tok2,"%var% (")) continue; } // count { and } using tok2 unsigned int indentlevel2 = 0; for (const Token *tok2 = tok1->tokAt(3); tok2; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel2; else if (tok2->str() == "}") { if (indentlevel2 <= 1) break; --indentlevel2; } // label / ?: else if (tok2->str() == ":") break; // Reassignment of the struct else if (tok2->varId() == varid1) { if (tok2->next()->str() == "=") break; if (Token::Match(tok2->tokAt(-2), "[,(] &")) break; } // Loop.. /** @todo don't bail out if the variable is not used in the loop */ else if (tok2->str() == "do") break; // return/break at base level => stop checking else if (indentlevel2 == 0 && (tok2->str() == "return" || tok2->str() == "break")) break; // Function call: If the pointer is not a local variable it // might be changed by the call. else if (Token::Match(tok2, "[;{}] %var% (") && Token::simpleMatch(tok2->tokAt(2)->link(), ") ;") && !isLocal) { break; } // Check if pointer is null. // TODO: false negatives for "if (!p || .." else if (Token::Match(tok2, "if ( !| %varid% )|&&", varid1)) { // Is this variable a pointer? if (isPointer(varid1)) nullPointerError(tok1, varname, tok2->linenr()); break; } } } }
void CheckNullPointer::nullPointerLinkedList() { // looping through items in a linked list in a inner loop. // Here is an example: // for (const Token *tok = tokens; tok; tok = tok->next) { // if (tok->str() == "hello") // tok = tok->next; // <- tok might become a null pointer! // } for (const Token *tok1 = _tokenizer->tokens(); tok1; tok1 = tok1->next()) { // search for a "for" token.. if (!Token::simpleMatch(tok1, "for (")) continue; // is there any dereferencing occurring in the for statement // parlevel2 counts the parentheses when using tok2. unsigned int parlevel2 = 1; for (const Token *tok2 = tok1->tokAt(2); tok2; tok2 = tok2->next()) { // Parentheses.. if (tok2->str() == "(") ++parlevel2; else if (tok2->str() == ")") { if (parlevel2 <= 1) break; --parlevel2; } // Dereferencing a variable inside the "for" parentheses.. else if (Token::Match(tok2, "%var% . %var%")) { // Variable id for dereferenced variable const unsigned int varid(tok2->varId()); if (varid == 0) continue; if (Token::Match(tok2->tokAt(-2), "%varid% ?", varid)) continue; // Variable name of dereferenced variable const std::string varname(tok2->str()); // Check usage of dereferenced variable in the loop.. unsigned int indentlevel3 = 0; for (const Token *tok3 = tok1->next()->link(); tok3; tok3 = tok3->next()) { if (tok3->str() == "{") ++indentlevel3; else if (tok3->str() == "}") { if (indentlevel3 <= 1) break; --indentlevel3; } // TODO: are there false negatives for "while ( %varid% ||" else if (Token::Match(tok3, "while ( %varid% &&|)", varid)) { // Make sure there is a "break" or "return" inside the loop. // Without the "break" a null pointer could be dereferenced in the // for statement. // indentlevel4 is a counter for { and }. When scanning the code with tok4 unsigned int indentlevel4 = indentlevel3; for (const Token *tok4 = tok3->next()->link(); tok4; tok4 = tok4->next()) { if (tok4->str() == "{") ++indentlevel4; else if (tok4->str() == "}") { if (indentlevel4 <= 1) { // Is this variable a pointer? if (isPointer(varid)) nullPointerError(tok1, varname, tok3->linenr()); break; } --indentlevel4; } // There is a "break" or "return" inside the loop. // TODO: there can be false negatives. There could still be // execution paths that are not properly terminated else if (tok4->str() == "break" || tok4->str() == "return") break; } } } } } } }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // THIS ARRAY MUST BE ORDERED ALPHABETICALLY static const char* const stl_stream[] = { "fstream", "ifstream", "iostream", "istream", "istringstream", "stringstream", "wistringstream", "wstringstream" }; const std::size_t functions = symbolDatabase->functionScopes.size(); for (std::size_t i = 0; i < functions; ++i) { const Scope * scope = symbolDatabase->functionScopes[i]; if (scope->function == 0 || !scope->function->hasBody) // We only look for functions with a body continue; const Token *tok = scope->classStart; if (scope->function && scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->classEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { nullPointerError(tok); } } else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (Token::Match(tok->previous(), "!!. %var% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::simpleMatch(tok->tokAt(2), "0 )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string|wstring !!::")) nullPointerError(tok); } else { // function call std::list<const Token *> var; parseFunctionCall(*tok, var, &_settings->library, 0); // is one of the var items a NULL pointer? for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) { if (Token::Match(*it, "0|NULL [,)]")) { nullPointerError(*it); } } } } else if (Token::Match(tok, "std :: string|wstring ( 0 )")) nullPointerError(tok); else if (Token::simpleMatch(tok->previous(), ">> 0")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable *var = tok2->variable(); if (var && var->isStlType(stl_stream)) nullPointerError(tok); } } const Variable *ovar = nullptr; if (Token::Match(tok, "0 ==|!= %var% !!.")) ovar = tok->tokAt(2)->variable(); else if (Token::Match(tok, "%var% ==|!= 0")) ovar = tok->variable(); else if (Token::Match(tok, "%var% =|+ 0 )|]|,|;|+")) ovar = tok->variable(); if (ovar && !ovar->isPointer() && !ovar->isArray() && Token::Match(ovar->typeStartToken(), "std :: string|wstring !!::")) nullPointerError(tok); } } }
void CheckNullPointer::nullPointerByCheckAndDeRef() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Check if pointer is NULL and then dereference it.. for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eIf && i->type != Scope::eElseIf && i->type != Scope::eWhile) continue; if (!i->classDef || i->classDef->isExpandedMacro()) continue; const Token* const tok = i->type != Scope::eElseIf ? i->classDef->next() : i->classDef->tokAt(2); // TODO: investigate false negatives: // - handle "while"? // - if there are logical operators // - if (x) { } else { ... } // If the if-body ends with a unknown macro then bailout if (Token::Match(i->classEnd->tokAt(-3), "[;{}] %var% ;") && i->classEnd->tokAt(-2)->isUpperCaseName()) continue; // vartok : token for the variable const Token *vartok = nullptr; const Token *checkConditionStart = nullptr; if (Token::Match(tok, "( ! %var% )|&&")) { vartok = tok->tokAt(2); checkConditionStart = vartok->next(); } else if (Token::Match(tok, "( %var% )|&&")) { vartok = tok->next(); } else if (Token::Match(tok, "( ! ( %var% =")) { vartok = tok->tokAt(3); if (Token::simpleMatch(tok->linkAt(2), ") &&")) checkConditionStart = tok->linkAt(2); } else continue; // Check if variable is a pointer const Variable *var = vartok->variable(); if (!var || !var->isPointer()) continue; // variable id for pointer const unsigned int varid(vartok->varId()); const Scope* declScope = &*i; while (declScope->nestedIn && var->scope() != declScope && declScope->type != Scope::eFunction) declScope = declScope->nestedIn; if (Token::Match(vartok->next(), "&& ( %varid% =", varid)) continue; // Name and line of the pointer const std::string &pointerName = vartok->str(); // Check the condition (eg. ( !x && x->i ) if (checkConditionStart) { const Token * const conditionEnd = tok->link(); for (const Token *tok2 = checkConditionStart; tok2 != conditionEnd; tok2 = tok2->next()) { // If we hit a || operator, abort if (tok2->str() == "||") break; // Pointer is used bool unknown = _settings->inconclusive; if (tok2->varId() == varid && (isPointerDeRef(tok2, unknown) || unknown)) { nullPointerError(tok2, pointerName, vartok, unknown); break; } } } // start token = inside the if-body const Token *tok1 = i->classStart; if (Token::Match(tok, "( %var% )|&&")) { // start token = first token after the if/while body tok1 = i->classEnd->next(); if (!tok1) continue; } int indentlevel = 0; // Set to true if we would normally bail out the check. bool inconclusive = false; // Count { and } for tok2 for (const Token *tok2 = tok1; tok2 != declScope->classEnd; tok2 = tok2->next()) { if (tok2->str() == "{") ++indentlevel; else if (tok2->str() == "}") { if (indentlevel == 0) { if (_settings->inconclusive) inconclusive = true; else break; } --indentlevel; // calling exit function? bool unknown = false; if (_tokenizer->IsScopeNoReturn(tok2, &unknown)) { if (_settings->inconclusive && unknown) inconclusive = true; else break; } if (indentlevel <= 0) { // skip all "else" blocks because they are not executed in this execution path while (Token::simpleMatch(tok2, "} else if (")) tok2 = tok2->linkAt(3)->linkAt(1); if (Token::simpleMatch(tok2, "} else {")) tok2 = tok2->linkAt(2); } } if (tok2->str() == "return" || tok2->str() == "throw") { bool unknown = _settings->inconclusive; for (; tok2 && tok2->str() != ";"; tok2 = tok2->next()) { if (tok2->varId() == varid) { if (CheckNullPointer::isPointerDeRef(tok2, unknown)) nullPointerError(tok2, pointerName, vartok, inconclusive); else if (unknown) nullPointerError(tok2, pointerName, vartok, true); if (Token::Match(tok2, "%var% %oror%|&&|?")) break; } } break; } // Bailout for "if". if (tok2->str() == "if") { if (_settings->inconclusive) inconclusive = true; else break; } if (Token::Match(tok2, "goto|continue|break|switch|for")) break; // parameters to sizeof are not dereferenced if (Token::Match(tok2, "decltype|sizeof|typeof")) { if (tok2->strAt(1) != "(") tok2 = tok2->next(); else tok2 = tok2->next()->link(); continue; } // function call, check if pointer is dereferenced if (Token::Match(tok2, "%var% (") && !Token::Match(tok2, "if|while")) { std::list<const Token *> vars; parseFunctionCall(*tok2, vars, &_settings->library, 0); for (std::list<const Token *>::const_iterator it = vars.begin(); it != vars.end(); ++it) { if (Token::Match(*it, "%varid% [,)]", varid)) { nullPointerError(*it, pointerName, vartok, inconclusive); break; } } } // calling unknown function (abort/init).. else if (Token::simpleMatch(tok2, ") ;") && (Token::Match(tok2->link()->tokAt(-2), "[;{}.] %var% (") || Token::Match(tok2->link()->tokAt(-5), "[;{}] ( * %var% ) ("))) { // noreturn function? bool unknown = false; if (_tokenizer->IsScopeNoReturn(tok2->tokAt(2), &unknown)) { if (!unknown || !_settings->inconclusive) { break; } inconclusive = _settings->inconclusive; } // init function (global variables) if (!(var->isLocal() || var->isArgument())) break; } if (tok2->varId() == varid) { // unknown: this is set to true by isPointerDeRef if // the function fails to determine if there // is a dereference or not bool unknown = _settings->inconclusive; if (Token::Match(tok2->previous(), "[;{}=] %var% = 0 ;")) ; else if (CheckNullPointer::isPointerDeRef(tok2, unknown)) nullPointerError(tok2, pointerName, vartok, inconclusive); else if (unknown && _settings->inconclusive) nullPointerError(tok2, pointerName, vartok, true); else break; } } } }
void CheckNullPointer::nullPointerLinkedList() { const SymbolDatabase* const symbolDatabase = _tokenizer->getSymbolDatabase(); // looping through items in a linked list in a inner loop. // Here is an example: // for (const Token *tok = tokens; tok; tok = tok->next) { // if (tok->str() == "hello") // tok = tok->next; // <- tok might become a null pointer! // } for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { const Token* const tok1 = i->classDef; // search for a "for" scope.. if (i->type != Scope::eFor || !tok1) continue; // is there any dereferencing occurring in the for statement const Token* end2 = tok1->linkAt(1); for (const Token *tok2 = tok1->tokAt(2); tok2 != end2; tok2 = tok2->next()) { // Dereferencing a variable inside the "for" parentheses.. if (Token::Match(tok2, "%var% . %var%")) { // Is this variable a pointer? const Variable *var = tok2->variable(); if (!var || !var->isPointer()) continue; // Variable id for dereferenced variable const unsigned int varid(tok2->varId()); // We don't support variables without a varid if (varid == 0) continue; if (Token::Match(tok2->tokAt(-2), "%varid% ?", varid)) continue; // Check usage of dereferenced variable in the loop.. for (std::list<Scope*>::const_iterator j = i->nestedList.begin(); j != i->nestedList.end(); ++j) { Scope* scope = *j; if (scope->type != Scope::eWhile) continue; // TODO: are there false negatives for "while ( %varid% ||" if (Token::Match(scope->classDef->next(), "( %varid% &&|)", varid)) { // Make sure there is a "break" or "return" inside the loop. // Without the "break" a null pointer could be dereferenced in the // for statement. for (const Token *tok4 = scope->classStart; tok4; tok4 = tok4->next()) { if (tok4 == i->classEnd) { nullPointerError(tok1, var->name(), scope->classDef); break; } // There is a "break" or "return" inside the loop. // TODO: there can be false negatives. There could still be // execution paths that are not properly terminated else if (tok4->str() == "break" || tok4->str() == "return") break; } } } } } } }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body continue; const Token *tok = scope->bodyStart; if (scope->function->isConstructor()) tok = scope->function->token; // Check initialization list for (; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) tok = tok->next()->link(); else if (Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { nullPointerError(tok); } } else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (Token::Match(tok->previous(), "!!. %name% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::Match(tok->tokAt(2), "0|NULL|nullptr )") && tok->varId()) { // constructor call const Variable *var = tok->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) nullPointerError(tok); } else { // function call std::list<const Token *> var; parseFunctionCall(*tok, var, &mSettings->library); // is one of the var items a NULL pointer? for (const Token *vartok : var) { if (Token::Match(vartok, "0|NULL|nullptr [,)]")) { nullPointerError(vartok); } } } } else if (Token::Match(tok, "std :: string|wstring ( 0|NULL|nullptr )")) nullPointerError(tok); else if (Token::Match(tok->previous(), "::|. %name% (")) { const std::vector<const Token *> &args = getArguments(tok); for (int argnr = 0; argnr < args.size(); ++argnr) { const Token *argtok = args[argnr]; if (!argtok->hasKnownIntValue()) continue; if (argtok->values().front().intvalue != 0) continue; if (mSettings->library.isnullargbad(tok, argnr+1)) nullPointerError(argtok); } } else if (Token::Match(tok->previous(), ">> 0|NULL|nullptr")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:|(")) break; } if (tok2 && tok2->previous() && tok2->previous()->str()=="(") continue; if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable *var = tok2->variable(); if (var && var->isStlType(stl_istream)) nullPointerError(tok); } } const Variable *ovar = nullptr; const Token *tokNull = nullptr; if (Token::Match(tok, "0|NULL|nullptr ==|!=|>|>=|<|<= %var%")) { if (!Token::Match(tok->tokAt(3),".|[")) { ovar = tok->tokAt(2)->variable(); tokNull = tok; } } else if (Token::Match(tok, "%var% ==|!=|>|>=|<|<= 0|NULL|nullptr") || Token::Match(tok, "%var% =|+ 0|NULL|nullptr )|]|,|;|+")) { ovar = tok->variable(); tokNull = tok->tokAt(2); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType() && tokNull && tokNull->originalName() != "'\\0'") nullPointerError(tokNull); } } }
void CheckNullPointer::nullPointerLinkedList() { if (!mSettings->isEnabled(Settings::WARNING)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); // looping through items in a linked list in a inner loop. // Here is an example: // for (const Token *tok = tokens; tok; tok = tok->next) { // if (tok->str() == "hello") // tok = tok->next; // <- tok might become a null pointer! // } for (const Scope &forScope : symbolDatabase->scopeList) { const Token* const tok1 = forScope.classDef; // search for a "for" scope.. if (forScope.type != Scope::eFor || !tok1) continue; // is there any dereferencing occurring in the for statement const Token* end2 = tok1->linkAt(1); for (const Token *tok2 = tok1->tokAt(2); tok2 != end2; tok2 = tok2->next()) { // Dereferencing a variable inside the "for" parentheses.. if (Token::Match(tok2, "%var% . %name%")) { // Is this variable a pointer? const Variable *var = tok2->variable(); if (!var || !var->isPointer()) continue; // Variable id for dereferenced variable const unsigned int varid(tok2->varId()); if (Token::Match(tok2->tokAt(-2), "%varid% ?", varid)) continue; // Check usage of dereferenced variable in the loop.. // TODO: Move this to ValueFlow for (const Scope *innerScope : forScope.nestedList) { if (innerScope->type != Scope::eWhile) continue; // TODO: are there false negatives for "while ( %varid% ||" if (Token::Match(innerScope->classDef->next(), "( %varid% &&|)", varid)) { // Make sure there is a "break" or "return" inside the loop. // Without the "break" a null pointer could be dereferenced in the // for statement. for (const Token *tok4 = innerScope->bodyStart; tok4; tok4 = tok4->next()) { if (tok4 == forScope.bodyEnd) { const ValueFlow::Value v(innerScope->classDef, 0LL); nullPointerError(tok1, var->name(), &v, false); break; } // There is a "break" or "return" inside the loop. // TODO: there can be false negatives. There could still be // execution paths that are not properly terminated else if (tok4->str() == "break" || tok4->str() == "return") break; } } } } } } }
/** Dereferencing null constant (simplified token list) */ void CheckNullPointer::nullConstantDereference() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { if (i->type != Scope::eFunction || !i->classStart) continue; const Token *tok = i->classStart; if (i->function && (i->function->type == Function::eConstructor || i->function->type == Function::eCopyConstructor)) tok = i->function->token; // Check initialization list for (; tok && tok != i->classEnd; tok = tok->next()) { if (Token::Match(tok, "sizeof|decltype|typeid (")) tok = tok->next()->link(); else if (tok && Token::simpleMatch(tok, "* 0")) { if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp() || tok->previous()->isAssignmentOp()) { nullPointerError(tok); } } else if (tok && Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) nullPointerError(tok); else if (tok && Token::Match(tok->previous(), "!!. %var% (") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { if (Token::simpleMatch(tok->tokAt(2), "0 )") && tok->varId()) { // constructor call const Variable* var = symbolDatabase->getVariableFromVarId(tok->varId()); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string !!::")) nullPointerError(tok); } else { // function call std::list<const Token *> var; parseFunctionCall(*tok, var, 0); // is one of the var items a NULL pointer? for (std::list<const Token *>::const_iterator it = var.begin(); it != var.end(); ++it) { if (Token::Match(*it, "0 [,)]")) { nullPointerError(*it); } } } } else if (Token::simpleMatch(tok, "std :: string ( 0 )")) nullPointerError(tok); else if (tok && Token::simpleMatch(tok->previous(), ">> 0")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well const Token* tok2 = tok->previous(); // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::simpleMatch(tok2, "std :: cin")) nullPointerError(tok); if (tok2 && tok2->varId() != 0) { const Variable* var = symbolDatabase->getVariableFromVarId(tok2->varId()); if (var && Token::Match(var->typeStartToken(), "std :: istream|ifstream|istringstream|stringstream|fstream|iostream")) nullPointerError(tok); } } unsigned int ovarid = 0; if (Token::Match(tok, "0 ==|!= %var%")) ovarid = tok->tokAt(2)->varId(); else if (Token::Match(tok, "%var% ==|!= 0")) ovarid = tok->varId(); else if (Token::Match(tok, "%var% =|+=|+ 0 )|]|,|;|+")) ovarid = tok->varId(); if (ovarid) { const Variable* var = symbolDatabase->getVariableFromVarId(ovarid); if (var && !var->isPointer() && !var->isArray() && Token::Match(var->typeStartToken(), "std :: string !!::")) nullPointerError(tok); } } } }
//dereferenceBeforeCheck void CheckNullPointer::nullPointerByDeRefAndChec() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); // Dereferencing a pointer and then checking if it's NULL.. // This check will first scan for the check. And then scan backwards // from the check, searching for dereferencing. for (std::list<Scope>::const_iterator i = symbolDatabase->scopeList.begin(); i != symbolDatabase->scopeList.end(); ++i) { // TODO: false negatives. // - logical operators const Token* tok = i->classDef; if ((i->type == Scope::eIf || i->type == Scope::eElseIf || i->type == Scope::eWhile) && tok && Token::Match(tok, "else| %var% ( !| %var% )|%oror%|&&") && !tok->next()->isExpandedMacro()) { if (tok->str() == "else") tok = tok->next(); const Token * vartok = tok->tokAt(2); if (vartok && vartok->str() == "!") vartok = vartok->next(); if(!vartok) continue; // Variable id for pointer const unsigned int varid(vartok->varId()); if (varid == 0) continue; // Name of pointer const std::string& varname(vartok->str()); const Variable* var = symbolDatabase->getVariableFromVarId(varid); // Check that variable is a pointer.. if (!var || !var->isPointer()) continue; const Token * const decltok = var->nameToken(); bool inconclusive = false; for (const Token *tok1 = tok->previous(); tok1 && tok1 != decltok; tok1 = tok1->previous()) { if (tok1->str() == ")" && Token::Match(tok1->link()->previous(), "%var% (")) { const Token *tok2 = tok1->link(); while (tok2 && !Token::Match(tok2, "[;{}?:]")) tok2 = tok2->previous(); if (tok2 && Token::Match(tok2, "[?:]")) break; if (tok2 && Token::Match(tok2->next(), "%varid% = %var%", varid)) break; if (tok2 && Token::Match(tok2->next(), "while ( %varid%", varid)) break; if (Token::Match(tok1->link(), "( ! %varid% %oror%", varid) || Token::Match(tok1->link(), "( %varid% &&", varid)) { tok1 = tok1->link(); continue; } if (Token::simpleMatch(tok1->link()->previous(), "sizeof (")) { tok1 = tok1->link()->previous(); continue; } if (tok2 && Token::Match(tok2->next(), "%var% ( %varid% ,", varid)) { std::list<const Token *> varlist; parseFunctionCall(*(tok2->next()), varlist, 0); if (!varlist.empty() && varlist.front() == tok2->tokAt(3)) { nullPointerError(tok2->tokAt(3), varname, tok, inconclusive); // break; continue; } } // Passing pointer as parameter.. if (tok2 && Token::Match(tok2->next(), "%type% (")) { bool unknown = false; if (CanFunctionAssignPointer(tok2->next(), varid, unknown)) { if (!_settings->inconclusive || !unknown) break; inconclusive = true; } } // calling unknown function => it might initialize the pointer if (!(var->isLocal() || var->isArgument())) break; } if (tok1->str() == "break") break; if (tok1->varId() == varid) { // Don't write warning if the dereferencing is // guarded by ?: or && const Token *tok2 = tok1->previous(); if (tok2 && (tok2->isArithmeticalOp() || tok2->str() == "(")) { while (tok2 && !Token::Match(tok2, "[;{}?:]")) { if (tok2->str() == ")") { tok2 = tok2->link(); if (Token::Match(tok2, "( %varid% =", varid)) { tok2 = tok2->next(); break; } } // guarded by && if (tok2->varId() == varid && tok2->next()->str() == "&&") break; tok2 = tok2->previous(); } } if (!tok2 || Token::Match(tok2, "[?:]") || tok2->varId() == varid) continue; // unknown : this is set by isPointerDeRef if it is // uncertain bool unknown = _settings->inconclusive; // reassign : is the pointer reassigned like this: // tok = tok->next(); bool reassign = false; if (Token::Match(tok1->previous(), "= %varid% .", varid)) { const Token *back = tok1->tokAt(-2); while (back) { if (back->varId() == varid) { reassign = true; break; } if (Token::Match(back, "[{};,(]")) { break; } back = back->previous(); } } if (reassign) { break; } else if (Token::simpleMatch(tok1->tokAt(-2), "* )") && Token::Match(tok1->linkAt(-1)->tokAt(-2), "%varid% = (", tok1->varId())) { break; } else if (Token::simpleMatch(tok1->tokAt(-3), "* ) (") && Token::Match(tok1->linkAt(-2)->tokAt(-2), "%varid% = (", tok1->varId())) { break; } else if (Token::Match(tok1->previous(), "&&|%oror%")) { break; } else if (Token::Match(tok1->tokAt(-2), "&&|%oror% !")) { break; } else if (CheckNullPointer::isPointerDeRef(tok1, unknown, symbolDatabase)) { nullPointerError(tok1, varname, tok, inconclusive); break; } else if (tok1->strAt(-1) == "&") { break; } else if (tok1->strAt(1) == "=") { break; } } else if (tok1->str() == "{" || tok1->str() == "}") break; // label.. else if (Token::Match(tok1, "%type% :")) break; } } } }