Library: added init attribute to <alloc>

This commit is contained in:
Daniel Marjamäki 2013-07-15 08:44:00 +02:00
parent 16d0a818c9
commit 66d8fa62d1
4 changed files with 99 additions and 25 deletions

View File

@ -54,8 +54,8 @@ static const Token * skipBrackets(const Token *tok)
class UninitVar : public ExecutionPath {
public:
/** Startup constructor */
explicit UninitVar(Check *c, const SymbolDatabase* db, bool isc)
: ExecutionPath(c, 0), symbolDatabase(db), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) {
explicit UninitVar(Check *c, const SymbolDatabase* db, const Library *lib, bool isc)
: ExecutionPath(c, 0), symbolDatabase(db), library(lib), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) {
}
private:
@ -68,8 +68,8 @@ private:
void operator=(const UninitVar &);
/** internal constructor for creating extra checks */
UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, bool isc)
: ExecutionPath(c, v->varId()), symbolDatabase(db), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) {
UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, const Library *lib, bool isc)
: ExecutionPath(c, v->varId()), symbolDatabase(db), library(lib), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) {
}
/** is other execution path equal? */
@ -81,6 +81,9 @@ private:
/** pointer to symbol database */
const SymbolDatabase* symbolDatabase;
/** pointer to library */
const Library *library;
const bool isC;
/** variable for this check */
@ -425,7 +428,7 @@ private:
}
if (var2->isPointer())
checks.push_back(new UninitVar(owner, var2, symbolDatabase, isC));
checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC));
else if (var2->typeEndToken()->str() != ">") {
bool stdtype = false; // TODO: change to isC to handle unknown types better
for (const Token* tok2 = var2->typeStartToken(); tok2 != var2->nameToken(); tok2 = tok2->next()) {
@ -435,7 +438,7 @@ private:
}
}
if (stdtype && (!var2->isArray() || var2->nameToken()->linkAt(1)->strAt(1) == ";"))
checks.push_back(new UninitVar(owner, var2, symbolDatabase, isC));
checks.push_back(new UninitVar(owner, var2, symbolDatabase, library, isC));
}
return &tok;
}
@ -573,7 +576,8 @@ private:
return &tok;
}
if (Token::Match(tok.next(), "= malloc|kmalloc") || Token::simpleMatch(tok.next(), "= new char [")) {
if (Token::Match(tok.next(), "= malloc|kmalloc") || Token::simpleMatch(tok.next(), "= new char [") ||
(Token::Match(tok.next(), "= %var% (") && library->returnuninitdata.find(tok.strAt(2)) != library->returnuninitdata.end())) {
alloc_pointer(checks, tok.varId());
if (tok.strAt(3) == "(")
return tok.tokAt(3);
@ -1037,7 +1041,7 @@ void CheckUninitVar::executionPaths()
if (_settings->_jobs == 1)
UninitVar::analyseFunctions(_tokenizer->tokens(), UninitVar::uvarFunctions);
UninitVar c(this, _tokenizer->getSymbolDatabase(), _tokenizer->isC());
UninitVar c(this, _tokenizer->getSymbolDatabase(), &_settings->library, _tokenizer->isC());
checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c);
}
}

View File

