aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp114
1 files changed, 80 insertions, 34 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index f486a7e8c9..98298c850b 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -42,6 +42,7 @@ public:
bool isReleased() const { return K == Released; }
//bool isEscaped() const { return K == Escaped; }
//bool isRelinquished() const { return K == Relinquished; }
+ const Stmt *getStmt() const { return S; }
bool operator==(const RefState &X) const {
return K == X.K && S == X.S;
@@ -65,8 +66,6 @@ public:
}
};
-class RegionState {};
-
class MallocChecker : public Checker<check::DeadSymbols,
check::EndPath,
check::PreStmt<ReturnStmt>,
@@ -188,7 +187,9 @@ private:
} // end anonymous namespace
typedef llvm::ImmutableMap<SymbolRef, RefState> RegionStateTy;
-
+typedef llvm::ImmutableMap<SymbolRef, SymbolRef> SymRefToSymRefTy;
+class RegionState {};
+class ReallocPairs {};
namespace clang {
namespace ento {
template <>
@@ -196,6 +197,12 @@ namespace ento {
: public ProgramStatePartialTrait<RegionStateTy> {
static void *GDMIndex() { static int x; return &x; }
};
+
+ template <>
+ struct ProgramStateTrait<ReallocPairs>
+ : public ProgramStatePartialTrait<SymRefToSymRefTy> {
+ static void *GDMIndex() { static int x; return &x; }
+ };
}
}
@@ -642,43 +649,55 @@ void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) const {
svalBuilder.evalEQ(state, Arg1Val,
svalBuilder.makeIntValWithPtrWidth(0, false));
+ ProgramStateRef StatePtrIsNull, StatePtrNotNull;
+ llvm::tie(StatePtrIsNull, StatePtrNotNull) = state->assume(PtrEQ);
+ ProgramStateRef StateSizeIsZero, StateSizeNotZero;
+ llvm::tie(StateSizeIsZero, StateSizeNotZero) = state->assume(SizeZero);
+ // We only assume exceptional states if they are definitely true; if the
+ // state is under-constrained, assume regular realloc behavior.
+ bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
+ bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero;
+
// If the ptr is NULL and the size is not 0, the call is equivalent to
// malloc(size).
- ProgramStateRef stateEqual = state->assume(PtrEQ, true);
- if (stateEqual && state->assume(SizeZero, false)) {
- // Hack: set the NULL symbolic region to released to suppress false warning.
- // In the future we should add more states for allocated regions, e.g.,
- // CheckedNull, CheckedNonNull.
-
- SymbolRef Sym = arg0Val.getAsLocSymbol();
- if (Sym)
- stateEqual = stateEqual->set<RegionState>(Sym, RefState::getReleased(CE));
-
+ if ( PrtIsNull && !SizeIsZero) {
ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
- UndefinedVal(), stateEqual);
+ UndefinedVal(), StatePtrIsNull);
C.addTransition(stateMalloc);
+ return;
}
- if (ProgramStateRef stateNotEqual = state->assume(PtrEQ, false)) {
- // If the size is 0, free the memory.
- if (ProgramStateRef stateSizeZero =
- stateNotEqual->assume(SizeZero, true))
- if (ProgramStateRef stateFree =
- FreeMemAux(C, CE, stateSizeZero, 0, false)) {
+ if (PrtIsNull && SizeIsZero)
+ return;
- // Bind the return value to NULL because it is now free.
- C.addTransition(stateFree->BindExpr(CE, LCtx,
- svalBuilder.makeNull(), true));
- }
- if (ProgramStateRef stateSizeNotZero =
- stateNotEqual->assume(SizeZero,false))
- if (ProgramStateRef stateFree = FreeMemAux(C, CE, stateSizeNotZero,
- 0, false)) {
- // FIXME: We should copy the content of the original buffer.
- ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
- UnknownVal(), stateFree);
- C.addTransition(stateRealloc);
- }
+ assert(!PrtIsNull);
+
+ // If the size is 0, free the memory.
+ if (SizeIsZero)
+ if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){
+ // Bind the return value to NULL because it is now free.
+ // TODO: This is tricky. Does not currently work.
+ // The semantics of the return value are:
+ // If size was equal to 0, either NULL or a pointer suitable to be passed
+ // to free() is returned.
+ C.addTransition(stateFree->BindExpr(CE, LCtx,
+ svalBuilder.makeNull(), true));
+ return;
+ }
+
+ // Default behavior.
+ if (ProgramStateRef stateFree = FreeMemAux(C, CE, state, 0, false)) {
+ // FIXME: We should copy the content of the original buffer.
+ ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
+ UnknownVal(), stateFree);
+ SymbolRef FromPtr = arg0Val.getAsSymbol();
+ SVal RetVal = state->getSVal(CE, LCtx);
+ SymbolRef ToPtr = RetVal.getAsSymbol();
+ if (!stateRealloc || !FromPtr || !ToPtr)
+ return;
+ stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr, FromPtr);
+ C.addTransition(stateRealloc);
+ return;
}
}
@@ -738,6 +757,14 @@ void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
}
}
+ // Cleanup the Realloc Pairs Map.
+ SymRefToSymRefTy RP = state->get<ReallocPairs>();
+ for (SymRefToSymRefTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
+ if (SymReaper.isDead(I->first) || SymReaper.isDead(I->second)) {
+ state = state->remove<ReallocPairs>(I->first);
+ }
+ }
+
ExplodedNode *N = C.addTransition(state->set<RegionState>(RS));
if (N && generateReport) {
@@ -871,7 +898,6 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
SVal Cond,
bool Assumption) const {
RegionStateTy RS = state->get<RegionState>();
-
for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
// If the symbol is assumed to NULL or another constant, this will
// return an APSInt*.
@@ -879,6 +905,26 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
state = state->remove<RegionState>(I.getKey());
}
+ // Realloc returns 0 when reallocation fails, which means that we should
+ // restore the state of the pointer being reallocated.
+ SymRefToSymRefTy RP = state->get<ReallocPairs>();
+ for (SymRefToSymRefTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
+ // If the symbol is assumed to NULL or another constant, this will
+ // return an APSInt*.
+ if (state->getSymVal(I.getKey())) {
+ const RefState *RS = state->get<RegionState>(I.getData());
+ if (RS) {
+ if (RS->isReleased())
+ state = state->set<RegionState>(I.getData(),
+ RefState::getAllocateUnchecked(RS->getStmt()));
+ if (RS->isAllocated())
+ state = state->set<RegionState>(I.getData(),
+ RefState::getReleased(RS->getStmt()));
+ }
+ state = state->remove<ReallocPairs>(I.getKey());
+ }
+ }
+
return state;
}