uninitstring: fix false negatives when non-zero memset is used. Ticket: #3050
This commit is contained in:
parent
b27bbea44a
commit
c7d0beefa8
|
@ -44,7 +44,7 @@ class UninitVar : public ExecutionPath
|
||||||
public:
|
public:
|
||||||
/** Startup constructor */
|
/** Startup constructor */
|
||||||
UninitVar(Check *c)
|
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 */
|
/** internal constructor for creating extra checks */
|
||||||
UninitVar(Check *c, unsigned int v, const std::string &name, bool p, bool a)
|
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
|
bool is_equal(const ExecutionPath *e) const
|
||||||
{
|
{
|
||||||
const UninitVar *c = static_cast<const UninitVar *>(e);
|
const UninitVar *c = static_cast<const UninitVar *>(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 */
|
/** variable name for this check */
|
||||||
|
@ -86,6 +86,9 @@ private:
|
||||||
/** is this variable initialized with strncpy (not always zero-terminated) */
|
/** is this variable initialized with strncpy (not always zero-terminated) */
|
||||||
bool strncpy_;
|
bool strncpy_;
|
||||||
|
|
||||||
|
/** is this variable initialized but not zero-terminated (memset) */
|
||||||
|
bool memset_nonzero;
|
||||||
|
|
||||||
/** allocating pointer. For example : p = malloc(10); */
|
/** allocating pointer. For example : p = malloc(10); */
|
||||||
static void alloc_pointer(std::list<ExecutionPath *> &checks, unsigned int varid)
|
static void alloc_pointer(std::list<ExecutionPath *> &checks, unsigned int varid)
|
||||||
{
|
{
|
||||||
|
@ -224,6 +227,24 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Initialize an array with memset (not zero). */
|
||||||
|
static void init_memset_nonzero(std::list<ExecutionPath *> &checks, const Token *tok)
|
||||||
|
{
|
||||||
|
const unsigned int varid(tok->varId());
|
||||||
|
if (!varid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::list<ExecutionPath *>::const_iterator it;
|
||||||
|
for (it = checks.begin(); it != checks.end(); ++it)
|
||||||
|
{
|
||||||
|
UninitVar *c = dynamic_cast<UninitVar *>(*it);
|
||||||
|
if (c && c->varId == varid)
|
||||||
|
{
|
||||||
|
c->memset_nonzero = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -268,8 +289,8 @@ private:
|
||||||
CheckUninitVar *checkUninitVar = dynamic_cast<CheckUninitVar *>(c->owner);
|
CheckUninitVar *checkUninitVar = dynamic_cast<CheckUninitVar *>(c->owner);
|
||||||
if (checkUninitVar)
|
if (checkUninitVar)
|
||||||
{
|
{
|
||||||
if (c->strncpy_)
|
if (c->strncpy_ || c->memset_nonzero)
|
||||||
checkUninitVar->uninitstringError(tok, c->varname);
|
checkUninitVar->uninitstringError(tok, c->varname, c->strncpy_);
|
||||||
else if (c->pointer && c->alloc)
|
else if (c->pointer && c->alloc)
|
||||||
checkUninitVar->uninitdataError(tok, c->varname);
|
checkUninitVar->uninitdataError(tok, c->varname);
|
||||||
else
|
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 ( )"))
|
if (Token::simpleMatch(&tok, "asm ( )"))
|
||||||
{
|
{
|
||||||
ExecutionPath::bailOut(checks);
|
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)
|
void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname)
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
/** @brief new type of check: check execution paths */
|
/** @brief new type of check: check execution paths */
|
||||||
void executionPaths();
|
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 uninitdataError(const Token *tok, const std::string &varname);
|
||||||
void uninitvarError(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);
|
CheckUninitVar c(0, settings, errorLogger);
|
||||||
|
|
||||||
// error
|
// error
|
||||||
c.uninitstringError(0, "varname");
|
c.uninitstringError(0, "varname", true);
|
||||||
c.uninitdataError(0, "varname");
|
c.uninitdataError(0, "varname");
|
||||||
c.uninitvarError(0, "varname");
|
c.uninitvarError(0, "varname");
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,7 @@ private:
|
||||||
TEST_CASE(uninitvar_switch); // handling switch
|
TEST_CASE(uninitvar_switch); // handling switch
|
||||||
TEST_CASE(uninitvar_references); // references
|
TEST_CASE(uninitvar_references); // references
|
||||||
TEST_CASE(uninitvar_strncpy); // strncpy doesn't always 0-terminate
|
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(uninitvar_func); // analyse functions
|
||||||
TEST_CASE(func_uninit_var); // analyse function calls for: 'int a(int x) { return x+x; }'
|
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; }'
|
TEST_CASE(func_uninit_pointer); // analyse function calls for: 'void a(int *p) { *p = 0; }'
|
||||||
|
@ -1313,6 +1314,16 @@ private:
|
||||||
ASSERT_EQUALS("", errout.str());
|
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[])
|
std::string analyseFunctions(const char code[])
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue