diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index 307364e69..0b69e1739 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -101,6 +101,9 @@ void VarInfo::print() case NOALLOC: status = "noalloc"; break; + case REALLOC: + status = "realloc"; + break; default: status = "?"; break; @@ -111,6 +114,7 @@ void VarInfo::print() << "possibleUsage='" << strusage << "' " << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " " << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " " + << "reallocedFrom=" << it->second.reallocedFromType << std::endl; } } @@ -499,18 +503,24 @@ void CheckLeakAutoVar::checkScope(const Token * const startToken, const Token *vartok = nullptr; if (astIsVariableComparison(tok3, "!=", "0", &vartok)) { + varInfo2.reallocToAlloc(vartok->varId()); varInfo2.erase(vartok->varId()); if (notzero.find(vartok->varId()) != notzero.end()) varInfo2.clear(); } else if (astIsVariableComparison(tok3, "==", "0", &vartok)) { + varInfo1.reallocToAlloc(vartok->varId()); varInfo1.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, "<", "0", &vartok)) { + varInfo1.reallocToAlloc(vartok->varId()); varInfo1.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, ">", "0", &vartok)) { + varInfo2.reallocToAlloc(vartok->varId()); varInfo2.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, "==", "-1", &vartok)) { + varInfo1.reallocToAlloc(vartok->varId()); varInfo1.erase(vartok->varId()); } else if (astIsVariableComparison(tok3, "!=", "-1", &vartok)) { + varInfo2.reallocToAlloc(vartok->varId()); varInfo2.erase(vartok->varId()); } return ChildrenToVisit::none; @@ -801,15 +811,18 @@ void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::maplibrary.getReallocFuncInfo(fTok); if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) { const Token* argTok = getArguments(fTok).at(f->reallocArg - 1); - VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()]; + if (alloctype.find(argTok->varId()) != alloctype.end()) { + VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()]; + if (argAlloc.type != 0 && argAlloc.type != f->groupId) + mismatchError(fTok, argAlloc.allocTok, argTok->str()); + argAlloc.status = VarInfo::REALLOC; + argAlloc.allocTok = fTok; + } VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()]; - if (argAlloc.type != 0 && argAlloc.type != f->groupId) - mismatchError(fTok, argAlloc.allocTok, argTok->str()); - argAlloc.status = VarInfo::DEALLOC; - argAlloc.allocTok = fTok; retAlloc.type = f->groupId; retAlloc.status = VarInfo::ALLOC; retAlloc.allocTok = fTok; + retAlloc.reallocedFromType = argTok->varId(); } } @@ -826,7 +839,8 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo *varInfo, const VarInfo::AllocI varInfo->erase(arg->varId()); } else if (var->second.managed()) { doubleFreeError(tok, var->second.allocTok, arg->str(), allocation.type); - } else if (var->second.type != allocation.type) { + var->second.status = allocation.status; + } else if (var->second.type != allocation.type && var->second.type != 0) { // mismatching allocation and deallocation mismatchError(tok, var->second.allocTok, arg->str()); varInfo->erase(arg->varId()); diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h index 76b820438..83f75853f 100644 --- a/lib/checkleakautovar.h +++ b/lib/checkleakautovar.h @@ -39,7 +39,7 @@ class Tokenizer; class CPPCHECKLIB VarInfo { public: - enum AllocStatus { OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; + enum AllocStatus { REALLOC = -3, OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; struct AllocInfo { AllocStatus status; /** Allocation type. If it is a positive value then it corresponds to @@ -47,6 +47,7 @@ public: * checkleakautovar allocation type. */ int type; + int reallocedFromType = -1; const Token * allocTok; AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {} @@ -80,6 +81,16 @@ public: referenced.swap(other.referenced); } + void reallocToAlloc(nonneg int varid) { + const AllocInfo& alloc = alloctype[varid]; + if (alloc.reallocedFromType >= 0) { + const std::map::iterator it = alloctype.find(alloc.reallocedFromType); + if (it != alloctype.end() && it->second.status == REALLOC) { + it->second.status = ALLOC; + } + } + } + /** set possible usage for all variables */ void possibleUsageAll(const std::string &functionName); diff --git a/test/testleakautovar.cpp b/test/testleakautovar.cpp index 272b783db..a76aa96cc 100644 --- a/test/testleakautovar.cpp +++ b/test/testleakautovar.cpp @@ -86,6 +86,8 @@ private: TEST_CASE(realloc1); TEST_CASE(realloc2); TEST_CASE(realloc3); + TEST_CASE(realloc4); + TEST_CASE(realloc5); // #9292, #9990 TEST_CASE(freopen1); TEST_CASE(freopen2); @@ -108,6 +110,7 @@ private: TEST_CASE(doublefree8); TEST_CASE(doublefree9); TEST_CASE(doublefree10); // #8706 + TEST_CASE(doublefree11); // exit TEST_CASE(exit1); @@ -139,6 +142,8 @@ private: 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); // switch TEST_CASE(switch1); @@ -467,6 +472,40 @@ private: 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" @@ -1196,6 +1235,22 @@ private: 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 exit1() { check("void f() {\n" " char *p = malloc(10);\n" @@ -1508,6 +1563,39 @@ private: 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 switch1() { check("void f() {\n" " char *p = 0;\n"