STL: check for dangerous usage of string::c_str(). Ticket: #1116
This commit is contained in:
parent
3faaa397e9
commit
e54129fa8d
|
@ -918,3 +918,49 @@ void CheckStl::missingComparisonError(const Token *incrementToken1, const Token
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CheckStl::string_c_str()
|
||||||
|
{
|
||||||
|
// Try to detect common problems when using string::c_str()
|
||||||
|
for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())
|
||||||
|
{
|
||||||
|
// Locate executable scopes:
|
||||||
|
if (Token::Match(tok, ") const| {"))
|
||||||
|
{
|
||||||
|
std::set<unsigned int> localvar;
|
||||||
|
|
||||||
|
// scan through this executable scope:
|
||||||
|
unsigned int indentlevel = 0;
|
||||||
|
while (NULL != (tok = tok->next()))
|
||||||
|
{
|
||||||
|
if (tok->str() == "{")
|
||||||
|
++indentlevel;
|
||||||
|
else if (tok->str() == "}")
|
||||||
|
{
|
||||||
|
if (indentlevel <= 1)
|
||||||
|
break;
|
||||||
|
--indentlevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variable declarations..
|
||||||
|
else if (Token::Match(tok->previous(), "[;{}] std :: %type% %var% ;"))
|
||||||
|
localvar.insert(tok->tokAt(3)->varId());
|
||||||
|
else if (Token::Match(tok->previous(), "[;{}] %type% %var% ;"))
|
||||||
|
localvar.insert(tok->next()->varId());
|
||||||
|
|
||||||
|
// Invalid usage..
|
||||||
|
else if (Token::Match(tok, "throw %var% . c_str ( ) ;") &&
|
||||||
|
tok->next()->varId() > 0 &&
|
||||||
|
localvar.find(tok->next()->varId()) != localvar.end())
|
||||||
|
{
|
||||||
|
string_c_strError(tok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckStl::string_c_strError(const Token *tok)
|
||||||
|
{
|
||||||
|
reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str()");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@ public:
|
||||||
checkStl.pushback();
|
checkStl.pushback();
|
||||||
checkStl.stlBoundries();
|
checkStl.stlBoundries();
|
||||||
checkStl.if_find();
|
checkStl.if_find();
|
||||||
|
checkStl.string_c_str();
|
||||||
|
|
||||||
// Style check
|
// Style check
|
||||||
checkStl.size();
|
checkStl.size();
|
||||||
|
@ -129,6 +130,10 @@ public:
|
||||||
void missingComparison();
|
void missingComparison();
|
||||||
void missingComparisonError(const Token *incrementToken1, const Token *incrementToken2);
|
void missingComparisonError(const Token *incrementToken1, const Token *incrementToken2);
|
||||||
|
|
||||||
|
/** Check for common mistakes when using the function string::c_str() */
|
||||||
|
void string_c_str();
|
||||||
|
void string_c_strError(const Token *tok);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -163,6 +168,7 @@ private:
|
||||||
stlBoundriesError(0, "container");
|
stlBoundriesError(0, "container");
|
||||||
if_findError(0, false);
|
if_findError(0, false);
|
||||||
if_findError(0, true);
|
if_findError(0, true);
|
||||||
|
string_c_strError(0);
|
||||||
sizeError(0);
|
sizeError(0);
|
||||||
eraseByValueError(0, "container", "iterator");
|
eraseByValueError(0, "container", "iterator");
|
||||||
redundantIfRemoveError(0);
|
redundantIfRemoveError(0);
|
||||||
|
@ -183,7 +189,8 @@ private:
|
||||||
"* for vectors: using iterator/pointer after push_back has been used\n"
|
"* for vectors: using iterator/pointer after push_back has been used\n"
|
||||||
"* optimisation: use empty() instead of size() to guarantee fast code\n"
|
"* optimisation: use empty() instead of size() to guarantee fast code\n"
|
||||||
"* suspicious condition when using find\n"
|
"* suspicious condition when using find\n"
|
||||||
"* redundant condition\n";
|
"* redundant condition\n"
|
||||||
|
"* common mistakes when using string::c_str()";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isStlContainer(const Token *tok);
|
bool isStlContainer(const Token *tok);
|
||||||
|
|
|
@ -98,6 +98,9 @@ private:
|
||||||
TEST_CASE(missingInnerComparison1);
|
TEST_CASE(missingInnerComparison1);
|
||||||
TEST_CASE(missingInnerComparison2); // no FP when there is comparison
|
TEST_CASE(missingInnerComparison2); // no FP when there is comparison
|
||||||
TEST_CASE(missingInnerComparison3); // no FP when there is iterator shadowing
|
TEST_CASE(missingInnerComparison3); // no FP when there is iterator shadowing
|
||||||
|
|
||||||
|
// catch common problems when using the string::c_str() function
|
||||||
|
TEST_CASE(cstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void check(const std::string &code)
|
void check(const std::string &code)
|
||||||
|
@ -1090,6 +1093,22 @@ private:
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cstr()
|
||||||
|
{
|
||||||
|
check("void f() {\n"
|
||||||
|
" std::string errmsg;\n"
|
||||||
|
" throw errmsg.c_str();\n"
|
||||||
|
"}");
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (error) Dangerous usage of c_str()\n", errout.str());
|
||||||
|
|
||||||
|
check("std::string f();\n"
|
||||||
|
"\n"
|
||||||
|
"void foo() {\n"
|
||||||
|
" const char *c = f().c_str();\n"
|
||||||
|
"}");
|
||||||
|
TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous usage of c_str()\n", errout.str());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestStl)
|
REGISTER_TEST(TestStl)
|
||||||
|
|
Loading…
Reference in New Issue