@ -33,7 +33,15 @@ Library::Library() : allocid(0)
_dealloc["fclose"] = allocid;
}
Library::Library(const Library &lib) : use(lib.use), ignore(lib.ignore), allocid(lib.allocid), _alloc(lib._alloc), _dealloc(lib._dealloc), _noreturn(lib._noreturn)
Library::Library(const Library &lib) :
use(lib.use),
ignore(lib.ignore),
functionArgument(lib.functionArgument),
returnuninitdata(lib.returnuninitdata),
allocid(lib.allocid),
_alloc(lib._alloc),
_dealloc(lib._dealloc),
_noreturn(lib._noreturn)
{
}
@ -56,9 +64,13 @@ bool Library::load(const char path[])
if (strcmp(node->Name(),"memory")==0) {
while (!ismemory(++allocid));
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
if (strcmp(memorynode->Name(),"alloc")==0)
if (strcmp(memorynode->Name(),"alloc")==0) {
_alloc[memorynode->GetText()] = allocid;
else if (strcmp(memorynode->Name(),"dealloc")==0)
const char *init = memorynode->Attribute("init");
if (init && strcmp(init,"false")==0) {
returnuninitdata.insert(memorynode->GetText());
}
} else if (strcmp(memorynode->Name(),"dealloc")==0)
_dealloc[memorynode->GetText()] = allocid;
else if (strcmp(memorynode->Name(),"use")==0)
use.insert(memorynode->GetText());

View File

@ -80,6 +80,8 @@ public:
// function name, argument nr => argument data
std::map<std::string, std::map<int, Argument> > functionArgument;
std::set<std::string> returnuninitdata;
private:
int allocid;
std::map<std::string, int> _alloc; // allocation functions

View File

@ -449,7 +449,7 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
<term>callstack</term>
<listitem>
callstack - if available
callstack - if available
</listitem>
</varlistentry>
@ -457,7 +457,7 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
<term>file</term>
<listitem>
filename
filename
</listitem>
</varlistentry>
@ -465,7 +465,7 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
<term>id</term>
<listitem>
message id
message id
</listitem>
</varlistentry>
@ -473,7 +473,7 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
<term>line</term>
<listitem>
line number
line number
</listitem>
</varlistentry>
@ -481,7 +481,7 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
<term>message</term>
<listitem>
verbose message text
verbose message text
</listitem>
</varlistentry>
@ -489,7 +489,7 @@ gui/test.cpp,16,error,mismatchAllocDealloc,Mismatching allocation and deallocati
<term>severity</term>
<listitem>
severity
severity
</listitem>
</varlistentry>
</variablelist>
@ -731,11 +731,11 @@ Checking test.c...
&lt;/def&gt;</programlisting>
<section>
<title>Leaks</title>
<title>&lt;alloc&gt; and &lt;dealloc&gt;</title>
<para>Allocation and deallocation is defined with
<literal>&lt;memory&gt;</literal> and
<literal>&lt;resource&gt;.</literal></para>
<para>Allocation and deallocation is defined within
<literal>&lt;memory&gt;</literal> or <literal>&lt;resource&gt; using
&lt;alloc&gt; and &lt;dealloc&gt;.</literal></para>
<para>Here is example code:</para>
@ -760,8 +760,8 @@ Checking test.c...</programlisting>
<programlisting>&lt;?xml version="1.0"?&gt;
&lt;def&gt;
&lt;memory&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;alloc&gt;alloc_something&lt;/alloc&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;/memory&gt;
&lt;/def&gt;</programlisting>
@ -771,7 +771,27 @@ Checking test.c...</programlisting>
Checking test.c...
[test.c:10]: (error) Memory leak: p</programlisting>
<para>Another example code:</para>
<para>Some allocation functions initialize the allocated data. Others
don't. Here is a short code example:</para>
<programlisting>void f()
{
char *p = alloc_something();
char c = *p;
free_something();
}</programlisting>
<para>If alloc_something() initializes the memory that is allocated then
that code is OK. Otherwise that code is bad. To configure that </para>
<para/>
</section>
<section>
<title>&lt;ignore&gt; and &lt;use&gt;</title>
<para>The &lt;ignore&gt; and &lt;use&gt; tells Cppcheck how functions
uses allocated memory. Example code:</para>
<programlisting>void f()
{
@ -786,8 +806,8 @@ Checking test.c...
<programlisting>&lt;?xml version="1.0"?&gt;
&lt;def&gt;
&lt;memory&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;alloc&gt;alloc_something&lt;/alloc&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;/memory&gt;
&lt;ignore&gt;do_something&lt;/ignore&gt;
&lt;/def&gt;</programlisting>
@ -804,14 +824,50 @@ Checking test.c...
<para><programlisting>&lt;?xml version="1.0"?&gt;
&lt;def&gt;
&lt;memory&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;alloc&gt;alloc_something&lt;/alloc&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;use&gt;do_something&lt;/use&gt;
&lt;/memory&gt;
&lt;/def&gt;</programlisting>Running Cppcheck now:</para>
<programlisting># cppcheck --library=something.cfg test.c
Checking test.c...</programlisting>
<para>Cppcheck will often assume that functions "use" allocated memory.
By using &lt;ignore&gt; you can make Cppcheck detect more errors. By
using &lt;use&gt;, no extra errors are detected but Cppcheck will not
need to assume.</para>
</section>
<section>
<title>&lt;alloc init="false"&gt;</title>
<para>Some allocation function initialize the data, others don't. Here
is a example code:</para>
<programlisting>void f()
{
char *p = alloc_something();
char c = *p;
free_something();
}</programlisting>
<para>Here is a configuration that tells cppcheck that alloc_something
doesn't initialize the data:</para>
<programlisting>&lt;?xml version="1.0"?&gt;
&lt;def&gt;
&lt;memory&gt;
&lt;alloc init="false"&gt;alloc_something&lt;/alloc&gt;
&lt;dealloc&gt;free_something&lt;/dealloc&gt;
&lt;/memory&gt;
&lt;/def&gt;</programlisting>
<para>Now you will get this error message:</para>
<programlisting>daniel@dator:~/cppcheck$ ./cppcheck --library=something.cfg test.c
Checking test.c...
[test.c:4]: (error) Memory is allocated but not initialized: p</programlisting>
</section>
<section>