From e54129fa8d600e52ef01f20be549a2bd9ae9b31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 17 Oct 2010 19:18:46 +0200 Subject: [PATCH] STL: check for dangerous usage of string::c_str(). Ticket: #1116 --- lib/checkstl.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ lib/checkstl.h | 9 ++++++++- test/teststl.cpp | 19 +++++++++++++++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 9698b6b14..b9beae19a 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -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 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()"); +} + diff --git a/lib/checkstl.h b/lib/checkstl.h index b0e061816..b10b0b6bc 100644 --- a/lib/checkstl.h +++ b/lib/checkstl.h @@ -55,6 +55,7 @@ public: checkStl.pushback(); checkStl.stlBoundries(); checkStl.if_find(); + checkStl.string_c_str(); // Style check checkStl.size(); @@ -129,6 +130,10 @@ public: void missingComparison(); 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: /** @@ -163,6 +168,7 @@ private: stlBoundriesError(0, "container"); if_findError(0, false); if_findError(0, true); + string_c_strError(0); sizeError(0); eraseByValueError(0, "container", "iterator"); redundantIfRemoveError(0); @@ -183,7 +189,8 @@ private: "* for vectors: using iterator/pointer after push_back has been used\n" "* optimisation: use empty() instead of size() to guarantee fast code\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); diff --git a/test/teststl.cpp b/test/teststl.cpp index a1309ced0..4e6982106 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -98,6 +98,9 @@ private: TEST_CASE(missingInnerComparison1); TEST_CASE(missingInnerComparison2); // no FP when there is comparison 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) @@ -1090,6 +1093,22 @@ private: "}\n"); 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)