Fixed #9295 (ValueFlow: Does not handle noreturn function)
This commit is contained in:
parent
c643e7e7b5
commit
b94a6d595a
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
Loading…
Reference in New Issue