aboutsummaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
diff options
context:
space:
mode:
authorAnna Zaks <ganna@apple.com>2012-08-24 02:28:20 +0000
committerAnna Zaks <ganna@apple.com>2012-08-24 02:28:20 +0000
commit55dd956d521d4d650dfd929d67f4b98ede61c0ea (patch)
tree5f878eff76444c47a7bce4740e6da1aafc487425 /lib/StaticAnalyzer/Checkers/MallocChecker.cpp
parent4f534e70b0922fa74d56022b71f0099d4b2c4e6b (diff)
[analyzer] Fix realloc related bug in the malloc checker.
When reallocation of a non-allocated (not owned) symbol fails do not expect it to be freed. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@162533 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/MallocChecker.cpp')
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp50
1 files changed, 38 insertions, 12 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index c6fa20c4ae..a8ef2e5bf8 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -70,9 +70,16 @@ public:
}
};
+/// \class ReallocPair
+/// \brief Stores information about the symbol being reallocated by a call to
+/// 'realloc' to allow modeling failed reallocation later in the path.
struct ReallocPair {
+ // \brief The symbol which realloc reallocated.
SymbolRef ReallocatedSym;
+ // \brief The flag is true if the symbol does not need to be freed after
+ // reallocation fails.
bool IsFreeOnFailure;
+
ReallocPair(SymbolRef S, bool F) : ReallocatedSym(S), IsFreeOnFailure(F) {}
void Profile(llvm::FoldingSetNodeID &ID) const {
ID.AddInteger(IsFreeOnFailure);
@@ -177,11 +184,13 @@ private:
const OwnershipAttr* Att) const;
ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
ProgramStateRef state, unsigned Num,
- bool Hold) const;
+ bool Hold,
+ bool &ReleasedAllocated) const;
ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
const Expr *ParentExpr,
ProgramStateRef state,
- bool Hold) const;
+ bool Hold,
+ bool &ReleasedAllocated) const;
ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
bool FreesMemOnFailure) const;
@@ -431,6 +440,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
ProgramStateRef State = C.getState();
+ bool ReleasedAllocatedMemory = false;
if (FD->getKind() == Decl::Function) {
initIdentifierInfo(C.getASTContext());
@@ -447,7 +457,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
} else if (FunI == II_calloc) {
State = CallocMem(C, CE);
} else if (FunI == II_free) {
- State = FreeMemAux(C, CE, State, 0, false);
+ State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
} else if (FunI == II_strdup) {
State = MallocUpdateRefState(C, CE, State);
} else if (FunI == II_strndup) {
@@ -494,6 +504,7 @@ void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call,
// Ex: [NSData dataWithBytesNoCopy:bytes length:10];
// Unless 'freeWhenDone' param set to 0.
// TODO: Check that the memory was allocated with malloc.
+ bool ReleasedAllocatedMemory = false;
Selector S = Call.getSelector();
if ((S.getNameForSlot(0) == "dataWithBytesNoCopy" ||
S.getNameForSlot(0) == "initWithBytesNoCopy" ||
@@ -501,7 +512,8 @@ void MallocChecker::checkPreObjCMessage(const ObjCMethodCall &Call,
!isFreeWhenDoneSetToZero(Call)){
unsigned int argIdx = 0;
C.addTransition(FreeMemAux(C, Call.getArgExpr(argIdx),
- Call.getOriginExpr(), C.getState(), true));
+ Call.getOriginExpr(), C.getState(), true,
+ ReleasedAllocatedMemory));
}
}
@@ -584,11 +596,13 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
return 0;
ProgramStateRef State = C.getState();
+ bool ReleasedAllocated = false;
for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
I != E; ++I) {
ProgramStateRef StateI = FreeMemAux(C, CE, State, *I,
- Att->getOwnKind() == OwnershipAttr::Holds);
+ Att->getOwnKind() == OwnershipAttr::Holds,
+ ReleasedAllocated);
if (StateI)
State = StateI;
}
@@ -599,18 +613,20 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const CallExpr *CE,
ProgramStateRef state,
unsigned Num,
- bool Hold) const {
+ bool Hold,
+ bool &ReleasedAllocated) const {
if (CE->getNumArgs() < (Num + 1))
return 0;
- return FreeMemAux(C, CE->getArg(Num), CE, state, Hold);
+ return FreeMemAux(C, CE->getArg(Num), CE, state, Hold, ReleasedAllocated);
}
ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
const Expr *ArgExpr,
const Expr *ParentExpr,
ProgramStateRef state,
- bool Hold) const {
+ bool Hold,
+ bool &ReleasedAllocated) const {
SVal ArgVal = state->getSVal(ArgExpr, C.getLocationContext());
if (!isa<DefinedOrUnknownSVal>(ArgVal))
@@ -691,6 +707,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
return 0;
}
+ ReleasedAllocated = (RS != 0);
+
// Normal free.
if (Hold)
return state->set<RegionState>(Sym, RefState::getRelinquished(ParentExpr));
@@ -886,9 +904,12 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
if (!FromPtr || !ToPtr)
return 0;
+ bool ReleasedAllocated = false;
+
// If the size is 0, free the memory.
if (SizeIsZero)
- if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero,0,false)){
+ if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0,
+ false, ReleasedAllocated)){
// 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. We just free the input pointer and do not add
@@ -897,14 +918,19 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
}
// Default behavior.
- if (ProgramStateRef stateFree = FreeMemAux(C, CE, state, 0, false)) {
- // FIXME: We should copy the content of the original buffer.
+ if (ProgramStateRef stateFree =
+ FreeMemAux(C, CE, state, 0, false, ReleasedAllocated)) {
+
ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
UnknownVal(), stateFree);
if (!stateRealloc)
return 0;
+
+ // Record the info about the reallocated symbol so that we could properly
+ // process failed reallocation.
stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
- ReallocPair(FromPtr, FreesOnFail));
+ ReallocPair(FromPtr, FreesOnFail || !ReleasedAllocated));
+ // The reallocated symbol should stay alive for as long as the new symbol.
C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
return stateRealloc;
}