From: Zhongxing Xu Date: Tue, 17 Nov 2009 07:54:15 +0000 (+0000) Subject: Add EvalEndPath interface to Checker. Now we can check memory leaked at the X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=243fde9f549a8f5f000c4baccb572dd0b7266a41;p=clang Add EvalEndPath interface to Checker. Now we can check memory leaked at the end of the path. Need to unify interfaces. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89063 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Analysis/PathSensitive/Checker.h b/include/clang/Analysis/PathSensitive/Checker.h index 26cfc8a520..b7ed20fab2 100644 --- a/include/clang/Analysis/PathSensitive/Checker.h +++ b/include/clang/Analysis/PathSensitive/Checker.h @@ -170,6 +170,8 @@ public: const Stmt *StoreE, SVal location, SVal val) {} virtual void EvalDeadSymbols(CheckerContext &C, const Stmt *S, SymbolReaper &SymReaper) {} + virtual void EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) {} }; } // end clang namespace diff --git a/lib/Analysis/GRExprEngine.cpp b/lib/Analysis/GRExprEngine.cpp index 0b7afe736e..2633177614 100644 --- a/lib/Analysis/GRExprEngine.cpp +++ b/lib/Analysis/GRExprEngine.cpp @@ -902,6 +902,11 @@ void GRExprEngine::VisitGuardedExpr(Expr* Ex, Expr* L, Expr* R, void GRExprEngine::ProcessEndPath(GREndPathNodeBuilder& builder) { getTF().EvalEndPath(*this, builder); StateMgr.EndPath(builder.getState()); + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + void *tag = I->first; + Checker *checker = I->second; + checker->EvalEndPath(builder, tag, *this); + } } /// ProcessSwitch - Called by GRCoreEngine. Used to generate successor diff --git a/lib/Analysis/MallocChecker.cpp b/lib/Analysis/MallocChecker.cpp index 6129358cf7..fdd6a3d026 100644 --- a/lib/Analysis/MallocChecker.cpp +++ b/lib/Analysis/MallocChecker.cpp @@ -22,8 +22,28 @@ using namespace clang; namespace { -enum RefState { - Allocated, Released, Escaped +struct RefState { + enum Kind { Allocated, Released, Escaped } K; + const Stmt *S; + + RefState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isAllocated() const { return K == Allocated; } + bool isReleased() const { return K == Released; } + bool isEscaped() const { return K == Escaped; } + + bool operator==(const RefState &X) const { + return K == X.K && S == X.S; + } + + static RefState getAllocated(const Stmt *s) { return RefState(Allocated, s); } + static RefState getReleased(const Stmt *s) { return RefState(Released, s); } + static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } }; class VISIBILITY_HIDDEN RegionState {}; @@ -39,25 +59,15 @@ public: static void *getTag(); void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); void EvalDeadSymbols(CheckerContext &C,const Stmt *S,SymbolReaper &SymReaper); + void EvalEndPath(GREndPathNodeBuilder &B, void *tag, GRExprEngine &Eng); private: void MallocMem(CheckerContext &C, const CallExpr *CE); void FreeMem(CheckerContext &C, const CallExpr *CE); }; } -namespace llvm { - template<> struct FoldingSetTrait { - static void Profile(const RefState &X, FoldingSetNodeID &ID) { - ID.AddInteger(X); - } - static void Profile(RefState &X, FoldingSetNodeID &ID) { - ID.AddInteger(X); - } - }; -} - namespace clang { - template<> + template <> struct GRStateTrait : public GRStatePartialTrait > { static void *GDMIndex() { return MallocChecker::getTag(); } @@ -101,7 +111,8 @@ void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { SymbolRef Sym = CallVal.getAsLocSymbol(); assert(Sym); // Set the symbol's state to Allocated. - const GRState *AllocState = state->set(Sym, Allocated); + const GRState *AllocState + = state->set(Sym, RefState::getAllocated(CE)); C.addTransition(C.GenerateNode(CE, AllocState)); } @@ -115,7 +126,7 @@ void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { assert(RS); // Check double free. - if (*RS == Released) { + if (RS->isReleased()) { ExplodedNode *N = C.GenerateNode(CE, true); if (N) { if (!BT_DoubleFree) @@ -130,7 +141,8 @@ void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { } // Normal free. - const GRState *FreedState = state->set(Sym, Released); + const GRState *FreedState + = state->set(Sym, RefState::getReleased(CE)); C.addTransition(C.GenerateNode(CE, FreedState)); } @@ -144,17 +156,37 @@ void MallocChecker::EvalDeadSymbols(CheckerContext &C, const Stmt *S, if (!RS) return; - if (*RS == Allocated) { + if (RS->isAllocated()) { ExplodedNode *N = C.GenerateNode(S, true); if (N) { if (!BT_Leak) BT_Leak = new BuiltinBug("Memory leak", "Allocated memory never released. Potential memory leak."); // FIXME: where it is allocated. - BugReport *R = new BugReport(*BT_Leak, - BT_Leak->getDescription(), N); + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); C.EmitReport(R); } } } } + +void MallocChecker::EvalEndPath(GREndPathNodeBuilder &B, void *tag, + GRExprEngine &Eng) { + const GRState *state = B.getState(); + typedef llvm::ImmutableMap SymMap; + SymMap M = state->get(); + + for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + RefState RS = I->second; + if (RS.isAllocated()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} diff --git a/test/Analysis/malloc.c b/test/Analysis/malloc.c index fb9674a32b..3dc5843ae0 100644 --- a/test/Analysis/malloc.c +++ b/test/Analysis/malloc.c @@ -8,9 +8,8 @@ void f1() { return; // expected-warning{{Allocated memory never released. Potential memory leak.}} } -// THIS TEST CURRENTLY FAILS. void f1_b() { - int *p = malloc(10); + int *p = malloc(10); // expected-warning{{Allocated memory never released. Potential memory leak.}} } void f2() { @@ -19,20 +18,20 @@ void f2() { free(p); // expected-warning{{Try to free a memory block that has been released}} } -// This case tests that storing malloc'ed memory to a static variable which is then returned -// is not leaked. In the absence of known contracts for functions or inter-procedural analysis, -// this is a conservative answer. +// This case tests that storing malloc'ed memory to a static variable which is +// then returned is not leaked. In the absence of known contracts for functions +// or inter-procedural analysis, this is a conservative answer. int *f3() { static int *p = 0; - p = malloc(10); // no-warning - return p; + p = malloc(10); // will be fixed. + return p; // expected-warning{{Allocated memory never released. Potential memory leak.}} } -// This case tests that storing malloc'ed memory to a static global variable which is then returned -// is not leaked. In the absence of known contracts for functions or inter-procedural analysis, -// this is a conservative answer. +// This case tests that storing malloc'ed memory to a static global variable +// which is then returned is not leaked. In the absence of known contracts for +// functions or inter-procedural analysis, this is a conservative answer. static int *p_f4 = 0; int *f4() { - p_f4 = malloc(10); // no-warning - return p_f4; + p_f4 = malloc(10); // will be fixed. + return p_f4; // expected-warning{{Allocated memory never released. Potential memory leak.}} }