494 lines
14 KiB
C++
494 lines
14 KiB
C++
/*
|
|
* Cppcheck - A tool for static C/C++ code analysis
|
|
* Copyright (C) 2007-2012 Daniel Marjamäki and 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 "tokenize.h"
|
|
#include "checkleakautovar.h"
|
|
#include "testsuite.h"
|
|
#include <sstream>
|
|
|
|
extern std::ostringstream errout;
|
|
|
|
class TestLeakAutoVar : public TestFixture {
|
|
public:
|
|
TestLeakAutoVar() : TestFixture("TestLeakAutoVar")
|
|
{ }
|
|
|
|
private:
|
|
|
|
void run() {
|
|
// 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(deallocuse1);
|
|
TEST_CASE(deallocuse2);
|
|
TEST_CASE(deallocuse3);
|
|
TEST_CASE(deallocuse4);
|
|
|
|
TEST_CASE(doublefree);
|
|
|
|
// exit
|
|
TEST_CASE(exit1);
|
|
TEST_CASE(exit2);
|
|
|
|
// goto
|
|
TEST_CASE(goto1);
|
|
|
|
// if/else
|
|
TEST_CASE(ifelse1);
|
|
TEST_CASE(ifelse2);
|
|
TEST_CASE(ifelse3);
|
|
TEST_CASE(ifelse4);
|
|
TEST_CASE(ifelse5);
|
|
TEST_CASE(ifelse6); // #3370
|
|
|
|
// switch
|
|
TEST_CASE(switch1);
|
|
|
|
// loops
|
|
TEST_CASE(loop1);
|
|
|
|
// mismatching allocation/deallocation
|
|
TEST_CASE(mismatch_fopen_free);
|
|
|
|
// Execution reaches a 'return'
|
|
TEST_CASE(return1);
|
|
TEST_CASE(return2);
|
|
TEST_CASE(return3);
|
|
TEST_CASE(return4);
|
|
|
|
// General tests: variable type, allocation type, etc
|
|
TEST_CASE(test1);
|
|
|
|
// Possible leak => Further configuration is needed for complete analysis
|
|
TEST_CASE(configuration1);
|
|
TEST_CASE(configuration2);
|
|
TEST_CASE(configuration3);
|
|
TEST_CASE(configuration4);
|
|
}
|
|
|
|
void check(const char code[]) {
|
|
// Clear the error buffer..
|
|
errout.str("");
|
|
|
|
// Tokenize..
|
|
Settings settings;
|
|
Tokenizer tokenizer(&settings, this);
|
|
std::istringstream istr(code);
|
|
tokenizer.tokenize(istr, "test.c");
|
|
tokenizer.simplifyTokenList();
|
|
|
|
// Check for leaks..
|
|
CheckLeakAutoVar c;
|
|
settings.experimental = true;
|
|
c.runSimplifiedChecks(&tokenizer, &settings, this);
|
|
}
|
|
|
|
void assign1() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" p = NULL;\n"
|
|
" free(p);\n"
|
|
"}\n");
|
|
ASSERT_EQUALS("[test.c:3]: (error) New memory leak: p\n", errout.str());
|
|
}
|
|
|
|
void assign2() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" char *q = p;\n"
|
|
" free(q);\n"
|
|
"}\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"
|
|
"}\n");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign4() {
|
|
check("void f() {\n"
|
|
" char *a = malloc(10);\n"
|
|
" a += 10;\n"
|
|
" free(a - 10);\n"
|
|
"}\n");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign5() {
|
|
check("void foo()\n"
|
|
"{\n"
|
|
" char *p = new char[100];\n"
|
|
" list += p;\n"
|
|
"}\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"
|
|
"}\n");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign7() {
|
|
check("void foo(struct str *d) {\n"
|
|
" struct str *p = malloc(10);\n"
|
|
" d->p = p;\n"
|
|
"}\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"
|
|
"}\n");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void assign9() {
|
|
check("void foo() {\n"
|
|
" char *p = x();\n"
|
|
" free(p);\n"
|
|
" p = NULL;\n"
|
|
"}\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"
|
|
"}\n");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void deallocuse1() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" *p = 0;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Using deallocated pointer p\n", errout.str());
|
|
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" char c = *p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Using deallocated pointer p\n", errout.str());
|
|
}
|
|
|
|
void deallocuse2() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" strcpy(a, p);\n"
|
|
"}");
|
|
TODO_ASSERT_EQUALS("error", "", errout.str());
|
|
|
|
check("void f(char *p) {\n" // #3041 - assigning pointer when it's used
|
|
" free(p);\n"
|
|
" strcpy(a, p=b());\n"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void deallocuse3() {
|
|
check("void f(struct str *p) {\n"
|
|
" free(p);\n"
|
|
" p = p->next;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Using deallocated pointer p\n", errout.str());
|
|
}
|
|
|
|
void deallocuse4() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" return p;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Returning/using deallocated pointer p\n", errout.str());
|
|
}
|
|
|
|
void doublefree() {
|
|
check("void f(char *p) {\n"
|
|
" free(p);\n"
|
|
" free(p);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) Double deallocation: p\n", 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:4]: (information) fatal_error configuration is needed to establish if there is a leak or not\n", 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 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) New 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("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (p) { } else { 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"
|
|
"}\n");
|
|
ASSERT_EQUALS("[test.c:6]: (error) New memory leak: a\n", 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"
|
|
"}\n");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void mismatch_fopen_free() {
|
|
check("void f() {\n"
|
|
" FILE*f=fopen(fname,a);\n"
|
|
" free(f);\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) New mismatching allocation and deallocation: f\n", errout.str());
|
|
}
|
|
|
|
void return1() {
|
|
check("int f() {\n"
|
|
" char *p = malloc(100);\n"
|
|
" return 123;\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (error) New 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"
|
|
"}");
|
|
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"
|
|
"}");
|
|
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"
|
|
"}");
|
|
ASSERT_EQUALS("", errout.str());
|
|
}
|
|
|
|
void test1() { // 3809
|
|
check("void f(double*&p) {\n"
|
|
" p = malloc(0x100);\n"
|
|
"}");
|
|
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:4]: (information) x configuration is needed to establish if there is a leak or not\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:4]: (information) x configuration is needed to establish if there is a leak or not\n", errout.str());
|
|
}
|
|
|
|
void configuration3() {
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (set_data(p)) { }\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:4]: (information) set_data configuration is needed to establish if there is a leak or not\n", errout.str());
|
|
|
|
check("void f() {\n"
|
|
" char *p = malloc(10);\n"
|
|
" if (set_data(p)) { return; }\n"
|
|
"}");
|
|
ASSERT_EQUALS("[test.c:3]: (information) set_data configuration is needed to establish if there is a leak or not\n"
|
|
"[test.c:4]: (information) set_data configuration is needed to establish if there is a leak or not\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) set_data configuration is needed to establish if there is a leak or not\n", errout.str());
|
|
}
|
|
};
|
|
|
|
REGISTER_TEST(TestLeakAutoVar)
|
|
|