Index: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h =================================================================== --- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h +++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h @@ -224,13 +224,39 @@ } /// \brief Generate a sink node. Generating a sink stops exploration of the - /// given path. - ExplodedNode *generateSink(ProgramStateRef State = nullptr, - ExplodedNode *Pred = nullptr, + /// given path. To create a sink node for the purpose of reporting an error, + /// checkers should use generateErrorNode() instead. + ExplodedNode *generateSink(ProgramStateRef State, ExplodedNode *Pred, const ProgramPointTag *Tag = nullptr) { return addTransitionImpl(State ? State : getState(), true, Pred, Tag); } + /// \brief Generate a transition to a node that will be used to report + /// an error. This node will be a sink. That is, it will stop exploration of + /// the given path. + /// + /// @param State The state of the generated node. + /// @param Tag The tag to uniquely identify the creation site. If null, + /// the default tag for the checker will be used. + ExplodedNode *generateErrorNode(ProgramStateRef State = nullptr, + const ProgramPointTag *Tag = nullptr) { + return generateSink(State, Pred, + (Tag ? Tag : Location.getTag())); + } + + /// \brief Generate a transition to a node that will be used to report + /// an error. This node will not be a sink. That is, exploration will + /// continue along this path. + /// + /// @param State The state of the generated node. + /// @param Tag The tag to uniquely identify the creation site. If null, + /// the default tag for the checker will be used. + ExplodedNode * + generateNonFatalErrorNode(ProgramStateRef State = nullptr, + const ProgramPointTag *Tag = nullptr) { + return addTransition(State, (Tag ? Tag : Location.getTag())); + } + /// \brief Emit the diagnostics report. void emitReport(std::unique_ptr R) { Changed = true; @@ -287,6 +313,18 @@ bool MarkAsSink, ExplodedNode *P = nullptr, const ProgramPointTag *Tag = nullptr) { + // The analyzer may stop exploring if it sees a state it has previously + // visited ("cache out"). The early return here is a defensive check to + // prevent accidental caching out by checker API clients. Unless there is a + // tag or the the client checker has requested that the generated node be + // marked as a sink, we assume that a client requesting a transition to a + // state that is the same as the predecessor state has made a mistake. We + // return the predecessor rather than cache out. + // + // TODO: We could potentially change the return to an assertion to alert + // clients to their mistake, but several checkers (including + // DereferenceChecker, CallAndMessageChecker, and DynamicTypePropagation) + // rely upon the defensive behavior and would need to be updated. if (!State || (State == Pred->getState() && !Tag && !MarkAsSink)) return Pred; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -62,7 +62,7 @@ ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -182,7 +182,7 @@ ProgramStateRef errorState, OOB_Kind kind) const { - ExplodedNode *errorNode = checkerContext.generateSink(errorState); + ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); if (!errorNode) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -140,7 +140,7 @@ ProgramStateRef State = C.getState(); if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { generateBugReport(N, Msg, E->getSourceRange(), E, C); } @@ -157,7 +157,7 @@ if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -489,14 +489,15 @@ if (SourceSize == TargetSize) return; - // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; - // otherwise generate a regular node. + // Generate an error. Only generate a sink error node + // if 'SourceSize < TargetSize'; otherwise generate a non-fatal error node. // // FIXME: We can actually create an abstract "CFNumber" object that has // the bits initialized to the provided values. // - if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() - : C.addTransition()) { + ExplodedNode *N = SourceSize < TargetSize ? C.generateErrorNode() + : C.generateNonFatalErrorNode(); + if (N) { SmallString<128> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -589,7 +590,7 @@ std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); if (stateTrue && !stateFalse) { - ExplodedNode *N = C.generateSink(stateTrue); + ExplodedNode *N = C.generateErrorNode(stateTrue); if (!N) return; @@ -656,7 +657,7 @@ if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) return; - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { SmallString<200> buf; llvm::raw_svector_ostream os(buf); @@ -800,7 +801,7 @@ // Generate only one error node to use for all bug reports. if (!errorNode.hasValue()) - errorNode = C.addTransition(); + errorNode = C.generateNonFatalErrorNode(); if (!errorNode.getValue()) continue; @@ -1025,7 +1026,7 @@ } if (!State) - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); else if (State != C.getState()) C.addTransition(State); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -32,7 +32,7 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef state, CheckerContext &C) const { - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT) BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value")); C.emitReport(llvm::make_unique(*BT, BT->getDescription(), N)); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -229,7 +229,7 @@ if (!Filter.CheckCStringNullArg) return nullptr; - ExplodedNode *N = C.generateSink(stateNull); + ExplodedNode *N = C.generateErrorNode(stateNull); if (!N) return nullptr; @@ -292,7 +292,7 @@ ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return nullptr; @@ -525,7 +525,7 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, const Stmt *First, const Stmt *Second) const { - ExplodedNode *N = C.generateSink(state); + ExplodedNode *N = C.generateErrorNode(state); if (!N) return; @@ -585,7 +585,7 @@ if (stateOverflow && !stateOkay) { // We have an overflow. Emit a bug report. - ExplodedNode *N = C.generateSink(stateOverflow); + ExplodedNode *N = C.generateErrorNode(stateOverflow); if (!N) return nullptr; @@ -706,7 +706,7 @@ if (!Filter.CheckCStringNotNullTerm) return UndefinedVal(); - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_NotCString) BT_NotCString.reset(new BuiltinBug( Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, @@ -766,7 +766,7 @@ if (!Filter.CheckCStringNotNullTerm) return UndefinedVal(); - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_NotCString) BT_NotCString.reset(new BuiltinBug( Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -97,7 +97,7 @@ void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -169,7 +169,7 @@ const ProgramStateRef State = C.getState(); const SVal PSV = State->getSVal(SValMemRegion); if (PSV.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); auto R = llvm::make_unique(*BT, Message, N); R->addRange(ArgRange); @@ -200,7 +200,7 @@ return true; if (V.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); // Generate a report for this bug. @@ -265,7 +265,7 @@ D->getStore()); if (F.Find(D->getRegion())) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); SmallString<512> Str; llvm::raw_svector_ostream os(Str); @@ -338,7 +338,7 @@ SVal Arg = C.getSVal(DE->getArgument()); if (Arg.isUndef()) { StringRef Desc; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT_cxx_delete_undef) @@ -395,7 +395,7 @@ // the function. unsigned Params = FD->getNumParams(); if (Call.getNumArgs() < Params) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -443,7 +443,7 @@ CheckerContext &C) const { SVal recVal = msg.getReceiverSVal(); if (recVal.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { BugType *BT = nullptr; switch (msg.getMessageKind()) { case OCM_Message: @@ -559,7 +559,7 @@ Ctx.LongDoubleTy == CanRetTy || Ctx.LongLongTy == CanRetTy || Ctx.UnsignedLongLongTy == CanRetTy)))) { - if (ExplodedNode *N = C.generateSink(state, nullptr, &Tag)) + if (ExplodedNode *N = C.generateErrorNode(state, &Tag)) emitNilReceiverBug(C, Msg, N); return; } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -131,7 +131,7 @@ if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) return; - if (ExplodedNode *errorNode = C.generateSink()) { + if (ExplodedNode *errorNode = C.generateErrorNode()) { if (!BT) BT.reset(new BuiltinBug(this, "Cast region with wrong size.", "Cast a region whose size is not a multiple" Index: cfe/trunk/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -56,7 +56,7 @@ // Now the cast-to-type is struct pointer, the original type is not void*. if (!OrigPointeeTy->isRecordType()) { - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Cast from non-struct type to struct type", Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -140,7 +140,7 @@ void *const* k = C.getState()->FindGDM(ChrootChecker::getTag()); if (k) if (isRootChanged((intptr_t) *k)) - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT_BreakJail) BT_BreakJail.reset(new BuiltinBug( this, "Break out of jail", "No call of chdir(\"/\") immediately " Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -91,7 +91,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C, bool IsBind) const { // Generate an error node. - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); if (!N) return; @@ -184,7 +184,7 @@ CheckerContext &C) const { // Check for dereference of an undefined value. if (l.isUndef()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_undef) BT_undef.reset( new BuiltinBug(this, "Dereference of undefined pointer value")); @@ -219,7 +219,7 @@ // Otherwise, we have the case where the location could either be // null or not-null. Record the error node as an "implicit" null // dereference. - if (ExplodedNode *N = C.generateSink(nullState)) { + if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), /*IsDirectDereference=*/false}; dispatchEvent(event); @@ -257,7 +257,7 @@ // At this point the value could be either null or non-null. // Record this as an "implicit" null dereference. - if (ExplodedNode *N = C.generateSink(StNull)) { + if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, &C.getBugReporter(), /*IsDirectDereference=*/false}; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -35,7 +35,7 @@ void DivZeroChecker::reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C) const { - if (ExplodedNode *N = C.generateSink(StateZero)) { + if (ExplodedNode *N = C.generateErrorNode(StateZero)) { if (!BT) BT.reset(new BuiltinBug(this, "Division by zero")); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -86,8 +86,7 @@ void ExprInspectionChecker::analyzerEval(const CallExpr *CE, CheckerContext &C) const { - ExplodedNode *N = C.getPredecessor(); - const LocationContext *LC = N->getLocationContext(); + const LocationContext *LC = C.getPredecessor()->getLocationContext(); // A specific instantiation of an inlined function may have more constrained // values than can generally be assumed. Skip the check. @@ -97,24 +96,28 @@ if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; C.emitReport( llvm::make_unique(*BT, getArgumentValueString(CE, C), N)); } void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const { - ExplodedNode *N = C.getPredecessor(); if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; C.emitReport(llvm::make_unique(*BT, "REACHABLE", N)); } void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const { - ExplodedNode *N = C.getPredecessor(); - const LocationContext *LC = N->getLocationContext(); + const LocationContext *LC = C.getPredecessor()->getLocationContext(); // An inlined function could conceivably also be analyzed as a top-level // function. We ignore this case and only emit a message (TRUE or FALSE) @@ -127,6 +130,9 @@ if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; C.emitReport( llvm::make_unique(*BT, getArgumentValueString(CE, C), N)); } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -50,7 +50,7 @@ if (!RV.isConstant() || RV.isZeroConstant()) return; - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Use fixed address", Index: cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -640,7 +640,7 @@ return false; // Generate diagnostic. - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique(*BT, Msg, N); report->addRange(E->getSourceRange()); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -255,7 +255,7 @@ CheckerContext &C) const { ProgramStateRef State = C.getState(); State = State->remove(AP.first); - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; @@ -301,7 +301,7 @@ // Remove the value from the state. The new symbol will be added for // tracking when the second allocator is processed in checkPostStmt(). State = State->remove(V); - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; initBugType(); @@ -364,7 +364,7 @@ if (isEnclosingFunctionParam(ArgExpr)) return; - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; initBugType(); @@ -430,7 +430,7 @@ // report a bad call to free. if (State->assume(ArgSVal.castAs(), false) && !definitelyDidnotReturnError(AS->Region, State, C.getSValBuilder())) { - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); if (!N) return; initBugType(); @@ -584,7 +584,9 @@ } static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); - ExplodedNode *N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); + ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (!N) + return; // Generate the error reports. for (const auto P : Errors) Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -62,7 +62,7 @@ if (!R || !isa(R->getMemorySpace())) return; - ExplodedNode *N = C.generateSink(state); + ExplodedNode *N = C.generateErrorNode(state); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -1592,7 +1592,7 @@ if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_BadFree[*CheckKind]) BT_BadFree[*CheckKind].reset( new BugType(CheckNames[*CheckKind], "Bad free", "Memory Error")); @@ -1637,7 +1637,7 @@ else return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_FreeAlloca[*CheckKind]) BT_FreeAlloca[*CheckKind].reset( new BugType(CheckNames[*CheckKind], "Free alloca()", "Memory Error")); @@ -1661,7 +1661,7 @@ if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_MismatchedDealloc) BT_MismatchedDealloc.reset( new BugType(CheckNames[CK_MismatchedDeallocatorChecker], @@ -1720,7 +1720,7 @@ if (!CheckKind.hasValue()) return; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -1774,7 +1774,7 @@ if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_UseFree[*CheckKind]) BT_UseFree[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Use-after-free", "Memory Error")); @@ -1801,7 +1801,7 @@ if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_DoubleFree[*CheckKind]) BT_DoubleFree[*CheckKind].reset( new BugType(CheckNames[*CheckKind], "Double free", "Memory Error")); @@ -1829,7 +1829,7 @@ if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_DoubleDelete) BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker], "Double delete", "Memory Error")); @@ -1856,7 +1856,7 @@ if (!CheckKind.hasValue()) return; - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_UseZerroAllocated[*CheckKind]) BT_UseZerroAllocated[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Use of zero allocated", "Memory Error")); @@ -2150,10 +2150,12 @@ ExplodedNode *N = C.getPredecessor(); if (!Errors.empty()) { static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak"); - N = C.addTransition(C.getState(), C.getPredecessor(), &Tag); - for (SmallVectorImpl::iterator + N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (N) { + for (SmallVectorImpl::iterator I = Errors.begin(), E = Errors.end(); I != E; ++I) { - reportLeak(*I, N, C); + reportLeak(*I, N, C); + } } } Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -62,7 +62,7 @@ BT.reset(new BugType(this, "Use -drain instead of -release", "API Upgrade (Apple)")); - ExplodedNode *N = C.addTransition(); + ExplodedNode *N = C.generateNonFatalErrorNode(); if (!N) { assert(0); return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -81,7 +81,7 @@ } if (BuildSinks) - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); } void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, @@ -90,7 +90,7 @@ if (const ObjCMethodDecl *MD = Msg.getDecl()) { MD = MD->getCanonicalDecl(); if (MD->hasAttr()) { - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); return; } } @@ -136,7 +136,7 @@ } // If we got here, it's one of the messages we care about. - C.generateSink(); + C.generateSink(C.getState(), C.getPredecessor()); } void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -143,7 +143,7 @@ if (!stateNotNull) { // Generate an error node. Check for a null node in case // we cache out. - if (ExplodedNode *errorNode = C.generateSink(stateNull)) { + if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { std::unique_ptr R; if (haveAttrNonNull) @@ -161,7 +161,7 @@ // Always return. Either we cached out or we just emitted an error. return; } - if (ExplodedNode *N = C.generateSink(stateNull)) { + if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { ImplicitNullDerefEvent event = { V, false, N, &C.getBugReporter(), /*IsDirectDereference=*/haveRefTypeParam}; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -499,7 +499,9 @@ Nullness == NullConstraint::IsNull && StaticNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); - ExplodedNode *N = C.generateSink(State, C.getPredecessor(), &Tag); + ExplodedNode *N = C.generateErrorNode(State, &Tag); + if (!N) + return; reportBugIfPreconditionHolds(ErrorKind::NilReturnedToNonnull, N, nullptr, C, RetExpr); return; @@ -569,7 +571,9 @@ if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && ArgStaticNullability != Nullability::Nonnull && ParamNullability == Nullability::Nonnull) { - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; reportBugIfPreconditionHolds(ErrorKind::NilPassedToNonnull, N, nullptr, C, ArgExpr); return; @@ -891,7 +895,9 @@ ValNullability != Nullability::Nonnull && LocNullability == Nullability::Nonnull) { static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); - ExplodedNode *N = C.generateSink(State, C.getPredecessor(), &Tag); + ExplodedNode *N = C.generateErrorNode(State, &Tag); + if (!N) + return; reportBugIfPreconditionHolds(ErrorKind::NilAssignedToNonnull, N, nullptr, C, S); return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -43,7 +43,7 @@ // Uninitialized value used for the mutex? if (V.getAs()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT_undef) BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " "for @synchronized")); @@ -66,7 +66,7 @@ if (!notNullState) { // Generate an error node. This isn't a sink since // a null mutex just means no synchronization occurs. - if (ExplodedNode *N = C.addTransition(nullState)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) { if (!BT_null) BT_null.reset(new BuiltinBug( this, "Nil value used as mutex for @synchronized() " Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -139,7 +139,7 @@ ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; initBugType(); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -153,7 +153,7 @@ return; // Generate an error node. - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -51,7 +51,7 @@ if (isa(LR) || isa(LR) || isa(LR)) { - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Dangerous pointer arithmetic", Index: cfe/trunk/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -60,7 +60,7 @@ if (isa(BaseLR) || isa(BaseRR)) return; - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "Pointer subtraction", Index: cfe/trunk/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -142,7 +142,7 @@ if (!BT_doublelock) BT_doublelock.reset(new BugType(this, "Double locking", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto report = llvm::make_unique( @@ -204,7 +204,7 @@ if (!BT_doubleunlock) BT_doubleunlock.reset(new BugType(this, "Double unlocking", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique( @@ -227,7 +227,7 @@ if (firstLockR != lockR) { if (!BT_lor) BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto report = llvm::make_unique( @@ -272,7 +272,7 @@ if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Destroy invalid lock", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique(*BT_destroylock, Message, N); @@ -307,7 +307,7 @@ if (!BT_initlock) BT_initlock.reset(new BugType(this, "Init invalid lock", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique(*BT_initlock, Message, N); @@ -320,7 +320,7 @@ if (!BT_destroylock) BT_destroylock.reset(new BugType(this, "Use destroyed lock", "Lock checker")); - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; auto Report = llvm::make_unique( Index: cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/RetainCountChecker.cpp @@ -3306,7 +3306,7 @@ if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None) return; - ExplodedNode *N = C.generateSink(St); + ExplodedNode *N = C.generateErrorNode(St); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -62,7 +62,7 @@ ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); if (StOutBound && !StInBound) { - ExplodedNode *N = C.generateSink(StOutBound); + ExplodedNode *N = C.generateErrorNode(StOutBound); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -80,7 +80,7 @@ static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, const Expr *TrackingE = nullptr) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -200,7 +200,9 @@ State = State->remove(Sym); } - ExplodedNode *N = C.addTransition(State); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; reportLeaks(LeakedStreams, C, N); } @@ -208,7 +210,7 @@ const CallEvent &Call, CheckerContext &C) const { // We reached a bug, stop exploring the path here by generating a sink. - ExplodedNode *ErrNode = C.generateSink(); + ExplodedNode *ErrNode = C.generateErrorNode(); // If we've already reached this node on another path, return. if (!ErrNode) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -94,7 +94,7 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE) const { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; @@ -211,7 +211,7 @@ return; // Generate an error node. - ExplodedNode *N = Ctx.addTransition(state); + ExplodedNode *N = Ctx.generateNonFatalErrorNode(state); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/StreamChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -271,7 +271,7 @@ if (x >= 0 && x <= 2) return; - if (ExplodedNode *N = C.addTransition(state)) { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { if (!BT_illegalwhence) BT_illegalwhence.reset( new BuiltinBug(this, "Illegal whence argument", @@ -349,7 +349,7 @@ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); if (!stateNotNull && stateNull) { - if (ExplodedNode *N = C.generateSink(stateNull)) { + if (ExplodedNode *N = C.generateErrorNode(stateNull)) { if (!BT_nullfp) BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", "Stream pointer might be NULL.")); @@ -378,7 +378,7 @@ // Check: Double close a File Descriptor could cause undefined behaviour. // Conforming to man-pages if (SS->isClosed()) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (N) { if (!BT_doubleclose) BT_doubleclose.reset(new BuiltinBug( @@ -406,7 +406,7 @@ continue; if (SS->isOpened()) { - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (N) { if (!BT_ResourceLeak) BT_ResourceLeak.reset(new BuiltinBug( Index: cfe/trunk/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -48,7 +48,7 @@ return; if (State->isTainted(E, C.getLocationContext())) { - if (ExplodedNode *N = C.addTransition()) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); auto report = llvm::make_unique(*BT, "tainted",N); report->addRange(E->getSourceRange()); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -167,7 +167,7 @@ } void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { - if (ExplodedNode *N = C.generateSink(C.getState())) { + if (ExplodedNode *N = C.generateErrorNode(C.getState())) { if (!DivZeroBug) DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -62,7 +62,7 @@ if (X.isUndef()) { // Generate a sink node, which implicitly marks both outgoing branches as // infeasible. - ExplodedNode *N = Ctx.generateSink(); + ExplodedNode *N = Ctx.generateErrorNode(); if (N) { if (!BT) BT.reset(new BuiltinBug( Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -74,7 +74,7 @@ // Get the VarRegion associated with VD in the local stack frame. if (Optional V = state->getSVal(I.getOriginalRegion()).getAs()) { - if (ExplodedNode *N = C.generateSink()) { + if (ExplodedNode *N = C.generateErrorNode()) { if (!BT) BT.reset( new BuiltinBug(this, "uninitialized variable captured by block")); Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -50,7 +50,7 @@ return; // Generate an error node. - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -46,7 +46,7 @@ if (Ctor->isDefaulted()) return; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; if (!BT) Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -46,7 +46,7 @@ if (C.getCalleeName(EnclosingFunctionDecl) == "swap") return; - ExplodedNode *N = C.generateSink(); + ExplodedNode *N = C.generateErrorNode(); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -77,7 +77,7 @@ ProgramStateRef State, const char *Msg, SourceRange SR) const { - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); if (!N) return; @@ -182,7 +182,7 @@ if (!R || !isa(R->getMemorySpace())) return; - ExplodedNode *N = C.generateSink(state); + ExplodedNode *N = C.generateErrorNode(state); if (!N) return; @@ -231,7 +231,7 @@ ProgramStateRef falseState, const Expr *arg, const char *fn_name) const { - ExplodedNode *N = C.generateSink(falseState); + ExplodedNode *N = C.generateErrorNode(falseState); if (!N) return false; Index: cfe/trunk/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ cfe/trunk/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -46,7 +46,7 @@ ProgramStateRef State, CheckerContext &C) const { // Generate an error node. - ExplodedNode *N = C.generateSink(State); + ExplodedNode *N = C.generateErrorNode(State); if (!N) return; Index: cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp =================================================================== --- cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp +++ cfe/trunk/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -3224,6 +3224,11 @@ void BugReporter::emitReport(std::unique_ptr R) { if (const ExplodedNode *E = R->getErrorNode()) { + // An error node must either be a sink or have a tag, otherwise + // it could get reclaimed before the path diagnostic is created. + assert((E->isSink() || E->getLocation().getTag()) && + "Error node must either be a sink or have a tag"); + const AnalysisDeclContext *DeclCtx = E->getLocationContext()->getAnalysisDeclContext(); // The source of autosynthesized body can be handcrafted AST or a model Index: cfe/trunk/test/Analysis/PR24184.cpp =================================================================== --- cfe/trunk/test/Analysis/PR24184.cpp +++ cfe/trunk/test/Analysis/PR24184.cpp @@ -0,0 +1,97 @@ +// RUN: %clang_cc1 -w -analyze -analyzer-eagerly-assume -fcxx-exceptions -analyzer-checker=core -analyzer-checker=alpha.core.PointerArithm,alpha.core.CastToStruct -analyzer-max-loop 64 -verify %s +// RUN: %clang_cc1 -w -analyze -analyzer-checker=core -analyzer-checker=cplusplus -fcxx-exceptions -analyzer-checker alpha.core.PointerArithm,alpha.core.CastToStruct -analyzer-max-loop 63 -verify %s + +// These tests used to hit an assertion in the bug report. Test case from http://llvm.org/PR24184. +typedef struct { + int cbData; + unsigned pbData; +} CRYPT_DATA_BLOB; + +typedef enum { DT_NONCE_FIXED } DATA_TYPE; +int a; +typedef int *vcreate_t(int *, DATA_TYPE, int, int); +void fn1(unsigned, unsigned) { + char b = 0; + for (; 1; a++, &b + a * 0) // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous}} + ; +} + +vcreate_t fn2; +struct A { + CRYPT_DATA_BLOB value; + int m_fn1() { + int c; + value.pbData == 0; + fn1(0, 0); + } +}; +struct B { + A IkeHashAlg; + A IkeGType; + A NoncePhase1_r; +}; +class C { + int m_fn2(B *); + void m_fn3(B *, int, int, int); +}; +int C::m_fn2(B *p1) { + int *d; + int e = p1->IkeHashAlg.m_fn1(); + unsigned f = p1->IkeGType.m_fn1(), h; + int g; + d = fn2(0, DT_NONCE_FIXED, (char)0, p1->NoncePhase1_r.value.cbData); + h = 0 | 0; + m_fn3(p1, 0, 0, 0); +} + +// case 2: +typedef struct { + int cbData; + unsigned char *pbData; +} CRYPT_DATA_BLOB_1; +typedef unsigned uint32_t; +void fn1_1(void *p1, const void *p2) { p1 != p2; } + +void fn2_1(uint32_t *p1, unsigned char *p2, uint32_t p3) { + unsigned i = 0; + for (0; i < p3; i++) + fn1_1(p1 + i, p2 + i * 0); // expected-warning{{Pointer arithmetic done on non-array variables means reliance on memory layout, which is dangerous}} +} + +struct A_1 { + CRYPT_DATA_BLOB_1 value; + uint32_t m_fn1() { + uint32_t a; + if (value.pbData) + fn2_1(&a, value.pbData, value.cbData); + return 0; + } +}; +struct { + A_1 HashAlgId; +} *b; +void fn3() { + uint32_t c, d; + d = b->HashAlgId.m_fn1(); + d << 0 | 0 | 0; + c = 0; + 0 | 1 << 0 | 0 && b; +} + +// case 3: +struct ST { + char c; +}; +char *p; +int foo1(ST); +int foo2() { + ST *p1 = (ST *)(p); // expected-warning{{Casting a non-structure type to a structure type and accessing a field can lead to memory access errors or data corruption}} + while (p1->c & 0x0F || p1->c & 0x07) + p1 = p1 + foo1(*p1); +} + +int foo3(int *node) { + int i = foo2(); + if (i) + return foo2(); +} Index: cfe/trunk/test/Analysis/malloc.c =================================================================== --- cfe/trunk/test/Analysis/malloc.c +++ cfe/trunk/test/Analysis/malloc.c @@ -1386,7 +1386,9 @@ int *s; char *b = realloc(a->p, size); char *m = realloc(a->p, size); // expected-warning {{Attempt to free released memory}} - return a->p; + // We don't expect a use-after-free for a->P here because the warning above + // is a sink. + return a->p; // no-warning } // We should not warn in this case since the caller will presumably free a->p in all cases.