/* * Cppcheck - A tool for static C/C++ code analysis * Copyright (C) 2007-2022 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 . */ #include "checkleakautovar.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "testsuite.h" #include "tokenize.h" #include #include #include #include #include #include #include class TestLeakAutoVarStrcpy; class TestLeakAutoVarWindows; 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("socket", id, -1); settings.library.setdealloc("close", 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["std::shared_ptr"]; settings.library.smartPointers["std::unique_ptr"]; settings.library.smartPointers["std::unique_ptr"].unique = true; const char xmldata[] = "\n" "\n" " \n" ""; 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); 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(assign21); // #10186 TEST_CASE(assign22); // #9139 TEST_CASE(assign23); TEST_CASE(assign24); // #7440 TEST_CASE(isAutoDealloc); TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); TEST_CASE(realloc4); TEST_CASE(realloc5); // #9292, #9990 TEST_CASE(freopen1); TEST_CASE(freopen2); TEST_CASE(deallocuse1); TEST_CASE(deallocuse2); TEST_CASE(deallocuse3); TEST_CASE(deallocuse4); 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(deallocuse9); // #9781 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 TEST_CASE(doublefree11); TEST_CASE(doublefree12); // #10502 TEST_CASE(doublefree13); // #11008 // 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)) TEST_CASE(ifelse16); // #9635 - if (p = malloc(4), p == NULL) TEST_CASE(ifelse17); // if (!!(!p)) TEST_CASE(ifelse18); TEST_CASE(ifelse19); TEST_CASE(ifelse20); // #10182 TEST_CASE(ifelse21); TEST_CASE(ifelse22); // #10187 TEST_CASE(ifelse23); // #5473 TEST_CASE(ifelse24); // #1733 TEST_CASE(ifelse25); // #9966 TEST_CASE(ifelse26); TEST_CASE(ifelse27); // 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); TEST_CASE(return9); // 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(configuration5); TEST_CASE(ptrptr); TEST_CASE(nestedAllocation); TEST_CASE(testKeywords); // #6767 TEST_CASE(inlineFunction); // #3989 TEST_CASE(smartPtrInContainer); // #8262 TEST_CASE(functionCallCastConfig); // #9652 TEST_CASE(functionCallLeakIgnoreConfig); // #7923 } #define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) void check_(const char* file, int line, const char code[], bool cpp = false) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, cpp ? "test.cpp" : "test.c"), file, line); // Check for leaks.. CheckLeakAutoVar c; settings.checkLibrary = true; settings.severity.enable(Severity::information); c.runChecks(&tokenizer, &settings, this); } void check_(const char* file, int line, const char code[], Settings & settings_) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings_, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for leaks.. CheckLeakAutoVar c; settings_.checkLibrary = true; settings_.severity.enable(Severity::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 / 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" "}"); TODO_ASSERT_EQUALS("", "[test.c:6]: (error) Memory leak: p\n", 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(malloc(10));\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void assign21() { // #10186 check("void f(int **x) {\n" " void *p = malloc(10);\n" " *x = (int*)p;\n" "}", true); ASSERT_EQUALS("", errout.str()); } void assign22() { // #9139 check("void f(char tempFileName[256]) {\n" " const int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Resource leak: fd\n", errout.str()); check("void f() {\n" " const void * const p = malloc(10);\n" "}", true); ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout.str()); } void assign23() { Settings s = settings; LOAD_LIB_2(settings.library, "posix.cfg"); check("void f() {\n" " int n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14;\n" " *&n1 = open(\"xx.log\", O_RDONLY);\n" " *&(n2) = open(\"xx.log\", O_RDONLY);\n" " *(&n3) = open(\"xx.log\", O_RDONLY);\n" " *&*&n4 = open(\"xx.log\", O_RDONLY);\n" " *&*&*&(n5) = open(\"xx.log\", O_RDONLY);\n" " *&*&(*&n6) = open(\"xx.log\", O_RDONLY);\n" " *&*(&*&n7) = open(\"xx.log\", O_RDONLY);\n" " *(&*&n8) = open(\"xx.log\", O_RDONLY);\n" " *&(*&*&(*&n9)) = open(\"xx.log\", O_RDONLY);\n" " (n10) = open(\"xx.log\", O_RDONLY);\n" " ((n11)) = open(\"xx.log\", O_RDONLY);\n" " ((*&n12)) = open(\"xx.log\", O_RDONLY);\n" " *(&(*&n13)) = open(\"xx.log\", O_RDONLY);\n" " ((*&(*&n14))) = open(\"xx.log\", O_RDONLY);\n" "}\n", true); ASSERT_EQUALS("[test.cpp:17]: (error) Resource leak: n1\n" "[test.cpp:17]: (error) Resource leak: n2\n" "[test.cpp:17]: (error) Resource leak: n3\n" "[test.cpp:17]: (error) Resource leak: n4\n" "[test.cpp:17]: (error) Resource leak: n5\n" "[test.cpp:17]: (error) Resource leak: n6\n" "[test.cpp:17]: (error) Resource leak: n7\n" "[test.cpp:17]: (error) Resource leak: n8\n" "[test.cpp:17]: (error) Resource leak: n9\n" "[test.cpp:17]: (error) Resource leak: n10\n" "[test.cpp:17]: (error) Resource leak: n11\n" "[test.cpp:17]: (error) Resource leak: n12\n" "[test.cpp:17]: (error) Resource leak: n13\n" "[test.cpp:17]: (error) Resource leak: n14\n", errout.str()); settings = s; } void assign24() { // #7440 check("void f() {\n" " char* data = new char[100];\n" " char** dataPtr = &data;\n" " delete[] *dataPtr;\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " char* data = new char[100];\n" " char** dataPtr = &data;\n" " printf(\"test\");\n" " delete[] *dataPtr;\n" "}\n", true); ASSERT_EQUALS("", 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()); check("void f(Bar& b) {\n" // #7622 " char* data = new char[10];\n" " b = Bar(*new Foo(data));\n" "}", /*cpp*/ true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function Foo() should have / configuration\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 realloc4() { check("void f(void *p) {\n" " void * q = realloc(p, 10);\n" " if (q == NULL)\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:5]: (error) Memory leak: q\n", errout.str()); } void realloc5() { // #9292 check("void * f(void * ptr, size_t size) {\n" " void *datap = realloc(ptr, size);\n" " if (size && !datap)\n" " free(ptr);\n" " return datap;\n" "}"); ASSERT_EQUALS("", errout.str()); // #9990 check("void f() {\n" " void * p1 = malloc(10);\n" " if (!p1)\n" " return;\n" " void * p2 = realloc(p1, 42);\n" " if (!p2) {\n" " free(p1);\n" " return;\n" " }\n" " free(p2);\n" "}"); ASSERT_EQUALS("", 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 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 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); TODO_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 deallocuse9() { check("void f(Type* p) {\n" // #9781 " std::shared_ptr sp(p);\n" " bool b = p->foo();\n" " return b;\n" "}\n", /*cpp*/ true); ASSERT_EQUALS("", errout.str()); check("struct A {\n" // #8635 " std::vector> array_;\n" " A* foo() {\n" " A* a = new A();\n" " array_.push_back(std::unique_ptr(a));\n" " return a;\n" " }\n" "};\n", /*cpp*/ true); ASSERT_EQUALS("", errout.str()); check("int g(int *p) {\n" // #9838 " std::unique_ptr temp(p);\n" " return DoSomething(p);\n" "}\n" "int f() {\n" " return g(new int(3));\n" "}\n", /*cpp*/ true); ASSERT_EQUALS("", 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 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 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 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 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 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 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 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 x(b->get(0));\n" " std::unique_ptr 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 doublefree11() { check("void f() {\n" " void * p = malloc(5);\n" " void * q = realloc(p, 10);\n" " if (q == NULL) {\n" " free(p);\n" " return;\n" " }\n" " free(p);\n" " if (q == NULL)\n" " return;\n" " free(q)\n" "}"); ASSERT_EQUALS("[test.c:3] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout.str()); } void doublefree12() { // #10502 check("int f(FILE *fp, const bool b) {\n" " if (b)\n" " return fclose(fp);\n" " fclose(fp);\n" " return 0;\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void doublefree13() { // #11008 check("struct buf_t { void* ptr; };\n" "void f() {\n" " struct buf_t buf;\n" " if ((buf.ptr = malloc(10)) == NULL)\n" " return;\n" " free(buf.ptr);\n" " if ((buf.ptr = malloc(10)) == NULL)\n" " return;\n" " free(buf.ptr);\n" "}\n"); 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 configuration\n" "[test.c:4]: (information) --check-library: Function fatal_error() should have / 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("int f() {\n" " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" " if (fd == -1)\n" " return -1;\n" " return fd;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f() {\n" " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" " if (fd != -1)\n" " return fd;\n" " return -1;\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 ifelse16() { // #9635 check("void f(void) {\n" " char *p;\n" " if(p = malloc(4), p == NULL)\n" " return;\n" " free(p);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); check("void f(void) {\n" " char *p, q;\n" " if(p = malloc(4), q = 1, p == NULL)\n" " return;\n" " free(p);\n" " return;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse17() { check("int *f() {\n" " int *p = realloc(nullptr, 10);\n" " if (!p)\n" " return NULL;\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int *f() {\n" " int *p = realloc(nullptr, 10);\n" " if (!!(!p))\n" " return NULL;\n" " return p;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse18() { check("void f() {\n" " void * p = malloc(10);\n" " void * q = realloc(p, 20);\n" " if (q == 0)\n" " return;\n" " free(q);\n" "}"); ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout.str()); check("void f() {\n" " void * p = malloc(10);\n" " void * q = realloc(p, 20);\n" " if (q != 0) {\n" " free(q);\n" " return;\n" " } else\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:8]: (error) Memory leak: p\n", errout.str()); } void ifelse19() { check("void f() {\n" " static char * a;\n" " char * b = realloc(a, 10);\n" " if (!b)\n" " return;\n" " a = b;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse20() { check("void f() {\n" " if (x > 0)\n" " void * p1 = malloc(5);\n" " else\n" " void * p2 = malloc(2);\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n" "[test.c:5]: (error) Memory leak: p2\n", errout.str()); check("void f() {\n" " if (x > 0)\n" " void * p1 = malloc(5);\n" " else\n" " void * p2 = malloc(2);\n" "}"); ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n" "[test.c:5]: (error) Memory leak: p2\n", errout.str()); } void ifelse21() { check("void f() {\n" " if (y) {\n" " void * p;\n" " if (x > 0)\n" " p = malloc(5);\n" " }\n" " return;\n" "}"); ASSERT_EQUALS("[test.c:6]: (error) Memory leak: p\n", errout.str()); } void ifelse22() { // #10187 check("int f(const char * pathname, int flags) {\n" " int fd = socket(pathname, flags);\n" " if (fd >= 0)\n" " return fd;\n" " return -1;\n" "}"); ASSERT_EQUALS("", errout.str()); check("int f(const char * pathname, int flags) {\n" " int fd = socket(pathname, flags);\n" " if (fd <= -1)\n" " return -1;\n" " return fd;\n" "}"); ASSERT_EQUALS("", errout.str()); } void ifelse23() { // #5473 check("void f() {\n" " if (FILE* fp = fopen(\"x\", \"r\")) {}\n" "}\n"); ASSERT_EQUALS("[test.c:2]: (error) Resource leak: fp\n", errout.str()); } void ifelse24() { // #1733 Settings s; LOAD_LIB_2(s.library, "std.cfg"); LOAD_LIB_2(s.library, "posix.cfg"); check("void f() {\n" " char* temp = strdup(\"temp.txt\");\n" " FILE* fp;\n" " if (NULL == x || NULL == (fp = fopen(temp, \"rt\")))\n" " return;\n" "}\n", s); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: temp\n" "[test.cpp:6]: (error) Memory leak: temp\n" "[test.cpp:6]: (error) Resource leak: fp\n", errout.str()); check("FILE* f() {\n" " char* temp = strdup(\"temp.txt\");\n" " FILE* fp = fopen(temp, \"rt\");\n" " return fp;\n" "}\n", s); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: temp\n", errout.str()); check("FILE* f() {\n" " char* temp = strdup(\"temp.txt\");\n" " FILE* fp = NULL;\n" " fopen_s(&fp, temp, \"rt\");\n" " return fp;\n" "}\n", s); ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: temp\n", errout.str()); check("void f() {\n" " char* temp = strdup(\"temp.txt\");\n" " FILE* fp = fopen(\"a.txt\", \"rb\");\n" " if (fp)\n" " freopen(temp, \"rt\", fp);\n" "}\n", s); ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: temp\n" "[test.cpp:6]: (error) Resource leak: fp\n", errout.str()); check("FILE* f() {\n" " char* temp = strdup(\"temp.txt\");\n" " return fopen(temp, \"rt\");\n" "}\n", s); TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: temp\n", "", errout.str()); } void ifelse25() { // #9966 check("void f() {\n" " void *p, *p2;\n" " if((p2 = p = malloc(10)) == NULL)\n" " return;\n" " (void)p;\n" " free(p2);\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void ifelse26() { // don't crash check("union tidi {\n" " long long ti;\n" " unsigned int di[2];\n" "};\n" "void f(long long val) {\n" " if (val == ({ union tidi d = {.di = {0x0, 0x80000000}}; d.ti; })) {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); } void ifelse27() { check("struct key { void* p; };\n" "int f(struct key** handle) {\n" " struct key* key;\n" " if (!(key = calloc(1, sizeof(*key))))\n" " return 0;\n" " if (!(key->p = malloc(4))) {\n" " free(key);\n" " return 0;\n" " }\n" " *handle = key;\n" " return 1;\n" "}\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 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 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 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 fp{f, &fclose};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr 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 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 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 xp(x, &destroy());\n" "}\n", true); ASSERT_EQUALS("", errout.str()); check("void f() {\n" " FILE*f=fopen(fname,a);\n" " std::shared_ptr 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 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 fp{f, [](FILE* x) { free(f); }};\n" "}", true); TODO_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 fp{f, [](FILE* x) {}};\n" "}", true); TODO_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 a{c, [](C*) {}};\n" "}", true); ASSERT_EQUALS("", errout.str()); check("class C;\n" "void f() {\n" " C* c = new C{};\n" " std::shared_ptr 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 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 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 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 return9() { check("void* f() {\n" " void *x = malloc (sizeof (struct alloc));\n" " return x + sizeof (struct alloc);\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 configuration\n" "[test.c:4]: (information) --check-library: Function x() should have / 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 configuration\n" "[test.c:4]: (information) --check-library: Function x() should have / 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 / configuration\n", errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function set_data() should have / 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 / configuration\n" "[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n" , errout.str()); check(code, true); ASSERT_EQUALS("[test.cpp:3]: (information) --check-library: Function set_data() should have / configuration\n" "[test.cpp:4]: (information) --check-library: Function set_data() should have / 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 / configuration\n", errout.str()); } void configuration5() { check("void f() {\n" " int(i);\n" "}\n"); ASSERT_EQUALS("", 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 / 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 / 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 > mList;\n" "void test(){\n" " int *pt = new int(1);\n" " mList.push_back(std::shared_ptr(pt));\n" "}\n", true ); ASSERT_EQUALS("", errout.str()); } void functionCallCastConfig() { // #9652 Settings settingsFunctionCall = settings; const char xmldata[] = "\n" "\n" " \n" " false\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" ""; 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 / configuration\n", errout.str()); } void functionCallLeakIgnoreConfig() { // #7923 Settings settingsLeakIgnore = settings; const char xmldata[] = "\n" "\n" " \n" " \n" " false\n" " \n" " \n" "\n"; tinyxml2::XMLDocument doc; doc.Parse(xmldata, sizeof(xmldata)); settingsLeakIgnore.library.load(doc); check("void f() {\n" " double* a = new double[1024];\n" " SomeClass::someMethod(a);\n" "}\n", settingsLeakIgnore); ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: a\n", errout.str()); } }; REGISTER_TEST(TestLeakAutoVar) class TestLeakAutoVarRecursiveCountLimit : public TestFixture { public: TestLeakAutoVarRecursiveCountLimit() : TestFixture("TestLeakAutoVarRecursiveCountLimit") {} private: Settings settings; void checkP(const char code[], bool cpp = false) { // Clear the error buffer.. errout.str(""); // Raw tokens.. std::vector 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 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 run() override { LOAD_LIB_2(settings.library, "std.cfg"); TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097 } 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" "}")); } }; REGISTER_TEST(TestLeakAutoVarRecursiveCountLimit) class TestLeakAutoVarStrcpy : public TestFixture { public: TestLeakAutoVarStrcpy() : TestFixture("TestLeakAutoVarStrcpy") {} private: Settings settings; void check_(const char* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for leaks.. CheckLeakAutoVar checkLeak; settings.checkLibrary = true; settings.severity.enable(Severity::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* file, int line, const char code[]) { // Clear the error buffer.. errout.str(""); // Tokenize.. Tokenizer tokenizer(&settings, this); std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.c"), file, line); // 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)