diff --git a/cfg/cppcheck-cfg.rng b/cfg/cppcheck-cfg.rng index 3dc77b886..4f3758b8e 100644 --- a/cfg/cppcheck-cfg.rng +++ b/cfg/cppcheck-cfg.rng @@ -101,7 +101,13 @@ - + + + true + false + maybe + + diff --git a/cfg/std.cfg b/cfg/std.cfg index 493e6ab74..16f88da2a 100644 --- a/cfg/std.cfg +++ b/cfg/std.cfg @@ -4566,7 +4566,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun - false + maybe diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 3001214ff..937b721c6 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -934,6 +934,7 @@ void CppCheck::checkRawTokens(const Tokenizer &tokenizer) void CppCheck::checkNormalTokens(const Tokenizer &tokenizer) { + mSettings.library.bugHunting = mSettings.bugHunting; if (mSettings.bugHunting) ExprEngine::runChecks(this, &tokenizer, &mSettings); else { diff --git a/lib/library.cpp b/lib/library.cpp index d929d572c..f6187d2f2 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -56,7 +56,7 @@ static void gettokenlistfromvalid(const std::string& valid, TokenList& tokenList } } -Library::Library() : mAllocId(0) +Library::Library() : bugHunting(false), mAllocId(0) { } @@ -646,9 +646,14 @@ Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, co for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { const std::string functionnodename = functionnode->Name(); - if (functionnodename == "noreturn") - mNoReturn[name] = (strcmp(functionnode->GetText(), "true") == 0); - else if (functionnodename == "pure") + if (functionnodename == "noreturn") { + if (strcmp(functionnode->GetText(), "false") == 0) + mNoReturn[name] = FalseTrueMaybe::False; + else if (strcmp(functionnode->GetText(), "maybe") == 0) + mNoReturn[name] = FalseTrueMaybe::Maybe; + else + mNoReturn[name] = FalseTrueMaybe::True; // Safe + } else if (functionnodename == "pure") func.ispure = true; else if (functionnodename == "const") { func.ispure = true; @@ -1402,14 +1407,19 @@ bool Library::isFunctionConst(const Token *ftok) const const std::map::const_iterator it = functions.find(getFunctionName(ftok)); return (it != functions.end() && it->second.isconst); } + bool Library::isnoreturn(const Token *ftok) const { if (ftok->function() && ftok->function()->isAttributeNoreturn()) return true; if (isNotLibraryFunction(ftok)) return false; - const std::map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); - return (it != mNoReturn.end() && it->second); + const std::map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); + if (it == mNoReturn.end()) + return false; + if (it->second == FalseTrueMaybe::Maybe) + return !bugHunting; // in bugHunting "maybe" means function is not noreturn + return it->second == FalseTrueMaybe::True; } bool Library::isnotnoreturn(const Token *ftok) const @@ -1418,8 +1428,12 @@ bool Library::isnotnoreturn(const Token *ftok) const return false; if (isNotLibraryFunction(ftok)) return false; - const std::map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); - return (it != mNoReturn.end() && !it->second); + const std::map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); + if (it == mNoReturn.end()) + return false; + if (it->second == FalseTrueMaybe::Maybe) + return bugHunting; // in bugHunting "maybe" means function is not noreturn + return it->second == FalseTrueMaybe::False; } bool Library::markupFile(const std::string &path) const diff --git a/lib/library.h b/lib/library.h index cd0bf1bc9..2a41eaa2b 100644 --- a/lib/library.h +++ b/lib/library.h @@ -140,7 +140,7 @@ public: /** add noreturn function setting */ void setnoreturn(const std::string& funcname, bool noreturn) { - mNoReturn[funcname] = noreturn; + mNoReturn[funcname] = noreturn ? FalseTrueMaybe::True : FalseTrueMaybe::False; } static bool isCompliantValidationExpression(const char* p); @@ -496,6 +496,8 @@ public: enum class TypeCheck { def, check, suppress }; TypeCheck getTypeCheck(const std::string &check, const std::string &typeName) const; + bool bugHunting; + private: // load a xml node Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements); @@ -554,12 +556,13 @@ private: int mOffset; std::set mBlocks; }; + enum class FalseTrueMaybe { False, True, Maybe }; int mAllocId; std::set mFiles; std::map mAlloc; // allocation functions std::map mDealloc; // deallocation functions std::map mRealloc; // reallocation functions - std::map mNoReturn; // is function noreturn? + std::map mNoReturn; // is function noreturn? std::map mReturnValue; std::map mReturnValueType; std::map mReturnValueContainer; diff --git a/test/testbughuntingchecks.cpp b/test/testbughuntingchecks.cpp index 5dcdef71b..4c417a9cf 100644 --- a/test/testbughuntingchecks.cpp +++ b/test/testbughuntingchecks.cpp @@ -46,6 +46,9 @@ private: TEST_CASE(bufferOverflowMemCmp2); TEST_CASE(bufferOverflowStrcpy1); TEST_CASE(bufferOverflowStrcpy2); + + TEST_CASE(divisionByZeroNoReturn); + TEST_CASE(uninit); TEST_CASE(uninit_array); TEST_CASE(uninit_function_par); @@ -61,6 +64,7 @@ private: } void check(const char code[]) { + settings.bugHunting = settings.library.bugHunting = true; Tokenizer tokenizer(&settings, this); std::istringstream istr(code); tokenizer.tokenize(istr, "test.cpp"); @@ -191,6 +195,18 @@ private: ASSERT_EQUALS("[test.cpp:2]: (error) Buffer read/write, when calling 'strcpy' it cannot be determined that 1st argument is not overflowed\n", errout.str()); } + + void divisionByZeroNoReturn() { + // Don't know if function is noreturn or not.. + check("int f(int leftarg, int rightarg) {\n" + " if (rightarg == 0)\n" + " raise (SIGFPE);\n" // <- maybe noreturn + " return leftarg / rightarg;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) There is division, cannot determine that there can't be a division by zero.\n", errout.str()); + } + + void uninit() { check("void foo() { int x; x = x + 1; }"); ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str());