When the first argument was (void *)(1), at the start of the second iteration, arg was pointing to the "1", which caused problems for nextArgument(), which saw the ")" as the next token and returned nullptr, signalling that there are no more arguments. Instead, save the first token in the argument, which makes nextArgument() do the right thing.
2204 lines
70 KiB
C++
2204 lines
70 KiB
C++
/*
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
|
* Copyright (C) 2007-2020 Cppcheck team.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "checkleakautovar.h"
|
|
#include "library.h"
|
|
#include "settings.h"
|
|
#include "testsuite.h"
|
|
#include "tokenize.h"
|
|
|
|
#include <simplecpp.h>
|
|
#include <tinyxml2.h>
|
|
#include <vector>
|
|
|
|
class TestLeakAutoVar : public TestFixture {
|
|
public:
|
|
TestLeakAutoVar() : TestFixture("TestLeakAutoVar") {
|
|
}
|
|
|
|
private:
|
|
Settings settings;
|
|
|
|
void run() OVERRIDE {
|
|
int id = 0;
|
|
while (!Library::ismemory(++id));
|
|
settings.library.setalloc("malloc", id, -1);
|
|
settings.library.setrealloc("realloc", id, -1);
|
|
settings.library.setdealloc("free", id, 1);
|
|
while (!Library::isresource(++id));
|
|
settings.library.setalloc("fopen", id, -1);
|
|
settings.library.setrealloc("freopen", id, -1, 3);
|
|
settings.library.setdealloc("fclose", id, 1);
|
|
settings.library.smartPointers.insert("std::shared_ptr");
|
|
settings.library.smartPointers.insert("std::unique_ptr");
|
|
|
|
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
|
"<def>\n"
|
|
" <podtype name=\"uint8_t\" sign=\"u\" size=\"1\"/>\n"
|
|
"</def>";
|
|
tinyxml2::XMLDocument doc;
|
|
doc.Parse(xmldata, sizeof(xmldata));
|
|
settings.library.load(doc);
|
|
|
|
// Assign
|
|
TEST_CASE(assign1);
|
|
TEST_CASE(assign2);
|
|
TEST_CASE(assign3);
|
|
TEST_CASE(assign4);
|
|
TEST_CASE(assign5);
|
|
TEST_CASE(assign6);
|
|
TEST_CASE(assign7);
|
|
TEST_CASE(assign8);
|
|
TEST_CASE(assign9);
|
|
TEST_CASE(assign10);
|
|
TEST_CASE(assign11); // #3942: x = a(b(p));
|
|
TEST_CASE(assign12); // #4236: FP. bar(&x);
|
|
// TODO TEST_CASE(assign13); // #4237: FP. char*&ref=p; p=malloc(10); free(ref);
|
|
TEST_CASE(assign14);
|
|
TEST_CASE(assign15);
|
|
TEST_CASE(assign16);
|
|
TEST_CASE(assign17); // #9047
|
|
TEST_CASE(assign18);
|
|
TEST_CASE(assign19);
|
|
TEST_CASE(assign20); // #9187
|
|
|
|
TEST_CASE(isAutoDealloc);
|
|
|
|
TEST_CASE(realloc1);
|
|
TEST_CASE(realloc2);
|
|
TEST_CASE(realloc3);
|
|
TEST_CASE(freopen1);
|
|
TEST_CASE(freopen2);
|
|
|
|
TEST_CASE(deallocuse1);
|
|
TEST_CASE(deallocuse2);
|
|
TEST_CASE(deallocuse3);
|
|
TEST_CASE(deallocuse4);
|
|
// TODO TEST_CASE(deallocuse5); // #4018: FP. free(p), p = 0;
|
|
TEST_CASE(deallocuse6); // #4034: FP. x = p = f();
|
|
TEST_CASE(deallocuse7); // #6467, #6469, #6473
|
|
TEST_CASE(deallocuse8); // #1765
|
|
|
|
TEST_CASE(doublefree1);
|
|
TEST_CASE(doublefree2);
|
|
TEST_CASE(doublefree3); // #4914
|
|
TEST_CASE(doublefree4); // #5451 - FP when exit is called
|
|
TEST_CASE(doublefree5); // #5522
|
|
TEST_CASE(doublefree6); // #7685
|
|
TEST_CASE(doublefree7);
|
|
TEST_CASE(doublefree8);
|
|
TEST_CASE(doublefree9);
|
|
TEST_CASE(doublefree10); // #8706
|
|
|
|
// exit
|
|
TEST_CASE(exit1);
|
|
TEST_CASE(exit2);
|
|
TEST_CASE(exit3);
|
|
|
|
// handling function calls
|
|
TEST_CASE(functioncall1);
|
|
|
|
// goto
|
|
TEST_CASE(goto1);
|
|
TEST_CASE(goto2);
|
|
|
|
// if/else
|
|
TEST_CASE(ifelse1);
|
|
TEST_CASE(ifelse2);
|
|
TEST_CASE(ifelse3);
|
|
TEST_CASE(ifelse4);
|
|
TEST_CASE(ifelse5);
|
|
TEST_CASE(ifelse6); // #3370
|
|
TEST_CASE(ifelse7); // #5576 - if (fd < 0)
|
|
TEST_CASE(ifelse8); // #5747 - if (fd == -1)
|
|
TEST_CASE(ifelse9); // #5273 - if (X(p==NULL, 0))
|
|
TEST_CASE(ifelse10); // #8794 - if (!(x!=NULL))
|
|
TEST_CASE(ifelse11); // #8365 - if (NULL == (p = malloc(4)))
|
|
TEST_CASE(ifelse12); // #8340 - if ((*p = malloc(4)) == NULL)
|
|
TEST_CASE(ifelse13); // #8392
|
|
TEST_CASE(ifelse14); // #9130 - if (x == (char*)NULL)
|
|
TEST_CASE(ifelse15); // #9206 - if (global_ptr = malloc(1))
|
|
|
|
// switch
|
|
TEST_CASE(switch1);
|
|
|
|
// loops
|
|
TEST_CASE(loop1);
|
|
|
|
// mismatching allocation/deallocation
|
|
TEST_CASE(mismatchAllocDealloc);
|
|
|
|
TEST_CASE(smartPointerDeleter);
|
|
TEST_CASE(smartPointerRelease);
|
|
|
|
// Execution reaches a 'return'
|
|
TEST_CASE(return1);
|
|
TEST_CASE(return2);
|
|
TEST_CASE(return3);
|
|
TEST_CASE(return4);
|
|
TEST_CASE(return5);
|
|
TEST_CASE(return6); // #8282 return {p, p}
|
|
TEST_CASE(return7); // #9343 return (uint8_t*)x
|
|
TEST_CASE(return8);
|
|
|
|
// General tests: variable type, allocation type, etc
|
|
TEST_CASE(test1);
|
|
TEST_CASE(test2);
|
|
TEST_CASE(test3); // #3954 - reference pointer
|
|
TEST_CASE(test4); // #5923 - static pointer
|
|
TEST_CASE(test5); // unknown type
|
|
|
|
// Execution reaches a 'throw'
|
|
TEST_CASE(throw1);
|
|
TEST_CASE(throw2);
|
|
|
|
// Possible leak => Further configuration is needed for complete analysis
|
|
TEST_CASE(configuration1);
|
|
TEST_CASE(configuration2);
|
|
TEST_CASE(configuration3);
|
|
TEST_CASE(configuration4);
|
|
|
|
TEST_CASE(ptrptr);
|
|
|
|
TEST_CASE(nestedAllocation);
|
|
TEST_CASE(testKeywords); // #6767
|
|
|
|
TEST_CASE(inlineFunction); // #3989
|
|
|
|
TEST_CASE(smartPtrInContainer); // #8262
|
|
|
|
TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097
|
|
|
|
TEST_CASE(functionCallCastConfig); // #9652
|
|
}
|
|
|
|
void check(const char code[], bool cpp = false) {
|
|
// Clear the error buffer..
|
|
errout.str("");
|
|
|
|
// Tokenize..
|
|
Tokenizer tokenizer(&settings, this);
|
|
std::istringstream istr(code);
|
|
tokenizer.tokenize(istr, cpp?"test.cpp":"test.c");
|
|
|
|
// Check for leaks..
|
|
CheckLeakAutoVar c;
|
|
settings.checkLibrary = true;
|
|
settings.addEnabled("information");
|
|
c.runChecks(&tokenizer, &settings, this);
|
|
}
|
|
|
|
void check(const char code[], Settings & settings) {
|
|
// Clear the error buffer..
|
|
errout.str("");
|
|
|
|
// Tokenize..
|
|
Tokenizer tokenizer(&settings, this);
|
|
std::istringstream istr(code);
|
|
tokenizer.tokenize(istr, "test.cpp");
|
|
|
|
// Check for leaks..
|
|
CheckLeakAutoVar c;
|
|
settings.checkLibrary = true;
|
|
settings.addEnabled("information");
|
|
c.runChecks(&tokenizer, &settings, this);
|
|
}
|
|
|
|
void checkP(const char code[], bool cpp = false) {
|
|
// Clear the error buffer..
|
|
errout.str("");
|
|
|
|
// Raw tokens..
|
|
std::vector<std::string> files(1, cpp?"test.cpp":"test.c");
|
|
std::istringstream istr(code);
|
|
const simplecpp::TokenList tokens1(istr, files, files[0]);
|
|
|
|
// Preprocess..
|
|
simplecpp::TokenList tokens2(files);
|
|
std::map<std::string, simplecpp::TokenList*> filedata;
|
|
simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI());
|
|
|
|
// Tokenizer..
|
|
Tokenizer tokenizer(&settings, this);
|
|
tokenizer.createTokens(std::move(tokens2));
|
|
tokenizer.simplifyTokens1("");
|
|
|
|
// Check for leaks..
|
|
CheckLeakAutoVar c;
|
|
settings.checkLibrary = true;
|
|
settings.addEnabled("information");
|
|
c.runChecks(&tokenizer, &settings, this);
|
|
}
|
|
|
|
void assign1() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" p = NULL;\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void assign2() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" char *q = p;\n"
|
|
" free(q);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign3() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" char *q = p + 1;\n"
|
|
" free(q - 1);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign4() {
|
|
check("void f() {\n"
|
|
" char *a = malloc(10);\n"
|
|
" a += 10;\n"
|
|
" free(a - 10);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign5() {
|
|
check("void foo()\n"
|
|
"{\n"
|
|
" char *p = new char[100];\n"
|
|
" list += p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign6() { // #2806 - FP when there is redundant assignment
|
|
check("void foo() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" p = strcpy(p,q);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign7() {
|
|
check("void foo(struct str *d) {\n"
|
|
" struct str *p = malloc(10);\n"
|
|
" d->p = p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign8() { // linux list
|
|
check("void foo(struct str *d) {\n"
|
|
" struct str *p = malloc(10);\n"
|
|
" d->p = &p->x;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign9() {
|
|
check("void foo() {\n"
|
|
" char *p = x();\n"
|
|
" free(p);\n"
|
|
" p = NULL;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign10() {
|
|
check("void foo() {\n"
|
|
" char *p;\n"
|
|
" if (x) { p = malloc(10); }\n"
|
|
" if (!x) { p = NULL; }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign11() { // #3942 - FP for x = a(b(p));
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" x = a(b(p));\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function b() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
}
|
|
|
|
void assign12() { // #4236: FP. bar(&x)
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" free(p);\n"
|
|
" bar(&p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign13() { // #4237: FP. char *&ref=p; p=malloc(10); free(ref);
|
|
check("void f() {\n"
|
|
" char *p;\n"
|
|
" char * &ref = p;\n"
|
|
" p = malloc(10);\n"
|
|
" free(ref);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign14() {
|
|
check("void f(int x) {\n"
|
|
" char *p;\n"
|
|
" if (x && (p = malloc(10))) { }"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f(int x) {\n"
|
|
" char *p;\n"
|
|
" if (x && (p = new char[10])) { }"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void assign15() {
|
|
// #8120
|
|
check("void f() {\n"
|
|
" baz *p;\n"
|
|
" p = malloc(sizeof *p);\n"
|
|
" free(p);\n"
|
|
" p = malloc(sizeof *p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign16() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" free(p);\n"
|
|
" if (p=dostuff()) *p = 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign17() { // #9047
|
|
check("void f() {\n"
|
|
" char *p = (char*)malloc(10);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *p = (char*)(int*)malloc(10);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void assign18() {
|
|
check("void f(int x) {\n"
|
|
" char *p;\n"
|
|
" if (x && (p = (char*)malloc(10))) { }"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f(int x) {\n"
|
|
" char *p;\n"
|
|
" if (x && (p = (char*)(int*)malloc(10))) { }"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void assign19() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" free((void*)p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign20() { // #9187
|
|
check("void f() {\n"
|
|
" char *p = static_cast<int>(malloc(10));\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void isAutoDealloc() {
|
|
check("void f() {\n"
|
|
" char *p = new char[100];"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" Fred *fred = new Fred;"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" std::string *str = new std::string;"
|
|
"}", true);
|
|
TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: str\n", "", errout.str());
|
|
|
|
check("class TestType {\n" // #9028
|
|
"public:\n"
|
|
" char ca[12];\n"
|
|
"};\n"
|
|
"void f() {\n"
|
|
" TestType *tt = new TestType();\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: tt\n", errout.str());
|
|
}
|
|
|
|
void realloc1() {
|
|
check("void f() {\n"
|
|
" void *p = malloc(10);\n"
|
|
" void *q = realloc(p, 20);\n"
|
|
" free(q)\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void realloc2() {
|
|
check("void f() {\n"
|
|
" void *p = malloc(10);\n"
|
|
" void *q = realloc(p, 20);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str());
|
|
}
|
|
|
|
void realloc3() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" char *q = (char*) realloc(p, 20);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout.str());
|
|
}
|
|
|
|
void freopen1() {
|
|
check("void f() {\n"
|
|
" void *p = fopen(name,a);\n"
|
|
" void *q = freopen(name, b, p);\n"
|
|
" fclose(q)\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void freopen2() {
|
|
check("void f() {\n"
|
|
" void *p = fopen(name,a);\n"
|
|
" void *q = freopen(name, b, p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4]: (error) Resource leak: q\n", errout.str());
|
|
}
|
|
|
|
void deallocuse1() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" *p = 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str());
|
|
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" char c = *p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str());
|
|
}
|
|
|
|
void deallocuse2() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" strcpy(a, p);\n"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("error (free,use)", "[test.c:3]: (information) --check-library: Function strcpy() should have <noreturn> configuration\n", errout.str());
|
|
|
|
check("void f(char *p) {\n" // #3041 - assigning pointer when it's used
|
|
" free(p);\n"
|
|
" strcpy(a, p=b());\n"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("", "[test.c:3]: (information) --check-library: Function strcpy() should have <noreturn> configuration\n", errout.str());
|
|
}
|
|
|
|
void deallocuse3() {
|
|
check("void f(struct str *p) {\n"
|
|
" free(p);\n"
|
|
" p = p->next;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout.str());
|
|
}
|
|
|
|
void deallocuse4() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" return p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout.str());
|
|
|
|
check("void f(char *p) {\n"
|
|
" if (!p) free(p);\n"
|
|
" return p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(char *p) {\n"
|
|
" if (!p) delete p;\n"
|
|
" return p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(char *p) {\n"
|
|
" if (!p) delete [] p;\n"
|
|
" return p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(void* p) {\n"
|
|
" if (a) {\n"
|
|
" free(p);\n"
|
|
" return;\n"
|
|
" }\n"
|
|
" g(p);\n"
|
|
" return;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void deallocuse5() { // #4018
|
|
check("void f(char *p) {\n"
|
|
" free(p), p = 0;\n"
|
|
" *p = 0;\n" // <- Make sure pointer info is reset. It is NOT a freed pointer dereference
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void deallocuse6() { // #4034
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" x = p = foo();\n" // <- p is not dereferenced
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void deallocuse7() { // #6467, #6469, #6473, #6648
|
|
check("struct Foo { int* ptr; };\n"
|
|
"void f(Foo* foo) {\n"
|
|
" delete foo->ptr;\n"
|
|
" foo->ptr = new Foo; \n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("struct Foo { int* ptr; };\n"
|
|
"void f(Foo* foo) {\n"
|
|
" delete foo->ptr;\n"
|
|
" x = *foo->ptr; \n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout.str());
|
|
|
|
check("void parse() {\n"
|
|
" struct Buf {\n"
|
|
" Buf(uint32_t len) : m_buf(new uint8_t[len]) {}\n"
|
|
" ~Buf() { delete[]m_buf; }\n"
|
|
" uint8_t *m_buf;\n"
|
|
" };\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("struct Foo {\n"
|
|
" Foo();\n"
|
|
" Foo* ptr;\n"
|
|
" void func();\n"
|
|
"};\n"
|
|
"void bar(Foo* foo) {\n"
|
|
" delete foo->ptr;\n"
|
|
" foo->ptr = new Foo;\n"
|
|
" foo->ptr->func();\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void foo(void (*conv)(char**)) {\n"
|
|
" char * ptr=(char*)malloc(42);\n"
|
|
" free(ptr);\n"
|
|
" (*conv)(&ptr);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void deallocuse8() { // #1765
|
|
check("void f() {\n"
|
|
" int *ptr = new int;\n"
|
|
" delete(ptr);\n"
|
|
" *ptr = 0;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout.str());
|
|
}
|
|
|
|
void doublefree1() { // #3895
|
|
check("void f(char *p) {\n"
|
|
" if (x)\n"
|
|
" free(p);\n"
|
|
" else\n"
|
|
" p = 0;\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3] -> [test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" free(p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p, char *r) {\n"
|
|
" free(p);\n"
|
|
" free(r);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo() {\n"
|
|
" free(p);\n"
|
|
" free(r);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" if (x < 3) free(p);\n"
|
|
" else { if (x > 9) free(p); }\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" free(p);\n"
|
|
" getNext(&p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" free(p);\n"
|
|
" bar();\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" free(p);\n"
|
|
" printf(\"Freed memory at location %x\", p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(FILE *p) {\n"
|
|
" fclose(p);\n"
|
|
" fclose(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Resource handle 'p' freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(FILE *p, FILE *r) {\n"
|
|
" fclose(p);\n"
|
|
" fclose(r);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(FILE *p) {\n"
|
|
" if (x < 3) fclose(p);\n"
|
|
" else { if (x > 9) fclose(p); }\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(FILE *p) {\n"
|
|
" fclose(p);\n"
|
|
" gethandle(&p);\n"
|
|
" fclose(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(FILE *p) {\n"
|
|
" fclose(p);\n"
|
|
" gethandle();\n"
|
|
" fclose(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Resource handle 'p' freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(Data* p) {\n"
|
|
" free(p->a);\n"
|
|
" free(p->b);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void f() {\n"
|
|
" char *p; p = malloc(100);\n"
|
|
" if (x) {\n"
|
|
" free(p);\n"
|
|
" exit();\n"
|
|
" }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void f() {\n"
|
|
" char *p; p = malloc(100);\n"
|
|
" if (x) {\n"
|
|
" free(p);\n"
|
|
" x = 0;\n"
|
|
" }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4] -> [test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void f() {\n"
|
|
" char *p; p = do_something();\n"
|
|
" free(p);\n"
|
|
" p = do_something();\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" delete p;\n"
|
|
" delete p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p, char *r) {\n"
|
|
" delete p;\n"
|
|
" delete r;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(P p) {\n"
|
|
" delete p.x;\n"
|
|
" delete p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char **p) {\n"
|
|
" delete p[0];\n"
|
|
" delete p[1];\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" delete p;\n"
|
|
" getNext(&p);\n"
|
|
" delete p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" delete p;\n"
|
|
" bar();\n"
|
|
" delete p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" delete[] p;\n"
|
|
" delete[] p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p, char *r) {\n"
|
|
" delete[] p;\n"
|
|
" delete[] r;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" delete[] p;\n"
|
|
" getNext(&p);\n"
|
|
" delete[] p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(char *p) {\n"
|
|
" delete[] p;\n"
|
|
" bar();\n"
|
|
" delete[] p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"LineMarker::~LineMarker() {\n"
|
|
" delete pxpm;\n"
|
|
"}\n"
|
|
"LineMarker &LineMarker::operator=(const LineMarker &) {\n"
|
|
" delete pxpm;\n"
|
|
" pxpm = NULL;\n"
|
|
" return *this;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo()\n"
|
|
"{\n"
|
|
" int* ptr; ptr = NULL;\n"
|
|
" try\n"
|
|
" {\n"
|
|
" ptr = new int(4);\n"
|
|
" }\n"
|
|
" catch(...)\n"
|
|
" {\n"
|
|
" delete ptr;\n"
|
|
" throw;\n"
|
|
" }\n"
|
|
" delete ptr;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"int foo()\n"
|
|
"{\n"
|
|
" int* a; a = new int;\n"
|
|
" bool doDelete; doDelete = true;\n"
|
|
" if (a != 0)\n"
|
|
" {\n"
|
|
" doDelete = false;\n"
|
|
" delete a;\n"
|
|
" }\n"
|
|
" if(doDelete)\n"
|
|
" delete a;\n"
|
|
" return 0;\n"
|
|
"}", true);
|
|
TODO_ASSERT_EQUALS("", "[test.cpp:8] -> [test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void foo(int y)\n"
|
|
"{\n"
|
|
" char * x; x = NULL;\n"
|
|
" while(true) {\n"
|
|
" x = new char[100];\n"
|
|
" if (y++ > 100)\n"
|
|
" break;\n"
|
|
" delete[] x;\n"
|
|
" }\n"
|
|
" delete[] x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(int y)\n"
|
|
"{\n"
|
|
" char * x; x = NULL;\n"
|
|
" for (int i = 0; i < 10000; i++) {\n"
|
|
" x = new char[100];\n"
|
|
" delete[] x;\n"
|
|
" }\n"
|
|
" delete[] x;\n"
|
|
"}", true);
|
|
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
|
|
|
check(
|
|
"void foo(int y)\n"
|
|
"{\n"
|
|
" char * x; x = NULL;\n"
|
|
" while (isRunning()) {\n"
|
|
" x = new char[100];\n"
|
|
" delete[] x;\n"
|
|
" }\n"
|
|
" delete[] x;\n"
|
|
"}", true);
|
|
TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
|
|
|
check(
|
|
"void foo(int y)\n"
|
|
"{\n"
|
|
" char * x; x = NULL;\n"
|
|
" while (isRunning()) {\n"
|
|
" x = malloc(100);\n"
|
|
" free(x);\n"
|
|
" }\n"
|
|
" free(x);\n"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout.str());
|
|
|
|
check(
|
|
"void foo(int y)\n"
|
|
"{\n"
|
|
" char * x; x = NULL;\n"
|
|
" for (;;) {\n"
|
|
" x = new char[100];\n"
|
|
" if (y++ > 100)\n"
|
|
" break;\n"
|
|
" delete[] x;\n"
|
|
" }\n"
|
|
" delete[] x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void foo(int y)\n"
|
|
"{\n"
|
|
" char * x; x = NULL;\n"
|
|
" do {\n"
|
|
" x = new char[100];\n"
|
|
" if (y++ > 100)\n"
|
|
" break;\n"
|
|
" delete[] x;\n"
|
|
" } while (true);\n"
|
|
" delete[] x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void f()\n"
|
|
"{\n"
|
|
" char *p; p = 0;\n"
|
|
" if (x < 100) {\n"
|
|
" p = malloc(10);\n"
|
|
" free(p);\n"
|
|
" }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:6] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
|
|
check(
|
|
"void MyFunction()\n"
|
|
"{\n"
|
|
" char* data; data = new char[100];\n"
|
|
" try\n"
|
|
" {\n"
|
|
" }\n"
|
|
" catch(err)\n"
|
|
" {\n"
|
|
" delete[] data;\n"
|
|
" MyThrow(err);\n"
|
|
" }\n"
|
|
" delete[] data;\n"
|
|
"}\n"
|
|
|
|
"void MyThrow(err)\n"
|
|
"{\n"
|
|
" throw(err);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check(
|
|
"void MyFunction()\n"
|
|
"{\n"
|
|
" char* data; data = new char[100];\n"
|
|
" try\n"
|
|
" {\n"
|
|
" }\n"
|
|
" catch(err)\n"
|
|
" {\n"
|
|
" delete[] data;\n"
|
|
" MyExit(err);\n"
|
|
" }\n"
|
|
" delete[] data;\n"
|
|
"}\n"
|
|
|
|
"void MyExit(err)\n"
|
|
"{\n"
|
|
" exit(err);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check( // #6252
|
|
"struct Wrapper {\n"
|
|
" Thing* m_thing;\n"
|
|
" Wrapper() : m_thing(0) {\n"
|
|
" }\n"
|
|
" ~Wrapper() {\n"
|
|
" delete m_thing;\n"
|
|
" }\n"
|
|
" void changeThing() {\n"
|
|
" delete m_thing;\n"
|
|
" m_thing = new Thing;\n"
|
|
" }\n"
|
|
"};", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
// #7401
|
|
check("void pCodeLabelDestruct(pCode *pc) {\n"
|
|
" free(PCL(pc)->label);\n"
|
|
" free(pc);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree2() { // #3891
|
|
check("void *f(int a) {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (a == 2) { free(p); return ((void*)1); }\n"
|
|
" free(p);\n"
|
|
" return 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree3() { // #4914
|
|
check("void foo() {\n"
|
|
" bool done = false;\n"
|
|
" do {\n"
|
|
" char *bar = malloc(10)\n"
|
|
" if(condition()) {\n"
|
|
" free(bar);\n"
|
|
" continue;\n"
|
|
" }\n"
|
|
" done = true;\n"
|
|
" free(bar)\n"
|
|
" } while(!done);\n"
|
|
" return;"
|
|
"}"
|
|
);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree4() { // #5451 - exit
|
|
check("void f(char *p) {\n"
|
|
" if (x) {\n"
|
|
" free(p);\n"
|
|
" exit(1);\n"
|
|
" }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree5() { // #5522
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" x = (q == p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str());
|
|
}
|
|
|
|
void doublefree6() { // #7685
|
|
check("void do_wordexp(FILE *f) {\n"
|
|
" free(getword(f));\n"
|
|
" fclose(f);\n"
|
|
"}", /*cpp=*/false);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree7() {
|
|
check("void f(char *p, int x) {\n"
|
|
" free(p);\n"
|
|
" if (x && (p = malloc(10)))\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(char *p, int x) {\n"
|
|
" delete[] p;\n"
|
|
" if (x && (p = new char[10]))\n"
|
|
" delete[] p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree8() {
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::unique_ptr<int> x(i);\n"
|
|
" delete i;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" delete i;\n"
|
|
" std::unique_ptr<int> x(i);\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::unique_ptr<int> x{i};\n"
|
|
" delete i;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::shared_ptr<int> x(i);\n"
|
|
" delete i;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::shared_ptr<int> x{i};\n"
|
|
" delete i;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
|
|
|
|
// Check for use-after-free FP
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::shared_ptr<int> x{i};\n"
|
|
" *i = 123;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int[1];\n"
|
|
" std::unique_ptr<int[]> x(i);\n"
|
|
" delete i;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout.str());
|
|
}
|
|
|
|
void doublefree9() {
|
|
check("struct foo {\n"
|
|
" int* get(int) { return new int(); }\n"
|
|
"};\n"
|
|
"void f(foo* b) {\n"
|
|
" std::unique_ptr<int> x(b->get(0));\n"
|
|
" std::unique_ptr<int> y(b->get(1));\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void doublefree10() {
|
|
check("void f(char* s) {\n"
|
|
" char *p = malloc(strlen(s));\n"
|
|
" if (p != NULL) {\n"
|
|
" strcat(p, s);\n"
|
|
" if (strlen(s) != 10)\n"
|
|
" free(p); p = NULL;\n"
|
|
" }\n"
|
|
" if (p != NULL)\n"
|
|
" free(p);\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(char* s) {\n"
|
|
" char *p = malloc(strlen(s));\n"
|
|
" if (p != NULL) {\n"
|
|
" strcat(p, s);\n"
|
|
" if (strlen(s) != 10)\n"
|
|
" free(p), p = NULL;\n"
|
|
" }\n"
|
|
" if (p != NULL)\n"
|
|
" free(p);\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void exit1() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" exit(0);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void exit2() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" fatal_error();\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function fatal_error() should have <noreturn> configuration\n"
|
|
"[test.c:4]: (information) --check-library: Function fatal_error() should have <use>/<leak-ignore> configuration\n",
|
|
errout.str());
|
|
}
|
|
|
|
void exit3() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(100);\n"
|
|
" if (x) {\n"
|
|
" free(p);\n"
|
|
" ::exit(0);\n"
|
|
" }"
|
|
" free(p);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *p = malloc(100);\n"
|
|
" if (x) {\n"
|
|
" free(p);\n"
|
|
" std::exit(0);\n"
|
|
" }"
|
|
" free(p);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void functioncall1() {
|
|
check("void f(struct S *p) {\n"
|
|
" p->x = malloc(10);\n"
|
|
" free(p->x);\n"
|
|
" p->x = 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void goto1() {
|
|
check("static void f() {\n"
|
|
" int err = -ENOMEM;\n"
|
|
" char *reg = malloc(100);\n"
|
|
" if (err) {\n"
|
|
" free(reg);\n"
|
|
" }\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void goto2() { // #4231
|
|
check("static char * f() {\n"
|
|
"x:\n"
|
|
" char *p = malloc(100);\n"
|
|
" if (err) {\n"
|
|
" free(p);\n"
|
|
" goto x;\n"
|
|
" }\n"
|
|
" return p;\n" // no error since there is a goto
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse1() {
|
|
check("int f() {\n"
|
|
" char *p = NULL;\n"
|
|
" if (x) { p = malloc(10); }\n"
|
|
" else { return 0; }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse2() {
|
|
check("int f() {\n"
|
|
" char *p = NULL;\n"
|
|
" if (x) { p = malloc(10); }\n"
|
|
" else { return 0; }\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void ifelse3() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (!p) { return; }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("char * f(size_t size) {"
|
|
" void *p = malloc(1);"
|
|
" if (!p && size != 0)"
|
|
" return NULL;"
|
|
" return p;"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (p) { } else { return; }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
// #3866 - UNLIKELY
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (UNLIKELY(!p)) { return; }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse4() {
|
|
check("void f(int x) {\n"
|
|
" char *p;\n"
|
|
" if (x) { p = malloc(10); }\n"
|
|
" if (x) { free(p); }\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(int x) {\n"
|
|
" char *p;\n"
|
|
" if (x) { p = malloc(10); }\n"
|
|
" if (!x) { return; }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse5() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (!p && x) { p = malloc(10); }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse6() { // #3370
|
|
check("void f(int x) {\n"
|
|
" int *a = malloc(20);\n"
|
|
" if (x)\n"
|
|
" free(a);\n"
|
|
" else\n"
|
|
" a = 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:6]: (error) Memory leak: a\n", errout.str());
|
|
}
|
|
|
|
void ifelse7() { // #5576
|
|
check("void f() {\n"
|
|
" int x = malloc(20);\n"
|
|
" if (x < 0)\n" // assume negative value indicates its unallocated
|
|
" return;\n"
|
|
" free(x);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse8() { // #5747
|
|
check("void f() {\n"
|
|
" int fd = socket(AF_INET, SOCK_PACKET, 0 );\n"
|
|
" if (fd == -1)\n"
|
|
" return;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse9() { // #5273
|
|
check("void f() {\n"
|
|
" char *p = malloc(100);\n"
|
|
" if (dostuff(p==NULL,0))\n"
|
|
" return;\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse10() { // #8794
|
|
check("void f() {\n"
|
|
" void *x = malloc(1U);\n"
|
|
" if (!(x != NULL))\n"
|
|
" return;\n"
|
|
" free(x);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse11() { // #8365
|
|
check("void f() {\n"
|
|
" void *p;\n"
|
|
" if (NULL == (p = malloc(4)))\n"
|
|
" return;\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse12() { // #8340
|
|
check("void f(char **p) {\n"
|
|
" if ((*p = malloc(4)) == NULL)\n"
|
|
" return;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse13() { // #8392
|
|
check("int f(int fd, const char *mode) {\n"
|
|
" char *path;\n"
|
|
" if (fd == -1 || (path = (char *)malloc(10)) == NULL)\n"
|
|
" return 1;\n"
|
|
" free(path);\n"
|
|
" return 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("int f(int fd, const char *mode) {\n"
|
|
" char *path;\n"
|
|
" if ((path = (char *)malloc(10)) == NULL || fd == -1)\n"
|
|
" return 1;\n" // <- memory leak
|
|
" free(path);\n"
|
|
" return 0;\n"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("[test.cpp:4] memory leak", "", errout.str());
|
|
}
|
|
|
|
void ifelse14() { // #9130
|
|
check("char* f() {\n"
|
|
" char* buf = malloc(10);\n"
|
|
" if (buf == (char*)NULL)\n"
|
|
" return NULL;\n"
|
|
" return buf;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void ifelse15() { // #9206
|
|
check("struct SSS { int a; };\n"
|
|
"SSS* global_ptr;\n"
|
|
"void test_alloc() {\n"
|
|
" if ( global_ptr = new SSS()) {}\n"
|
|
" return;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("FILE* hFile;\n"
|
|
"int openFile( void ) {\n"
|
|
" if ((hFile = fopen(\"1.txt\", \"wb\" )) == NULL) return 0;\n"
|
|
" return 1;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void switch1() {
|
|
check("void f() {\n"
|
|
" char *p = 0;\n"
|
|
" switch (x) {\n"
|
|
" case 123: p = malloc(100); break;\n"
|
|
" default: return;\n"
|
|
" }\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void loop1() {
|
|
// test the handling of { }
|
|
check("void f() {\n"
|
|
" char *p;\n"
|
|
" for (i=0;i<5;i++) { }\n"
|
|
" if (x) { free(p) }\n"
|
|
" else { a = p; }\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void mismatchAllocDealloc() {
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" free(f);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" free((void*)f);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *cPtr = new char[100];\n"
|
|
" delete[] cPtr;\n"
|
|
" cPtr = new char[100]('x');\n"
|
|
" delete[] cPtr;\n"
|
|
" cPtr = new char[100];\n"
|
|
" delete cPtr;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *cPtr = new char[100];\n"
|
|
" free(cPtr);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: cPtr\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *cPtr = new (buf) char[100];\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int[1];\n"
|
|
" std::unique_ptr<int> x(i);\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::unique_ptr<int[]> x(i);\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" void* a = malloc(1);\n"
|
|
" void* b = freopen(f, p, a);\n"
|
|
" free(b);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: a\n"
|
|
"[test.c:3] -> [test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" void* a;\n"
|
|
" void* b = realloc(a, 10);\n"
|
|
" free(b);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" int * j = realloc(i, 2 * sizeof(int));\n"
|
|
" delete[] j;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n"
|
|
"[test.cpp:3] -> [test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout.str());
|
|
}
|
|
|
|
void smartPointerDeleter() {
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::unique_ptr<FILE> fp{f};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::unique_ptr<FILE, decltype(&fclose)> fp{f, &fclose};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::shared_ptr<FILE> fp{f, &fclose};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("struct deleter { void operator()(FILE* f) { fclose(f); }};\n"
|
|
"void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::unique_ptr<FILE, deleter> fp{f};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("int * create();\n"
|
|
"void destroy(int * x);\n"
|
|
"void f() {\n"
|
|
" int x * = create()\n"
|
|
" std::unique_ptr<int, decltype(&destroy)> xp{x, &destroy()};\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("int * create();\n"
|
|
"void destroy(int * x);\n"
|
|
"void f() {\n"
|
|
" int x * = create()\n"
|
|
" std::unique_ptr<int, decltype(&destroy)> xp(x, &destroy());\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::shared_ptr<FILE> fp{f, [](FILE* x) { fclose(x); }};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::shared_ptr<FILE> fp{f, +[](FILE* x) { fclose(x); }};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::shared_ptr<FILE> fp{f, [](FILE* x) { free(f); }};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" std::shared_ptr<FILE> fp{f, [](FILE* x) {}};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout.str());
|
|
|
|
check("class C;\n"
|
|
"void f() {\n"
|
|
" C* c = new C{};\n"
|
|
" std::shared_ptr<C> a{c, [](C*) {}};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("class C;\n"
|
|
"void f() {\n"
|
|
" C* c = new C{};\n"
|
|
" std::shared_ptr<C> a{c, [](C* x) { delete x; }};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
void smartPointerRelease() {
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::unique_ptr<int> x(i);\n"
|
|
" x.release();\n"
|
|
" delete i;\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" int * i = new int;\n"
|
|
" std::unique_ptr<int> x(i);\n"
|
|
" x.release();\n"
|
|
"}\n", true);
|
|
ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: i\n", errout.str());
|
|
}
|
|
|
|
void return1() {
|
|
check("int f() {\n"
|
|
" char *p = malloc(100);\n"
|
|
" return 123;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void return2() {
|
|
check("char *f() {\n"
|
|
" char *p = malloc(100);\n"
|
|
" return p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void return3() {
|
|
check("struct dev * f() {\n"
|
|
" struct ABC *abc = malloc(100);\n"
|
|
" return &abc->dev;\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void return4() { // ticket #3862
|
|
// avoid false positives
|
|
check("void f(char *p, int x) {\n"
|
|
" if (x==12) {\n"
|
|
" free(p);\n"
|
|
" throw 1;\n"
|
|
" }\n"
|
|
" free(p);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(char *p, int x) {\n"
|
|
" if (x==12) {\n"
|
|
" delete p;\n"
|
|
" throw 1;\n"
|
|
" }\n"
|
|
" delete p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f(char *p, int x) {\n"
|
|
" if (x==12) {\n"
|
|
" delete [] p;\n"
|
|
" throw 1;\n"
|
|
" }\n"
|
|
" delete [] p;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void return5() { // ticket #6397 - conditional allocation/deallocation and conditional return
|
|
// avoid false positives
|
|
check("void f(int *p, int x) {\n"
|
|
" if (x != 0) {\n"
|
|
" free(p);\n"
|
|
" }\n"
|
|
" if (x != 0) {\n"
|
|
" return;\n"
|
|
" }\n"
|
|
" *p = 0;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void return6() { // #8282
|
|
check("std::pair<char*, char*> f(size_t n) {\n"
|
|
" char* p = (char* )malloc(n);\n"
|
|
" return {p, p};\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void return7() { // #9343
|
|
check("uint8_t *f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (uint8_t *)x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("uint8_t f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (uint8_t)x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str());
|
|
|
|
check("void** f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (void**)x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (long long)x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (void*)(short)x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str());
|
|
|
|
check("void* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (mytype)x;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void* f() {\n" // Do not crash
|
|
" void *x = malloc(1);\n"
|
|
" return (mytype)y;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout.str());
|
|
}
|
|
|
|
void return8() {
|
|
check("void* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (x);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return ((x));\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return ((((x))));\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("char* f() {\n"
|
|
" void *x = malloc(1);\n"
|
|
" return (char*)(x);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void test1() { // 3809
|
|
check("void f(double*&p) {\n"
|
|
" p = malloc(0x100);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void test2() { // 3899
|
|
check("struct Fred {\n"
|
|
" char *p;\n"
|
|
" void f1() { free(p); }\n"
|
|
"};");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void test3() { // 3954 - reference pointer
|
|
check("void f() {\n"
|
|
" char *&p = x();\n"
|
|
" p = malloc(10);\n"
|
|
"};");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void test4() { // 5923 - static pointer
|
|
check("void f() {\n"
|
|
" static char *p;\n"
|
|
" if (!p) p = malloc(10);\n"
|
|
" if (x) { free(p); p = 0; }\n"
|
|
"};");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void test5() { // unknown type
|
|
check("void f() { Fred *p = malloc(10); }", true);
|
|
ASSERT_EQUALS("[test.cpp:1]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f() { Fred *p = malloc(10); }", false);
|
|
ASSERT_EQUALS("[test.c:1]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f() { Fred *p = new Fred; }", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() { Fred fred = malloc(10); }", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void throw1() { // 3987 - Execution reach a 'throw'
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" throw 123;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *p;\n"
|
|
" try {\n"
|
|
" p = malloc(10);\n"
|
|
" throw 123;\n"
|
|
" } catch (...) { }\n"
|
|
" free(p);\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void throw2() { // do not miss ::NS::Except()
|
|
check("namespace NS {\n"
|
|
" class Except {\n"
|
|
" };\n"
|
|
"}\n"
|
|
"void foo(int i)\n"
|
|
"{\n"
|
|
" int *pi = new int;\n"
|
|
" if (i == 42) {\n"
|
|
" delete pi;\n"
|
|
" throw ::NS::Except();\n"
|
|
" }\n"
|
|
" delete pi;\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void configuration1() {
|
|
// Possible leak => configuration is required for complete analysis
|
|
// The user should be able to "white list" and "black list" functions.
|
|
|
|
// possible leak. If the function 'x' deallocates the pointer or
|
|
// takes the address, there is no leak.
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" x(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have <noreturn> configuration\n"
|
|
"[test.c:4]: (information) --check-library: Function x() should have <use>/<leak-ignore> configuration\n",
|
|
errout.str());
|
|
}
|
|
|
|
void configuration2() {
|
|
// possible leak. If the function 'x' deallocates the pointer or
|
|
// takes the address, there is no leak.
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" x(&p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have <noreturn> configuration\n"
|
|
"[test.c:4]: (information) --check-library: Function x() should have <use>/<leak-ignore> configuration\n",
|
|
errout.str());
|
|
}
|
|
|
|
void configuration3() {
|
|
const char * code = "void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (set_data(p)) { }\n"
|
|
"}";
|
|
check(code);
|
|
ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
check(code, true);
|
|
ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
|
|
code = "void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (set_data(p)) { return; }\n"
|
|
"}";
|
|
check(code);
|
|
ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n"
|
|
"[test.c:4]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n"
|
|
, errout.str());
|
|
check(code, true);
|
|
ASSERT_EQUALS("[test.cpp:3]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n"
|
|
"[test.cpp:4]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n"
|
|
, errout.str());
|
|
}
|
|
|
|
void configuration4() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" int ret = set_data(p);\n"
|
|
" return ret;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
}
|
|
|
|
void ptrptr() {
|
|
check("void f() {\n"
|
|
" char **p = malloc(10);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void nestedAllocation() {
|
|
check("void QueueDSMCCPacket(unsigned char *data, int length) {\n"
|
|
" unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n"
|
|
" m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy));\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function DSMCCPacket() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
|
|
check("void QueueDSMCCPacket(unsigned char *data, int length) {\n"
|
|
" unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n"
|
|
" m_dsmccQueue.enqueue(new DSMCCPacket(somethingunrelated));\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: dataCopy\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *buf = new char[1000];\n"
|
|
" clist.push_back(new (std::nothrow) C(buf));\n"
|
|
"}", true);
|
|
ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function C() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
}
|
|
|
|
void testKeywords() {
|
|
check("int main(int argc, char **argv) {\n"
|
|
" double *new = malloc(1*sizeof(double));\n"
|
|
" free(new);\n"
|
|
" return 0;\n"
|
|
"}", false);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void inlineFunction() {
|
|
check("int test() {\n"
|
|
" char *c;\n"
|
|
" int ret() {\n"
|
|
" free(c);\n"
|
|
" return 0;\n"
|
|
" }\n"
|
|
" c = malloc(128);\n"
|
|
" return ret();\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
// #8262
|
|
void smartPtrInContainer() {
|
|
check("std::list< std::shared_ptr<int> > mList;\n"
|
|
"void test(){\n"
|
|
" int *pt = new int(1);\n"
|
|
" mList.push_back(std::shared_ptr<int>(pt));\n"
|
|
"}\n",
|
|
true
|
|
);
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void recursiveCountLimit() { // #5872 #6157 #9097
|
|
ASSERT_THROW(checkP("#define ONE else if (0) { }\n"
|
|
"#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n"
|
|
"#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n"
|
|
"#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n"
|
|
"void foo() {\n"
|
|
" if (0) { }\n"
|
|
" THOU THOU\n"
|
|
"}"), InternalError);
|
|
ASSERT_NO_THROW(checkP("#define ONE if (0) { }\n"
|
|
"#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n"
|
|
"#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n"
|
|
"#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n"
|
|
"void foo() {\n"
|
|
" if (0) { }\n"
|
|
" THOU THOU\n"
|
|
"}"));
|
|
}
|
|
|
|
void functionCallCastConfig() { // #9652
|
|
Settings settingsFunctionCall = settings;
|
|
|
|
const char xmldata[] = "<?xml version=\"1.0\"?>\n"
|
|
"<def format=\"2\">\n"
|
|
" <function name=\"free_func\">\n"
|
|
" <noreturn>false</noreturn>\n"
|
|
" <arg nr=\"1\">\n"
|
|
" <not-uninit/>\n"
|
|
" </arg>\n"
|
|
" <arg nr=\"2\">\n"
|
|
" <not-uninit/>\n"
|
|
" </arg>\n"
|
|
" </function>\n"
|
|
"</def>";
|
|
tinyxml2::XMLDocument doc;
|
|
doc.Parse(xmldata, sizeof(xmldata));
|
|
settingsFunctionCall.library.load(doc);
|
|
check("void test_func()\n"
|
|
"{\n"
|
|
" char * buf = malloc(4);\n"
|
|
" free_func((void *)(1), buf);\n"
|
|
"}", settingsFunctionCall);
|
|
ASSERT_EQUALS("[test.cpp:5]: (information) --check-library: Function free_func() should have <use>/<leak-ignore> configuration\n", errout.str());
|
|
}
|
|
};
|
|
|
|
REGISTER_TEST(TestLeakAutoVar)
|
|
|
|
|
|
class TestLeakAutoVarStrcpy: public TestFixture {
|
|
public:
|
|
TestLeakAutoVarStrcpy() : TestFixture("TestLeakAutoVarStrcpy") {
|
|
}
|
|
|
|
private:
|
|
Settings settings;
|
|
|
|
void check(const char code[]) {
|
|
// Clear the error buffer..
|
|
errout.str("");
|
|
|
|
// Tokenize..
|
|
Tokenizer tokenizer(&settings, this);
|
|
std::istringstream istr(code);
|
|
tokenizer.tokenize(istr, "test.cpp");
|
|
|
|
// Check for leaks..
|
|
CheckLeakAutoVar checkLeak;
|
|
settings.checkLibrary = true;
|
|
settings.addEnabled("information");
|
|
checkLeak.runChecks(&tokenizer, &settings, this);
|
|
}
|
|
|
|
void run() OVERRIDE {
|
|
LOAD_LIB_2(settings.library, "std.cfg");
|
|
|
|
TEST_CASE(returnedValue); // #9298
|
|
TEST_CASE(fclose_false_positive); // #9575
|
|
}
|
|
|
|
void returnedValue() { // #9298
|
|
check("char *m;\n"
|
|
"void strcpy_returnedvalue(const char* str)\n"
|
|
"{\n"
|
|
" char* ptr = new char[strlen(str)+1];\n"
|
|
" m = strcpy(ptr, str);\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void fclose_false_positive() { // #9575
|
|
check("int f(FILE *fp) { return fclose(fp); }");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
};
|
|
|
|
REGISTER_TEST(TestLeakAutoVarStrcpy)
|
|
|
|
|
|
class TestLeakAutoVarWindows : public TestFixture {
|
|
public:
|
|
TestLeakAutoVarWindows() : TestFixture("TestLeakAutoVarWindows") {
|
|
}
|
|
|
|
private:
|
|
Settings settings;
|
|
|
|
void check(const char code[]) {
|
|
// Clear the error buffer..
|
|
errout.str("");
|
|
|
|
// Tokenize..
|
|
Tokenizer tokenizer(&settings, this);
|
|
std::istringstream istr(code);
|
|
tokenizer.tokenize(istr, "test.c");
|
|
|
|
// Check for leaks..
|
|
CheckLeakAutoVar checkLeak;
|
|
checkLeak.runChecks(&tokenizer, &settings, this);
|
|
}
|
|
|
|
void run() OVERRIDE {
|
|
LOAD_LIB_2(settings.library, "windows.cfg");
|
|
|
|
TEST_CASE(heapDoubleFree);
|
|
}
|
|
|
|
void heapDoubleFree() {
|
|
check("void f() {"
|
|
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
|
|
" int *a = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" int *b = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" HeapFree(MyHeap, 0, a);"
|
|
" HeapFree(MyHeap, 0, b);"
|
|
" HeapDestroy(MyHeap);"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {"
|
|
" int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));"
|
|
" int *b = HeapAlloc(GetProcessHeap(), 0, sizeof(int));"
|
|
" HeapFree(GetProcessHeap(), 0, a);"
|
|
" HeapFree(GetProcessHeap(), 0, b);"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
|
|
check("void f() {"
|
|
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
|
|
" int *a = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" int *b = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" HeapFree(MyHeap, 0, a);"
|
|
" HeapDestroy(MyHeap);"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout.str());
|
|
|
|
check("void f() {"
|
|
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
|
|
" int *a = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" int *b = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" HeapFree(MyHeap, 0, a);"
|
|
" HeapFree(MyHeap, 0, b);"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("[test.c:1] (error) Resource leak: MyHeap",
|
|
"", errout.str());
|
|
|
|
check("void f() {"
|
|
" HANDLE MyHeap = HeapCreate(0, 0, 0);"
|
|
" int *a = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" int *b = HeapAlloc(MyHeap, 0, sizeof(int));"
|
|
" HeapFree(MyHeap, 0, a);"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("[test.c:1] (error) Memory leak: MyHeap\n"
|
|
"[test.c:1] (error) Memory leak: b",
|
|
"[test.c:1]: (error) Memory leak: b\n", errout.str());
|
|
}
|
|
};
|
|
|
|
REGISTER_TEST(TestLeakAutoVarWindows)
|