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 {};
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(); }
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));
}
assert(RS);
// Check double free.
- if (*RS == Released) {
+ if (RS->isReleased()) {
ExplodedNode *N = C.GenerateNode(CE, true);
if (N) {
if (!BT_DoubleFree)
}
// 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));
}
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);
+ }
+ }
+ }
+}
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() {
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.}}
}