Fixed #9295 (ValueFlow: Does not handle noreturn function)
This commit is contained in:
parent
c643e7e7b5
commit
b94a6d595a
|
@ -101,7 +101,13 @@
|
||||||
|
|
||||||
<interleave>
|
<interleave>
|
||||||
<optional>
|
<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>
|
||||||
<optional>
|
<optional>
|
||||||
<element name="pure"><empty/></element>
|
<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); -->
|
<!-- int raise(int sig); -->
|
||||||
<function name="raise,std::raise">
|
<function name="raise,std::raise">
|
||||||
<returnValue type="int"/>
|
<returnValue type="int"/>
|
||||||
<noreturn>false</noreturn>
|
<noreturn>maybe</noreturn>
|
||||||
<arg nr="1" direction="in">
|
<arg nr="1" direction="in">
|
||||||
<not-uninit/>
|
<not-uninit/>
|
||||||
</arg>
|
</arg>
|
||||||
|
|
|
@ -934,6 +934,7 @@ void CppCheck::checkRawTokens(const Tokenizer &tokenizer)
|
||||||
|
|
||||||
void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
|
void CppCheck::checkNormalTokens(const Tokenizer &tokenizer)
|
||||||
{
|
{
|
||||||
|
mSettings.library.bugHunting = mSettings.bugHunting;
|
||||||
if (mSettings.bugHunting)
|
if (mSettings.bugHunting)
|
||||||
ExprEngine::runChecks(this, &tokenizer, &mSettings);
|
ExprEngine::runChecks(this, &tokenizer, &mSettings);
|
||||||
else {
|
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()) {
|
for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) {
|
||||||
const std::string functionnodename = functionnode->Name();
|
const std::string functionnodename = functionnode->Name();
|
||||||
if (functionnodename == "noreturn")
|
if (functionnodename == "noreturn") {
|
||||||
mNoReturn[name] = (strcmp(functionnode->GetText(), "true") == 0);
|
if (strcmp(functionnode->GetText(), "false") == 0)
|
||||||
else if (functionnodename == "pure")
|
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;
|
func.ispure = true;
|
||||||
else if (functionnodename == "const") {
|
else if (functionnodename == "const") {
|
||||||
func.ispure = true;
|
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));
|
const std::map<std::string, Function>::const_iterator it = functions.find(getFunctionName(ftok));
|
||||||
return (it != functions.end() && it->second.isconst);
|
return (it != functions.end() && it->second.isconst);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Library::isnoreturn(const Token *ftok) const
|
bool Library::isnoreturn(const Token *ftok) const
|
||||||
{
|
{
|
||||||
if (ftok->function() && ftok->function()->isAttributeNoreturn())
|
if (ftok->function() && ftok->function()->isAttributeNoreturn())
|
||||||
return true;
|
return true;
|
||||||
if (isNotLibraryFunction(ftok))
|
if (isNotLibraryFunction(ftok))
|
||||||
return false;
|
return false;
|
||||||
const std::map<std::string, bool>::const_iterator it = mNoReturn.find(getFunctionName(ftok));
|
const std::map<std::string, FalseTrueMaybe>::const_iterator it = mNoReturn.find(getFunctionName(ftok));
|
||||||
return (it != mNoReturn.end() && it->second);
|
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
|
bool Library::isnotnoreturn(const Token *ftok) const
|
||||||
|
@ -1418,8 +1428,12 @@ bool Library::isnotnoreturn(const Token *ftok) const
|
||||||
return false;
|
return false;
|
||||||
if (isNotLibraryFunction(ftok))
|
if (isNotLibraryFunction(ftok))
|
||||||
return false;
|
return false;
|
||||||
const std::map<std::string, bool>::const_iterator it = mNoReturn.find(getFunctionName(ftok));
|
const std::map<std::string, FalseTrueMaybe>::const_iterator it = mNoReturn.find(getFunctionName(ftok));
|
||||||
return (it != mNoReturn.end() && !it->second);
|
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
|
bool Library::markupFile(const std::string &path) const
|
||||||
|
|
|
@ -140,7 +140,7 @@ public:
|
||||||
|
|
||||||
/** add noreturn function setting */
|
/** add noreturn function setting */
|
||||||
void setnoreturn(const std::string& funcname, bool noreturn) {
|
void setnoreturn(const std::string& funcname, bool noreturn) {
|
||||||
mNoReturn[funcname] = noreturn;
|
mNoReturn[funcname] = noreturn ? FalseTrueMaybe::True : FalseTrueMaybe::False;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isCompliantValidationExpression(const char* p);
|
static bool isCompliantValidationExpression(const char* p);
|
||||||
|
@ -496,6 +496,8 @@ public:
|
||||||
enum class TypeCheck { def, check, suppress };
|
enum class TypeCheck { def, check, suppress };
|
||||||
TypeCheck getTypeCheck(const std::string &check, const std::string &typeName) const;
|
TypeCheck getTypeCheck(const std::string &check, const std::string &typeName) const;
|
||||||
|
|
||||||
|
bool bugHunting;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// load a <function> xml node
|
// load a <function> xml node
|
||||||
Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set<std::string> &unknown_elements);
|
Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set<std::string> &unknown_elements);
|
||||||
|
@ -554,12 +556,13 @@ private:
|
||||||
int mOffset;
|
int mOffset;
|
||||||
std::set<std::string> mBlocks;
|
std::set<std::string> mBlocks;
|
||||||
};
|
};
|
||||||
|
enum class FalseTrueMaybe { False, True, Maybe };
|
||||||
int mAllocId;
|
int mAllocId;
|
||||||
std::set<std::string> mFiles;
|
std::set<std::string> mFiles;
|
||||||
std::map<std::string, AllocFunc> mAlloc; // allocation functions
|
std::map<std::string, AllocFunc> mAlloc; // allocation functions
|
||||||
std::map<std::string, AllocFunc> mDealloc; // deallocation functions
|
std::map<std::string, AllocFunc> mDealloc; // deallocation functions
|
||||||
std::map<std::string, AllocFunc> mRealloc; // reallocation 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> mReturnValue;
|
||||||
std::map<std::string, std::string> mReturnValueType;
|
std::map<std::string, std::string> mReturnValueType;
|
||||||
std::map<std::string, int> mReturnValueContainer;
|
std::map<std::string, int> mReturnValueContainer;
|
||||||
|
|
|
@ -46,6 +46,9 @@ private:
|
||||||
TEST_CASE(bufferOverflowMemCmp2);
|
TEST_CASE(bufferOverflowMemCmp2);
|
||||||
TEST_CASE(bufferOverflowStrcpy1);
|
TEST_CASE(bufferOverflowStrcpy1);
|
||||||
TEST_CASE(bufferOverflowStrcpy2);
|
TEST_CASE(bufferOverflowStrcpy2);
|
||||||
|
|
||||||
|
TEST_CASE(divisionByZeroNoReturn);
|
||||||
|
|
||||||
TEST_CASE(uninit);
|
TEST_CASE(uninit);
|
||||||
TEST_CASE(uninit_array);
|
TEST_CASE(uninit_array);
|
||||||
TEST_CASE(uninit_function_par);
|
TEST_CASE(uninit_function_par);
|
||||||
|
@ -61,6 +64,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const char code[]) {
|
void check(const char code[]) {
|
||||||
|
settings.bugHunting = settings.library.bugHunting = true;
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
std::istringstream istr(code);
|
std::istringstream istr(code);
|
||||||
tokenizer.tokenize(istr, "test.cpp");
|
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());
|
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() {
|
void uninit() {
|
||||||
check("void foo() { int x; x = x + 1; }");
|
check("void foo() { int x; x = x + 1; }");
|
||||||
ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:1]: (error) Cannot determine that 'x' is initialized\n", errout.str());
|
||||||
|
|
Loading…
Reference in New Issue