diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CStringChecker.cpp | 1 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp | 188 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/IteratorChecker.cpp | 25 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 43 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp | 2 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp | 18 | ||||
-rw-r--r-- | lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp | 10 |
7 files changed, 194 insertions, 93 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 278452ec994a6..12a576e5d80d5 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -552,6 +552,7 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get()); auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); + Report->addRange(S->getSourceRange()); bugreporter::trackNullOrUndefValue(N, S, *Report); C.emitReport(std::move(Report)); } diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index ed877ab345189..29677f737f5cf 100644 --- a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -91,37 +91,53 @@ public: ReserveFn("reserve"), ResizeFn("resize"), ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {} - /// Check whether the function called on the container object is a - /// member function that potentially invalidates pointers referring - /// to the objects's internal buffer. - bool mayInvalidateBuffer(const CallEvent &Call) const; - - /// Record the connection between the symbol returned by c_str() and the - /// corresponding string object region in the ProgramState. Mark the symbol - /// released if the string object is destroyed. + /// Check if the object of this member function call is a `basic_string`. + bool isCalledOnStringObject(const CXXInstanceCall *ICall) const; + + /// Check whether the called member function potentially invalidates + /// pointers referring to the container object's inner buffer. + bool isInvalidatingMemberFunction(const CallEvent &Call) const; + + /// Mark pointer symbols associated with the given memory region released + /// in the program state. + void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, + const MemRegion *ObjRegion, + CheckerContext &C) const; + + /// Standard library functions that take a non-const `basic_string` argument by + /// reference may invalidate its inner pointers. Check for these cases and + /// mark the pointers released. + void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, + CheckerContext &C) const; + + /// Record the connection between raw pointers referring to a container + /// object's inner buffer and the object's memory region in the program state. + /// Mark potentially invalidated pointers released. void checkPostCall(const CallEvent &Call, CheckerContext &C) const; - /// Clean up the ProgramState map. + /// Clean up the program state map. void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; }; } // end anonymous namespace -// [string.require] -// -// "References, pointers, and iterators referring to the elements of a -// basic_string sequence may be invalidated by the following uses of that -// basic_string object: -// -// -- TODO: As an argument to any standard library function taking a reference -// to non-const basic_string as an argument. For example, as an argument to -// non-member functions swap(), operator>>(), and getline(), or as an argument -// to basic_string::swap(). -// -// -- Calling non-const member functions, except operator[], at, front, back, -// begin, rbegin, end, and rend." -// -bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const { +bool InnerPointerChecker::isCalledOnStringObject( + const CXXInstanceCall *ICall) const { + const auto *ObjRegion = + dyn_cast_or_null<TypedValueRegion>(ICall->getCXXThisVal().getAsRegion()); + if (!ObjRegion) + return false; + + QualType ObjTy = ObjRegion->getValueType(); + if (ObjTy.isNull() || + ObjTy->getAsCXXRecordDecl()->getName() != "basic_string") + return false; + + return true; +} + +bool InnerPointerChecker::isInvalidatingMemberFunction( + const CallEvent &Call) const { if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) { OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); if (Opc == OO_Equal || Opc == OO_PlusEqual) @@ -137,53 +153,104 @@ bool InnerPointerChecker::mayInvalidateBuffer(const CallEvent &Call) const { Call.isCalled(SwapFn)); } -void InnerPointerChecker::checkPostCall(const CallEvent &Call, - CheckerContext &C) const { - const auto *ICall = dyn_cast<CXXInstanceCall>(&Call); - if (!ICall) +void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, + ProgramStateRef State, + const MemRegion *MR, + CheckerContext &C) const { + if (const PtrSet *PS = State->get<RawPtrMap>(MR)) { + const Expr *Origin = Call.getOriginExpr(); + for (const auto Symbol : *PS) { + // NOTE: `Origin` may be null, and will be stored so in the symbol's + // `RefState` in MallocChecker's `RegionState` program state map. + State = allocation_state::markReleased(State, Symbol, Origin); + } + State = State->remove<RawPtrMap>(MR); + C.addTransition(State); return; + } +} - SVal Obj = ICall->getCXXThisVal(); - const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(Obj.getAsRegion()); - if (!ObjRegion) - return; +void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, + ProgramStateRef State, + CheckerContext &C) const { + if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) { + const FunctionDecl *FD = FC->getDecl(); + if (!FD || !FD->isInStdNamespace()) + return; - auto *TypeDecl = ObjRegion->getValueType()->getAsCXXRecordDecl(); - if (TypeDecl->getName() != "basic_string") - return; + for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) { + QualType ParamTy = FD->getParamDecl(I)->getType(); + if (!ParamTy->isReferenceType() || + ParamTy->getPointeeType().isConstQualified()) + continue; - ProgramStateRef State = C.getState(); + // In case of member operator calls, `this` is counted as an + // argument but not as a parameter. + bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC); + unsigned ArgI = isaMemberOpCall ? I+1 : I; - if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { - SVal RawPtr = Call.getReturnValue(); - if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { - // Start tracking this raw pointer by adding it to the set of symbols - // associated with this container object in the program state map. - PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); - const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); - PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); - assert(C.wasInlined || !Set.contains(Sym)); - Set = F.add(Set, Sym); - State = State->set<RawPtrMap>(ObjRegion, Set); - C.addTransition(State); + SVal Arg = FC->getArgSVal(ArgI); + const auto *ArgRegion = + dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion()); + if (!ArgRegion) + continue; + + markPtrSymbolsReleased(Call, State, ArgRegion, C); } - return; } +} - if (mayInvalidateBuffer(Call)) { - if (const PtrSet *PS = State->get<RawPtrMap>(ObjRegion)) { - // Mark all pointer symbols associated with the deleted object released. - const Expr *Origin = Call.getOriginExpr(); - for (const auto Symbol : *PS) { - // NOTE: `Origin` may be null, and will be stored so in the symbol's - // `RefState` in MallocChecker's `RegionState` program state map. - State = allocation_state::markReleased(State, Symbol, Origin); +// [string.require] +// +// "References, pointers, and iterators referring to the elements of a +// basic_string sequence may be invalidated by the following uses of that +// basic_string object: +// +// -- As an argument to any standard library function taking a reference +// to non-const basic_string as an argument. For example, as an argument to +// non-member functions swap(), operator>>(), and getline(), or as an argument +// to basic_string::swap(). +// +// -- Calling non-const member functions, except operator[], at, front, back, +// begin, rbegin, end, and rend." + +void InnerPointerChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { + if (isCalledOnStringObject(ICall)) { + const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>( + ICall->getCXXThisVal().getAsRegion()); + + if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { + SVal RawPtr = Call.getReturnValue(); + if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { + // Start tracking this raw pointer by adding it to the set of symbols + // associated with this container object in the program state map. + + PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); + const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); + PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); + assert(C.wasInlined || !Set.contains(Sym)); + Set = F.add(Set, Sym); + + State = State->set<RawPtrMap>(ObjRegion, Set); + C.addTransition(State); + } + return; + } + + // Check [string.require] / second point. + if (isInvalidatingMemberFunction(Call)) { + markPtrSymbolsReleased(Call, State, ObjRegion, C); + return; } - State = State->remove<RawPtrMap>(ObjRegion); - C.addTransition(State); - return; } } + + // Check [string.require] / first point. + checkFunctionArguments(Call, State, C); } void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, @@ -216,7 +283,6 @@ InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { - if (!isSymbolTracked(N->getState(), PtrToBuf) || isSymbolTracked(PrevN->getState(), PtrToBuf)) return nullptr; diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp index 56c250cd16783..520c32e1c7703 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -291,6 +291,7 @@ const ContainerData *getContainerData(ProgramStateRef State, const MemRegion *Cont); ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, const ContainerData &CData); +bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); bool isOutOfRange(ProgramStateRef State, const IteratorPosition &Pos); bool isZero(ProgramStateRef State, const NonLoc &Val); } // namespace @@ -536,7 +537,11 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, auto ContMap = State->get<ContainerMap>(); for (const auto Cont : ContMap) { if (!SR.isLiveRegion(Cont.first)) { - State = State->remove<ContainerMap>(Cont.first); + // We must keep the container data while it has live iterators to be able + // to compare them to the begin and the end of the container. + if (!hasLiveIterators(State, Cont.first)) { + State = State->remove<ContainerMap>(Cont.first); + } } } @@ -546,6 +551,8 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, State = State->remove<IteratorComparisonMap>(Comp.first); } } + + C.addTransition(State); } ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond, @@ -1188,6 +1195,22 @@ ProgramStateRef relateIteratorPositions(ProgramStateRef State, return NewState; } +bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { + auto RegionMap = State->get<IteratorRegionMap>(); + for (const auto Reg : RegionMap) { + if (Reg.second.getContainer() == Cont) + return true; + } + + auto SymbolMap = State->get<IteratorSymbolMap>(); + for (const auto Sym : SymbolMap) { + if (Sym.second.getContainer() == Cont) + return true; + } + + return false; +} + bool isZero(ProgramStateRef State, const NonLoc &Val) { auto &BVF = State->getBasicVals(); return compare(State, Val, diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 8f07f413e81f6..ebaf79a780c07 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -179,11 +179,11 @@ public: II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), II_if_nameindex(nullptr), II_if_freenameindex(nullptr), II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr), - II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), - II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), - II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), - II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), - II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), + II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), + II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), + II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), + II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), + II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), II_g_try_realloc_n(nullptr) {} /// In pessimistic mode, the checker assumes that it does not know which @@ -248,11 +248,11 @@ private: *II_realloc, *II_calloc, *II_valloc, *II_reallocf, *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, *II_if_nameindex, *II_if_freenameindex, *II_wcsdup, - *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, - *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, - *II_g_try_realloc, *II_g_free, *II_g_memdup, - *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n, - *II_g_try_malloc_n, *II_g_try_malloc0_n, + *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, + *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, + *II_g_try_realloc, *II_g_free, *II_g_memdup, + *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n, + *II_g_try_malloc_n, *II_g_try_malloc0_n, *II_g_try_realloc_n; mutable Optional<uint64_t> KernelZeroFlagVal; @@ -346,7 +346,7 @@ private: ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, bool FreesMemOnFailure, - ProgramStateRef State, + ProgramStateRef State, bool SuffixWithN = false) const; static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); @@ -652,7 +652,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, initIdentifierInfo(C); if (Family == AF_Malloc && CheckFree) { - if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || FunI == II_g_free) return true; } @@ -662,12 +662,12 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup || FunI == II_win_wcsdup || FunI == II_kmalloc || - FunI == II_g_malloc || FunI == II_g_malloc0 || - FunI == II_g_realloc || FunI == II_g_try_malloc || + FunI == II_g_malloc || FunI == II_g_malloc0 || + FunI == II_g_realloc || FunI == II_g_try_malloc || FunI == II_g_try_malloc0 || FunI == II_g_try_realloc || - FunI == II_g_memdup || FunI == II_g_malloc_n || - FunI == II_g_malloc0_n || FunI == II_g_realloc_n || - FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n || + FunI == II_g_memdup || FunI == II_g_malloc_n || + FunI == II_g_malloc0_n || FunI == II_g_realloc_n || + FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n || FunI == II_g_try_realloc_n) return true; } @@ -873,7 +873,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_realloc || FunI == II_g_realloc || + } else if (FunI == II_realloc || FunI == II_g_realloc || FunI == II_g_try_realloc) { State = ReallocMemAux(C, CE, false, State); State = ProcessZeroAllocation(C, CE, 1, State); @@ -936,7 +936,7 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { return; State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || + } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { if (CE->getNumArgs() < 2) return; @@ -2930,6 +2930,11 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( OS << MemCallE->getMethodDecl()->getNameAsString(); } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { OS << OpCallE->getDirectCallee()->getNameAsString(); + } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { + auto &CEMgr = BRC.getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); + const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()); + OS << (D ? D->getNameAsString() : "unknown"); } OS << "'"; } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp index 2c1e139330d68..9c85c09837239 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -4000,7 +4000,7 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS, // Don't process anything within synthesized bodies. const LocationContext *LCtx = Pred->getLocationContext(); if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) { - assert(!LCtx->inTopFrame()); + assert(!LCtx->inTopFrame()); return; } diff --git a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp index 64b61a0213d28..55516a34d1a78 100644 --- a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp @@ -45,9 +45,9 @@ const char * RunLoopBind = "NSRunLoopM"; const char * RunLoopRunBind = "RunLoopRunM"; const char * OtherMsgBind = "OtherMessageSentM"; const char * AutoreleasePoolBind = "AutoreleasePoolM"; +const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM"; -class RunLoopAutoreleaseLeakChecker : public Checker< - check::ASTCodeBody> { +class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> { public: void checkASTCodeBody(const Decl *D, @@ -66,6 +66,8 @@ static TriBoolTy seenBeforeRec(const Stmt *Parent, const Stmt *A, const Stmt *B, MemoizationMapTy &Memoization) { for (const Stmt *C : Parent->children()) { + if (!C) continue; + if (C == A) return true; @@ -110,17 +112,20 @@ static void emitDiagnostics(BoundNodes &Match, const auto *AP = Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind); + const auto *OAP = + Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind); bool HasAutoreleasePool = (AP != nullptr); const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind); const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind); assert(RLR && "Run loop launch not found"); - assert(ME != RLR); - if (HasAutoreleasePool && seenBefore(AP, RLR, ME)) + + // Launch of run loop occurs before the message-sent expression is seen. + if (seenBefore(DeclBody, RLR, ME)) return; - if (!HasAutoreleasePool && seenBefore(DeclBody, RLR, ME)) + if (HasAutoreleasePool && (OAP != AP)) return; PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( @@ -169,7 +174,8 @@ static void checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR, const RunLoopAutoreleaseLeakChecker *Chkr) { StatementMatcher RunLoopRunM = getRunLoopRunM(); - StatementMatcher OtherMessageSentM = getOtherMessageSentM(); + StatementMatcher OtherMessageSentM = getOtherMessageSentM( + hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind))); StatementMatcher RunLoopInAutorelease = autoreleasePoolStmt( diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index feae9e59b343a..e7a20fa03a4ae 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -129,7 +129,7 @@ bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const { for (const auto &C : B.captures()) { const auto *T = C.getVariable()->getType()->getAs<TypedefType>(); if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII) - return true; + return true; } return false; } @@ -175,9 +175,9 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( // There is a not-too-uncommon idiom // where a block passed to dispatch_async captures a semaphore // and then the thread (which called dispatch_async) is blocked on waiting - // for the completion of the execution of the block - // via dispatch_semaphore_wait. To avoid false-positives (for now) - // we ignore all the blocks which have captured + // for the completion of the execution of the block + // via dispatch_semaphore_wait. To avoid false-positives (for now) + // we ignore all the blocks which have captured // a variable of the type "dispatch_semaphore_t". if (isSemaphoreCaptured(*B.getDecl())) return; @@ -263,7 +263,7 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) checkReturnedBlockCaptures(*B, C); - if (!isa<StackSpaceRegion>(R->getMemorySpace()) || + if (!isa<StackSpaceRegion>(R->getMemorySpace()) || isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C)) return; |