diff options
author | Anna Zaks <ganna@apple.com> | 2012-08-24 02:28:20 +0000 |
---|---|---|
committer | Anna Zaks <ganna@apple.com> | 2012-08-24 02:28:20 +0000 |
commit | 55dd956d521d4d650dfd929d67f4b98ede61c0ea (patch) | |
tree | 5f878eff76444c47a7bce4740e6da1aafc487425 /lib/StaticAnalyzer/Checkers/MallocChecker.cpp | |
parent | 4f534e70b0922fa74d56022b71f0099d4b2c4e6b (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.cpp | 50 |
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; } |