diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index af1c963b5..22aac025f 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -44,7 +44,7 @@ class UninitVar : public ExecutionPath public: /** Startup constructor */ UninitVar(Check *c) - : ExecutionPath(c, 0), pointer(false), array(false), alloc(false), strncpy_(false) + : ExecutionPath(c, 0), pointer(false), array(false), alloc(false), strncpy_(false), memset_nonzero(false) { } @@ -60,7 +60,7 @@ private: /** internal constructor for creating extra checks */ UninitVar(Check *c, unsigned int v, const std::string &name, bool p, bool a) - : ExecutionPath(c, v), varname(name), pointer(p), array(a), alloc(false), strncpy_(false) + : ExecutionPath(c, v), varname(name), pointer(p), array(a), alloc(false), strncpy_(false), memset_nonzero(false) { } @@ -68,7 +68,7 @@ private: bool is_equal(const ExecutionPath *e) const { const UninitVar *c = static_cast(e); - return (varname == c->varname && pointer == c->pointer && array == c->array && alloc == c->alloc && strncpy_ == c->strncpy_); + return (varname == c->varname && pointer == c->pointer && array == c->array && alloc == c->alloc && strncpy_ == c->strncpy_ && memset_nonzero == c->memset_nonzero); } /** variable name for this check */ @@ -86,6 +86,9 @@ private: /** is this variable initialized with strncpy (not always zero-terminated) */ bool strncpy_; + /** is this variable initialized but not zero-terminated (memset) */ + bool memset_nonzero; + /** allocating pointer. For example : p = malloc(10); */ static void alloc_pointer(std::list &checks, unsigned int varid) { @@ -224,6 +227,24 @@ private: } } + /** Initialize an array with memset (not zero). */ + static void init_memset_nonzero(std::list &checks, const Token *tok) + { + const unsigned int varid(tok->varId()); + if (!varid) + return; + + std::list::const_iterator it; + for (it = checks.begin(); it != checks.end(); ++it) + { + UninitVar *c = dynamic_cast(*it); + if (c && c->varId == varid) + { + c->memset_nonzero = true; + } + } + } + /** @@ -268,8 +289,8 @@ private: CheckUninitVar *checkUninitVar = dynamic_cast(c->owner); if (checkUninitVar) { - if (c->strncpy_) - checkUninitVar->uninitstringError(tok, c->varname); + if (c->strncpy_ || c->memset_nonzero) + checkUninitVar->uninitstringError(tok, c->varname, c->strncpy_); else if (c->pointer && c->alloc) checkUninitVar->uninitdataError(tok, c->varname); else @@ -679,6 +700,13 @@ private: } } + // memset (not zero terminated).. + if (Token::Match(&tok, "memset ( %var% , !!0 , %num% )")) + { + init_memset_nonzero(checks, tok.tokAt(2)); + return tok.next()->link(); + } + if (Token::simpleMatch(&tok, "asm ( )")) { ExecutionPath::bailOut(checks); @@ -1131,9 +1159,9 @@ void CheckUninitVar::executionPaths() } } -void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname) +void CheckUninitVar::uninitstringError(const Token *tok, const std::string &varname, bool strncpy_) { - reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "' (strncpy doesn't always 0-terminate it)"); + reportError(tok, Severity::error, "uninitstring", "Dangerous usage of '" + varname + "'" + (strncpy_ ? " (strncpy doesn't always 0-terminate it)" : " (not 0-terminated)")); } void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index 8ab4844ed..ebca05e5e 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -73,7 +73,7 @@ public: /** @brief new type of check: check execution paths */ void executionPaths(); - void uninitstringError(const Token *tok, const std::string &varname); + void uninitstringError(const Token *tok, const std::string &varname, bool strncpy_); void uninitdataError(const Token *tok, const std::string &varname); void uninitvarError(const Token *tok, const std::string &varname); @@ -82,7 +82,7 @@ public: CheckUninitVar c(0, settings, errorLogger); // error - c.uninitstringError(0, "varname"); + c.uninitstringError(0, "varname", true); c.uninitdataError(0, "varname"); c.uninitvarError(0, "varname"); } diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 6112fa552..5bcef9af9 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -45,6 +45,7 @@ private: TEST_CASE(uninitvar_switch); // handling switch TEST_CASE(uninitvar_references); // references TEST_CASE(uninitvar_strncpy); // strncpy doesn't always 0-terminate + TEST_CASE(uninitvar_memset); // not 0-terminated TEST_CASE(uninitvar_func); // analyse functions TEST_CASE(func_uninit_var); // analyse function calls for: 'int a(int x) { return x+x; }' TEST_CASE(func_uninit_pointer); // analyse function calls for: 'void a(int *p) { *p = 0; }' @@ -1313,6 +1314,16 @@ private: ASSERT_EQUALS("", errout.str()); } + // initialization with memset (not 0-terminating string).. + void uninitvar_memset() + { + checkUninitVar("void f() {\n" + " char a[20];\n" + " memset(a, 'a', 20);\n" + " strcat(a, s);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Dangerous usage of 'a' (not 0-terminated)\n", errout.str()); + } std::string analyseFunctions(const char code[]) {