Fixed #9295 (ValueFlow: Does not handle noreturn function)

This commit is contained in:
Daniel Marjamäki 2020-12-28 19:59:51 +01:00
parent c643e7e7b5
commit b94a6d595a
6 changed files with 52 additions and 12 deletions

View File

@ -101,7 +101,13 @@
<interleave>
<optional>
<element name="noreturn"><ref name="DATA-BOOL"/></element>
<element name="noreturn">
<choice>
<value>true</value>
<value>false</value>
<value>maybe</value>
</choice>
</element>
</optional>
<optional>
<element name="pure"><empty/></element>

View File

@ -4566,7 +4566,7 @@ The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun
<!-- int raise(int sig); -->
<function name="raise,std::raise">
<returnValue type="int"/>
<noreturn>false</noreturn>
<noreturn>maybe</noreturn>
<arg nr="1" direction="in">
<not-uninit/>
</arg>

View File

@ -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 {

View File

@ -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<std::string, Function>::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<std::string, bool>::const_iterator it = mNoReturn.find(getFunctionName(ftok));
return (it != mNoReturn.end() && it->second);
const std::map<std::string, FalseTrueMaybe>::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<std::string, bool>::const_iterator it = mNoReturn.find(getFunctionName(ftok));
return (it != mNoReturn.end() && !it->second);
const std::map<std::string, FalseTrueMaybe>::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

View File

@ -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 <function> xml node
Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set<std::string> &unknown_elements);
@ -554,12 +556,13 @@ private:
int mOffset;
std::set<std::string> mBlocks;
};
enum class FalseTrueMaybe { False, True, Maybe };
int mAllocId;
std::set<std::string> mFiles;
std::map<std::string, AllocFunc> mAlloc; // allocation functions
std::map<std::string, AllocFunc> mDealloc; // deallocation functions
std::map<std::string, AllocFunc> mRealloc; // reallocation functions
std::map<std::string, bool> mNoReturn; // is function noreturn?
std::map<std::string, FalseTrueMaybe> mNoReturn; // is function noreturn?
std::map<std::string, std::string> mReturnValue;
std::map<std::string, std::string> mReturnValueType;
std::map<std::string, int> mReturnValueContainer;

View File

@ -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());