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 26cfc8a..b7ed20f 100644
--- a/include/clang/Analysis/PathSensitive/Checker.h
+++ b/include/clang/Analysis/PathSensitive/Checker.h
@@ -170,6 +170,8 @@
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 0b7afe7..2633177 100644
--- a/lib/Analysis/GRExprEngine.cpp
+++ b/lib/Analysis/GRExprEngine.cpp
@@ -902,6 +902,11 @@
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 6129358..fdd6a3d 100644
--- a/lib/Analysis/MallocChecker.cpp
+++ b/lib/Analysis/MallocChecker.cpp
@@ -22,8 +22,28 @@
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 @@
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<RefState> {
- 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<RegionState>
: public GRStatePartialTrait<llvm::ImmutableMap<SymbolRef, RefState> > {
static void *GDMIndex() { return MallocChecker::getTag(); }
@@ -101,7 +111,8 @@
SymbolRef Sym = CallVal.getAsLocSymbol();
assert(Sym);
// Set the symbol's state to Allocated.
- const GRState *AllocState = state->set<RegionState>(Sym, Allocated);
+ const GRState *AllocState
+ = state->set<RegionState>(Sym, RefState::getAllocated(CE));
C.addTransition(C.GenerateNode(CE, AllocState));
}
@@ -115,7 +126,7 @@
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 @@
}
// Normal free.
- const GRState *FreedState = state->set<RegionState>(Sym, Released);
+ const GRState *FreedState
+ = state->set<RegionState>(Sym, RefState::getReleased(CE));
C.addTransition(C.GenerateNode(CE, FreedState));
}
@@ -144,17 +156,37 @@
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<SymbolRef, RefState> SymMap;
+ SymMap M = state->get<RegionState>();
+
+ 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 fb9674a..3dc5843 100644
--- a/test/Analysis/malloc.c
+++ b/test/Analysis/malloc.c
@@ -8,9 +8,8 @@
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 @@
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.}}
}