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::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); } }
/** * @brief Does one part of the check for nullPointer(). * -# default argument that sets a pointer to 0 * -# dereference pointer */ void CheckNullPointer::nullPointerDefaultArgument() { const SymbolDatabase *symbolDatabase = _tokenizer->getSymbolDatabase(); 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; // Scan the argument list for default arguments that are pointers and // which default to a NULL pointer if no argument is specified. std::set<unsigned int> pointerArgs; for (const Token *tok = scope->function->arg; tok != scope->function->arg->link(); tok = tok->next()) { if (Token::Match(tok, "%var% = 0 ,|)") && tok->varId() != 0) { const Variable *var = tok->variable(); if (var && var->isPointer()) pointerArgs.insert(tok->varId()); } } // Report an error if any of the default-NULL arguments are dereferenced if (!pointerArgs.empty()) { for (const Token *tok = scope->classStart; tok != scope->classEnd; tok = tok->next()) { // If we encounter a possible NULL-pointer check, skip over its body if (Token::simpleMatch(tok, "if ( ")) { bool dependsOnPointer = false; const Token *endOfCondition = tok->next()->link(); if (!endOfCondition) continue; const Token *startOfIfBlock = Token::simpleMatch(endOfCondition, ") {") ? endOfCondition->next() : nullptr; if (!startOfIfBlock) continue; // If this if() statement may return, it may be a null // pointer check for the pointers referenced in its condition const Token *endOfIf = startOfIfBlock->link(); bool isExitOrReturn = Token::findmatch(startOfIfBlock, "exit|return|throw", endOfIf) != nullptr; if (Token::Match(tok, "if ( %var% == 0 )")) { const unsigned int var = tok->tokAt(2)->varId(); if (var > 0 && pointerArgs.count(var) > 0) { if (isExitOrReturn) pointerArgs.erase(var); else dependsOnPointer = true; } } else { for (const Token *tok2 = tok->next(); tok2 != endOfCondition; tok2 = tok2->next()) { if (tok2->isName() && tok2->varId() > 0 && pointerArgs.count(tok2->varId()) > 0) { // If the if() depends on a pointer and may return, stop // considering that pointer because it may be a NULL-pointer // check that returns if the pointer is NULL. if (isExitOrReturn) pointerArgs.erase(tok2->varId()); else dependsOnPointer = true; } } } if (dependsOnPointer && endOfIf) { for (; tok != endOfIf; tok = tok->next()) { // If a pointer is assigned a new value, stop considering it. if (Token::Match(tok, "%var% =")) pointerArgs.erase(tok->varId()); else removeAssignedVarFromSet(tok, pointerArgs); } continue; } } // If there is a noreturn function (e.g. exit()), stop considering the rest of // this function. bool unknown = false; if (Token::Match(tok, "return|throw|exit") || (_tokenizer->IsScopeNoReturn(tok, &unknown) && !unknown)) break; removeAssignedVarFromSet(tok, pointerArgs); if (tok->varId() == 0 || pointerArgs.count(tok->varId()) == 0) continue; // If a pointer is assigned a new value, stop considering it. if (Token::Match(tok, "%var% =")) pointerArgs.erase(tok->varId()); // If a pointer dereference is preceded by an && or ||, // they serve as a sequence point so the dereference // may not be executed. if (isPointerDeRef(tok, unknown) && !unknown && tok->strAt(-1) != "&&" && tok->strAt(-1) != "||" && tok->strAt(-2) != "&&" && tok->strAt(-2) != "||") nullPointerDefaultArgError(tok, tok->str()); } } } }
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; } } } }
bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings) { unknown = false; // Is pointer used as function parameter? if (Token::Match(tok->previous(), "[(,] %name% [,)]") && settings) { const Token *ftok = tok->previous(); while (ftok && ftok->str() != "(") { if (ftok->str() == ")") ftok = ftok->link(); ftok = ftok->previous(); } if (ftok && ftok->previous()) { std::list<const Token *> varlist; parseFunctionCall(*ftok->previous(), varlist, &settings->library); if (std::find(varlist.begin(), varlist.end(), tok) != varlist.end()) { return true; } } } const Token* parent = tok->astParent(); if (!parent) return false; if (parent->str() == "." && parent->astOperand2() == tok) return isPointerDeRef(parent, unknown, settings); const bool firstOperand = parent->astOperand1() == tok; while (parent->str() == "(" && (parent->astOperand2() == nullptr && parent->strAt(1) != ")")) { // Skip over casts parent = parent->astParent(); if (!parent) return false; } // Dereferencing pointer.. if (parent->isUnaryOp("*") && !Token::Match(parent->tokAt(-2), "sizeof|decltype|typeof")) return true; // array access if (firstOperand && parent->str() == "[" && (!parent->astParent() || parent->astParent()->str() != "&")) return true; // address of member variable / array element const Token *parent2 = parent; while (Token::Match(parent2, "[|.")) parent2 = parent2->astParent(); if (parent2 != parent && parent2 && parent2->isUnaryOp("&")) return false; // read/write member variable if (firstOperand && parent->str() == "." && (!parent->astParent() || parent->astParent()->str() != "&")) { if (!parent->astParent() || parent->astParent()->str() != "(" || parent->astParent() == tok->previous()) return true; unknown = true; return false; } if (Token::Match(tok, "%name% (")) return true; if (Token::Match(tok, "%var% = %var% .") && tok->varId() == tok->tokAt(2)->varId()) return true; // std::string dereferences nullpointers if (Token::Match(parent->tokAt(-3), "std :: string|wstring (") && tok->strAt(1) == ")") return true; if (Token::Match(parent->previous(), "%name% (") && tok->strAt(1) == ")") { const Variable* var = tok->tokAt(-2)->variable(); if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) return true; } // streams dereference nullpointers if (Token::Match(parent, "<<|>>") && !firstOperand) { const Variable* var = tok->variable(); if (var && var->isPointer() && Token::Match(var->typeStartToken(), "char|wchar_t")) { // Only outputting or reading to char* can cause problems const Token* tok2 = parent; // Find start of statement for (; tok2; tok2 = tok2->previous()) { if (Token::Match(tok2->previous(), ";|{|}|:")) break; } if (Token::Match(tok2, "std :: cout|cin|cerr")) return true; if (tok2 && tok2->varId() != 0) { const Variable* var2 = tok2->variable(); if (var2 && var2->isStlType(stl_stream)) return true; } } } const Variable *ovar = nullptr; if (Token::Match(parent, "+|==|!=") || (parent->str() == "=" && !firstOperand)) { if (parent->astOperand1() == tok && parent->astOperand2()) ovar = parent->astOperand2()->variable(); else if (parent->astOperand1() && parent->astOperand2() == tok) ovar = parent->astOperand1()->variable(); } if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType()) return true; // assume that it's not a dereference (no false positives) return false; }
/** * Is there a pointer dereference? Everything that should result in * a nullpointer dereference error message will result in a true * return value. If it's unknown if the pointer is dereferenced false * is returned. * @param tok token for the pointer * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) * @return true => there is a dereference */ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown) const { return isPointerDeRef(tok, unknown, mSettings); }