Library: added init attribute to <alloc>
This commit is contained in:
parent
16d0a818c9
commit
66d8fa62d1
|
@ -54,8 +54,8 @@ static const Token * skipBrackets(const Token *tok)
|
||||||
class UninitVar : public ExecutionPath {
|
class UninitVar : public ExecutionPath {
|
||||||
public:
|
public:
|
||||||
/** Startup constructor */
|
/** Startup constructor */
|
||||||
explicit UninitVar(Check *c, const SymbolDatabase* db, bool isc)
|
explicit UninitVar(Check *c, const SymbolDatabase* db, const Library *lib, bool isc)
|
||||||
: ExecutionPath(c, 0), symbolDatabase(db), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) {
|
: ExecutionPath(c, 0), symbolDatabase(db), library(lib), isC(isc), var(0), alloc(false), strncpy_(false), memset_nonzero(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -68,8 +68,8 @@ private:
|
||||||
void operator=(const UninitVar &);
|
void operator=(const UninitVar &);
|
||||||
|
|
||||||
/** internal constructor for creating extra checks */
|
/** internal constructor for creating extra checks */
|
||||||
UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, bool isc)
|
UninitVar(Check *c, const Variable* v, const SymbolDatabase* db, const Library *lib, bool isc)
|
||||||
: ExecutionPath(c, v->varId()), symbolDatabase(db), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) {
|
: ExecutionPath(c, v->varId()), symbolDatabase(db), library(lib), isC(isc), var(v), alloc(false), strncpy_(false), memset_nonzero(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** is other execution path equal? */
|
/** is other execution path equal? */
|
||||||
|
@ -81,6 +81,9 @@ private:
|
||||||
/** pointer to symbol database */
|
/** pointer to symbol database */
|
||||||
const SymbolDatabase* symbolDatabase;
|
const SymbolDatabase* symbolDatabase;
|
||||||
|
|
||||||
|
/** pointer to library */
|
||||||
|
const Library *library;
|
||||||
|
|
||||||
const bool isC;
|
const bool isC;
|
||||||
|
|
||||||
/** variable for this check */
|
/** variable for this check */
|
||||||
|
@ -425,7 +428,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (var2->isPointer())
|
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() != ">") {
|
else if (var2->typeEndToken()->str() != ">") {
|
||||||
bool stdtype = false; // TODO: change to isC to handle unknown types better
|
bool stdtype = false; // TODO: change to isC to handle unknown types better
|
||||||
for (const Token* tok2 = var2->typeStartToken(); tok2 != var2->nameToken(); tok2 = tok2->next()) {
|
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) == ";"))
|
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;
|
return &tok;
|
||||||
}
|
}
|
||||||
|
@ -573,7 +576,8 @@ private:
|
||||||
return &tok;
|
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());
|
alloc_pointer(checks, tok.varId());
|
||||||
if (tok.strAt(3) == "(")
|
if (tok.strAt(3) == "(")
|
||||||
return tok.tokAt(3);
|
return tok.tokAt(3);
|
||||||
|
@ -1037,7 +1041,7 @@ void CheckUninitVar::executionPaths()
|
||||||
if (_settings->_jobs == 1)
|
if (_settings->_jobs == 1)
|
||||||
UninitVar::analyseFunctions(_tokenizer->tokens(), UninitVar::uvarFunctions);
|
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);
|
checkExecutionPaths(_tokenizer->getSymbolDatabase(), &c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,15 @@ Library::Library() : allocid(0)
|
||||||
_dealloc["fclose"] = allocid;
|
_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) {
|
if (strcmp(node->Name(),"memory")==0) {
|
||||||
while (!ismemory(++allocid));
|
while (!ismemory(++allocid));
|
||||||
for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) {
|
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;
|
_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;
|
_dealloc[memorynode->GetText()] = allocid;
|
||||||
else if (strcmp(memorynode->Name(),"use")==0)
|
else if (strcmp(memorynode->Name(),"use")==0)
|
||||||
use.insert(memorynode->GetText());
|
use.insert(memorynode->GetText());
|
||||||
|
|
|
@ -80,6 +80,8 @@ public:
|
||||||
// function name, argument nr => argument data
|
// function name, argument nr => argument data
|
||||||
std::map<std::string, std::map<int, Argument> > functionArgument;
|
std::map<std::string, std::map<int, Argument> > functionArgument;
|
||||||
|
|
||||||
|
std::set<std::string> returnuninitdata;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int allocid;
|
int allocid;
|
||||||
std::map<std::string, int> _alloc; // allocation functions
|
std::map<std::string, int> _alloc; // allocation functions
|
||||||
|
|
|
@ -731,11 +731,11 @@ Checking test.c...
|
||||||
</def></programlisting>
|
</def></programlisting>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<title>Leaks</title>
|
<title><alloc> and <dealloc></title>
|
||||||
|
|
||||||
<para>Allocation and deallocation is defined with
|
<para>Allocation and deallocation is defined within
|
||||||
<literal><memory></literal> and
|
<literal><memory></literal> or <literal><resource> using
|
||||||
<literal><resource>.</literal></para>
|
<alloc> and <dealloc>.</literal></para>
|
||||||
|
|
||||||
<para>Here is example code:</para>
|
<para>Here is example code:</para>
|
||||||
|
|
||||||
|
@ -760,8 +760,8 @@ Checking test.c...</programlisting>
|
||||||
<programlisting><?xml version="1.0"?>
|
<programlisting><?xml version="1.0"?>
|
||||||
<def>
|
<def>
|
||||||
<memory>
|
<memory>
|
||||||
<dealloc>free_something</dealloc>
|
|
||||||
<alloc>alloc_something</alloc>
|
<alloc>alloc_something</alloc>
|
||||||
|
<dealloc>free_something</dealloc>
|
||||||
</memory>
|
</memory>
|
||||||
</def></programlisting>
|
</def></programlisting>
|
||||||
|
|
||||||
|
@ -771,7 +771,27 @@ Checking test.c...</programlisting>
|
||||||
Checking test.c...
|
Checking test.c...
|
||||||
[test.c:10]: (error) Memory leak: p</programlisting>
|
[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><ignore> and <use></title>
|
||||||
|
|
||||||
|
<para>The <ignore> and <use> tells Cppcheck how functions
|
||||||
|
uses allocated memory. Example code:</para>
|
||||||
|
|
||||||
<programlisting>void f()
|
<programlisting>void f()
|
||||||
{
|
{
|
||||||
|
@ -786,8 +806,8 @@ Checking test.c...
|
||||||
<programlisting><?xml version="1.0"?>
|
<programlisting><?xml version="1.0"?>
|
||||||
<def>
|
<def>
|
||||||
<memory>
|
<memory>
|
||||||
<dealloc>free_something</dealloc>
|
|
||||||
<alloc>alloc_something</alloc>
|
<alloc>alloc_something</alloc>
|
||||||
|
<dealloc>free_something</dealloc>
|
||||||
</memory>
|
</memory>
|
||||||
<ignore>do_something</ignore>
|
<ignore>do_something</ignore>
|
||||||
</def></programlisting>
|
</def></programlisting>
|
||||||
|
@ -804,14 +824,50 @@ Checking test.c...
|
||||||
<para><programlisting><?xml version="1.0"?>
|
<para><programlisting><?xml version="1.0"?>
|
||||||
<def>
|
<def>
|
||||||
<memory>
|
<memory>
|
||||||
<dealloc>free_something</dealloc>
|
|
||||||
<alloc>alloc_something</alloc>
|
<alloc>alloc_something</alloc>
|
||||||
|
<dealloc>free_something</dealloc>
|
||||||
<use>do_something</use>
|
<use>do_something</use>
|
||||||
</memory>
|
</memory>
|
||||||
</def></programlisting>Running Cppcheck now:</para>
|
</def></programlisting>Running Cppcheck now:</para>
|
||||||
|
|
||||||
<programlisting># cppcheck --library=something.cfg test.c
|
<programlisting># cppcheck --library=something.cfg test.c
|
||||||
Checking test.c...</programlisting>
|
Checking test.c...</programlisting>
|
||||||
|
|
||||||
|
<para>Cppcheck will often assume that functions "use" allocated memory.
|
||||||
|
By using <ignore> you can make Cppcheck detect more errors. By
|
||||||
|
using <use>, no extra errors are detected but Cppcheck will not
|
||||||
|
need to assume.</para>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<title><alloc init="false"></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><?xml version="1.0"?>
|
||||||
|
<def>
|
||||||
|
<memory>
|
||||||
|
<alloc init="false">alloc_something</alloc>
|
||||||
|
<dealloc>free_something</dealloc>
|
||||||
|
</memory>
|
||||||
|
</def></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>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
|
|
Loading…
Reference in New Issue