diff options
Diffstat (limited to 'lib/StaticAnalyzer')
115 files changed, 4273 insertions, 4343 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index 58017acb4a246..8d4793e0802ff 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -75,7 +75,8 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, // reference is outside the range. // Generate a report for this bug. - auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); report->addRange(LoadS->getSourceRange()); C.emitReport(std::move(report)); diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 3bf8a1836b19b..8f3bf138cae4a 100644 --- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -208,7 +208,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, SVal ByteOffset = rawOffset.getByteOffset(); if (isTainted(state, ByteOffset)) { reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted, - llvm::make_unique<TaintBugVisitor>(ByteOffset)); + std::make_unique<TaintBugVisitor>(ByteOffset)); return; } } else if (state_exceedsUpperBound) { @@ -256,7 +256,7 @@ void ArrayBoundCheckerV2::reportOOB( break; } - auto BR = llvm::make_unique<BugReport>(*BT, os.str(), errorNode); + auto BR = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), errorNode); BR->addVisitor(std::move(Visitor)); checkerContext.emitReport(std::move(BR)); } diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index e3fb4c3eb523f..325952fe4ed4d 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -211,7 +211,7 @@ void NilArgChecker::generateBugReport(ExplodedNode *N, if (!BT) BT.reset(new APIMisuse(this, "nil argument")); - auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); R->addRange(Range); bugreporter::trackExpressionValue(N, E, *R); C.emitReport(std::move(R)); @@ -520,7 +520,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE, if (!BT) BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs")); - auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); + auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); report->addRange(CE->getArg(2)->getSourceRange()); C.emitReport(std::move(report)); } @@ -575,7 +575,7 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, OS << "Null pointer argument in call to " << cast<FunctionDecl>(Call.getDecl())->getName(); - auto report = llvm::make_unique<BugReport>(BT, OS.str(), N); + auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N); report->addRange(Call.getArgSourceRange(0)); bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report); C.emitReport(std::move(report)); @@ -635,7 +635,7 @@ void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, "of class '" << Class->getName() << "' and not the class directly"; - auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); + auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); report->addRange(msg.getSourceRange()); C.emitReport(std::move(report)); } @@ -788,7 +788,8 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, ArgTy.print(os, C.getLangOpts()); os << "'"; - auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue()); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), + errorNode.getValue()); R->addRange(msg.getArgSourceRange(I)); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp index 009160fc98157..0eb3c3d1d0e6d 100644 --- a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -126,7 +126,7 @@ bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { - const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent()); + const auto *DRecordDecl = cast<CXXRecordDecl>(Dtor->getDecl()->getParent()); auto IdentifierInfo = DRecordDecl->getIdentifier(); if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) return true; @@ -173,7 +173,8 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection( llvm::raw_string_ostream os(msg); os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() << "' inside of critical section"; - auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>(*BlockInCritSectionBugType, + os.str(), ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(BlockDescSym); C.emitReport(std::move(R)); diff --git a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp index de8763c1b7b57..1423b9c39b266 100644 --- a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -34,7 +34,9 @@ void BoolAssignmentChecker::emitReport(ProgramStateRef 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<BugReport>(*BT, BT->getDescription(), N)); + + C.emitReport( + std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N)); } } diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 44f4530781a8e..503c451670b8d 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -48,10 +48,10 @@ public: DefaultBool CheckCStringBufferOverlap; DefaultBool CheckCStringNotNullTerm; - CheckName CheckNameCStringNullArg; - CheckName CheckNameCStringOutOfBounds; - CheckName CheckNameCStringBufferOverlap; - CheckName CheckNameCStringNotNullTerm; + CheckerNameRef CheckNameCStringNullArg; + CheckerNameRef CheckNameCStringOutOfBounds; + CheckerNameRef CheckNameCStringBufferOverlap; + CheckerNameRef CheckNameCStringNotNullTerm; }; CStringChecksFilter Filter; @@ -198,7 +198,8 @@ public: ProgramStateRef checkNonNull(CheckerContext &C, ProgramStateRef state, const Expr *S, - SVal l) const; + SVal l, + unsigned IdxOfArg) const; ProgramStateRef CheckLocation(CheckerContext &C, ProgramStateRef state, const Expr *S, @@ -277,7 +278,8 @@ CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, ProgramStateRef state, - const Expr *S, SVal l) const { + const Expr *S, SVal l, + unsigned IdxOfArg) const { // If a previous check has failed, propagate the failure. if (!state) return nullptr; @@ -288,11 +290,13 @@ ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, if (stateNull && !stateNonNull) { if (Filter.CheckCStringNullArg) { SmallString<80> buf; - llvm::raw_svector_ostream os(buf); + llvm::raw_svector_ostream OS(buf); assert(CurrentFunctionDescription); - os << "Null pointer argument in call to " << CurrentFunctionDescription; + OS << "Null pointer argument in call to " << CurrentFunctionDescription + << ' ' << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg) + << " parameter"; - emitNullArgBug(C, stateNull, S, os.str()); + emitNullArgBug(C, stateNull, S, OS.str()); } return nullptr; } @@ -384,7 +388,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, // Check that the first buffer is non-null. SVal BufVal = C.getSVal(FirstBuf); - state = checkNonNull(C, state, FirstBuf, BufVal); + state = checkNonNull(C, state, FirstBuf, BufVal, 1); if (!state) return nullptr; @@ -424,7 +428,7 @@ ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, // If there's a second buffer, check it as well. if (SecondBuf) { BufVal = state->getSVal(SecondBuf, LCtx); - state = checkNonNull(C, state, SecondBuf, BufVal); + state = checkNonNull(C, state, SecondBuf, BufVal, 2); if (!state) return nullptr; @@ -566,7 +570,7 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, categories::UnixAPI, "Improper arguments")); // Generate a report for this bug. - auto report = llvm::make_unique<BugReport>( + auto report = std::make_unique<PathSensitiveBugReport>( *BT_Overlap, "Arguments must not be overlapping buffers", N); report->addRange(First->getSourceRange()); report->addRange(Second->getSourceRange()); @@ -583,7 +587,7 @@ void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, "Null pointer argument in call to byte string function")); BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get()); - auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N); Report->addRange(S->getSourceRange()); if (const auto *Ex = dyn_cast<Expr>(S)) bugreporter::trackExpressionValue(N, Ex, *Report); @@ -607,7 +611,7 @@ void CStringChecker::emitOutOfBoundsBug(CheckerContext &C, // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. - auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, WarningMsg, N); Report->addRange(S->getSourceRange()); C.emitReport(std::move(Report)); } @@ -622,7 +626,8 @@ void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, "Argument is not a null-terminated string.")); - auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N); Report->addRange(S->getSourceRange()); C.emitReport(std::move(Report)); @@ -644,7 +649,8 @@ void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, "This expression will create a string whose length is too big to " "be represented as a size_t"; - auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_NotCString, WarningMsg, N); C.emitReport(std::move(Report)); } } @@ -1163,7 +1169,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // Ensure the destination is not null. If it is NULL there will be a // NULL pointer dereference. - state = checkNonNull(C, state, Dest, destVal); + state = checkNonNull(C, state, Dest, destVal, 1); if (!state) return; @@ -1172,7 +1178,7 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, // Ensure the source is not null. If it is NULL there will be a // NULL pointer dereference. - state = checkNonNull(C, state, Source, srcVal); + state = checkNonNull(C, state, Source, srcVal, 2); if (!state) return; @@ -1383,7 +1389,7 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg, LCtx); - state = checkNonNull(C, state, Arg, ArgVal); + state = checkNonNull(C, state, Arg, ArgVal, 1); if (!state) return; @@ -1541,14 +1547,14 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, const Expr *Dst = CE->getArg(0); SVal DstVal = state->getSVal(Dst, LCtx); - state = checkNonNull(C, state, Dst, DstVal); + state = checkNonNull(C, state, Dst, DstVal, 1); if (!state) return; // Check that the source is non-null. const Expr *srcExpr = CE->getArg(1); SVal srcVal = state->getSVal(srcExpr, LCtx); - state = checkNonNull(C, state, srcExpr, srcVal); + state = checkNonNull(C, state, srcExpr, srcVal, 2); if (!state) return; @@ -1904,14 +1910,14 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, // Check that the first string is non-null const Expr *s1 = CE->getArg(0); SVal s1Val = state->getSVal(s1, LCtx); - state = checkNonNull(C, state, s1, s1Val); + state = checkNonNull(C, state, s1, s1Val, 1); if (!state) return; // Check that the second string is non-null. const Expr *s2 = CE->getArg(1); SVal s2Val = state->getSVal(s2, LCtx); - state = checkNonNull(C, state, s2, s2Val); + state = checkNonNull(C, state, s2, s2Val, 2); if (!state) return; @@ -2038,14 +2044,14 @@ void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { // Check that the search string pointer is non-null (though it may point to // a null string). SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx); - State = checkNonNull(C, State, SearchStrPtr, SearchStrVal); + State = checkNonNull(C, State, SearchStrPtr, SearchStrVal, 1); if (!State) return; // Check that the delimiter string is non-null. const Expr *DelimStr = CE->getArg(1); SVal DelimStrVal = State->getSVal(DelimStr, LCtx); - State = checkNonNull(C, State, DelimStr, DelimStrVal); + State = checkNonNull(C, State, DelimStr, DelimStrVal, 2); if (!State) return; @@ -2148,7 +2154,7 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { // Ensure the memory area is not null. // If it is NULL there will be a NULL pointer dereference. - State = checkNonNull(C, StateNonZeroSize, Mem, MemVal); + State = checkNonNull(C, StateNonZeroSize, Mem, MemVal, 1); if (!State) return; @@ -2195,7 +2201,7 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { // Ensure the memory area is not null. // If it is NULL there will be a NULL pointer dereference. - State = checkNonNull(C, StateNonZeroSize, Mem, MemVal); + State = checkNonNull(C, StateNonZeroSize, Mem, MemVal, 1); if (!State) return; @@ -2403,14 +2409,12 @@ bool ento::shouldRegisterCStringModeling(const LangOptions &LO) { void ento::register##name(CheckerManager &mgr) { \ CStringChecker *checker = mgr.getChecker<CStringChecker>(); \ checker->Filter.Check##name = true; \ - checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ } \ \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } + bool ento::shouldRegister##name(const LangOptions &LO) { return true; } - REGISTER_CHECKER(CStringNullArg) - REGISTER_CHECKER(CStringOutOfBounds) - REGISTER_CHECKER(CStringBufferOverlap) +REGISTER_CHECKER(CStringNullArg) +REGISTER_CHECKER(CStringOutOfBounds) +REGISTER_CHECKER(CStringBufferOverlap) REGISTER_CHECKER(CStringNotNullTerm) diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp index b828ac0592363..d84fcc69a4925 100644 --- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -156,14 +156,21 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { const Expr *DstArg = CE->getArg(0); const Expr *LenArg = CE->getArg(2); - const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts()); - const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts()); + const auto *DstArgDRE = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts()); + const auto *LenArgDRE = + dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts()); uint64_t DstOff = 0; if (isSizeof(LenArg, DstArg)) return false; + // - size_t dstlen = sizeof(dst) - if (LenArgDecl) { - const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl()); + if (LenArgDRE) { + const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDRE->getDecl()); + // If it's an EnumConstantDecl instead, then we're missing out on something. + if (!LenArgVal) { + assert(isa<EnumConstantDecl>(LenArgDRE->getDecl())); + return false; + } if (LenArgVal->getInit()) LenArg = LenArgVal->getInit(); } @@ -177,9 +184,10 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { // Case when there is pointer arithmetic on the destination buffer // especially when we offset from the base decreasing the // buffer length accordingly. - if (!DstArgDecl) { - if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) { - DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts()); + if (!DstArgDRE) { + if (const auto *BE = + dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) { + DstArgDRE = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts()); if (BE->getOpcode() == BO_Add) { if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) { DstOff = IL->getValue().getZExtValue(); @@ -187,8 +195,9 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { } } } - if (DstArgDecl) { - if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) { + if (DstArgDRE) { + if (const auto *Buffer = + dyn_cast<ConstantArrayType>(DstArgDRE->getType())) { ASTContext &C = BR.getContext(); uint64_t BufferLen = C.getTypeSize(Buffer) / 8; auto RemainingBufferLen = BufferLen - DstOff; diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 5a7eba0760fe5..2fcb765cd4eef 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -49,7 +49,7 @@ class CallAndMessageChecker public: DefaultBool Check_CallAndMessageUnInitRefArg; - CheckName CheckName_CallAndMessageUnInitRefArg; + CheckerNameRef CheckName_CallAndMessageUnInitRefArg; void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; @@ -95,7 +95,7 @@ void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, if (!N) return; - auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); if (BadE) { R->addRange(BadE->getSourceRange()); if (BadE->isGLValue()) @@ -175,7 +175,7 @@ bool CallAndMessageChecker::uninitRefOrPointer( if (PSV.isUndef()) { if (ExplodedNode *N = C.generateErrorNode()) { LazyInit_BT(BD, BT); - auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Os.str(), N); R->addRange(ArgRange); if (ArgEx) bugreporter::trackExpressionValue(N, ArgEx, *R); @@ -252,7 +252,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, SmallString<200> Buf; llvm::raw_svector_ostream Os(Buf); describeUninitializedArgumentInCall(Call, ArgumentNumber, Os); - auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Os.str(), N); R->addRange(ArgRange); if (ArgEx) @@ -295,7 +295,7 @@ bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, } // Generate a report for this bug. - auto R = llvm::make_unique<BugReport>(*BT, os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); R->addRange(ArgRange); if (ArgEx) @@ -358,7 +358,7 @@ void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE, else Desc = "Argument to 'delete' is uninitialized"; BugType *BT = BT_cxx_delete_undef.get(); - auto R = llvm::make_unique<BugReport>(*BT, Desc, N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Desc, N); bugreporter::trackExpressionValue(N, DE, *R); C.emitReport(std::move(R)); return; @@ -420,8 +420,8 @@ void CallAndMessageChecker::checkPreCall(const CallEvent &Call, << (Params == 1 ? "" : "s") << " is called with fewer (" << Call.getNumArgs() << ")"; - C.emitReport( - llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N)); + C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT_call_few_args, + os.str(), N)); } } @@ -482,7 +482,7 @@ void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, } assert(BT && "Unknown message kind."); - auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); const ObjCMessageExpr *ME = msg.getOriginExpr(); R->addRange(ME->getReceiverRange()); @@ -525,7 +525,8 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, os << "' that will be garbage"; } - auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT_msg_ret, os.str(), N); report->addRange(ME->getReceiverRange()); // FIXME: This won't track "self" in messages to super. if (const Expr *receiver = ME->getInstanceReceiver()) { @@ -611,7 +612,7 @@ bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) { void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) { CallAndMessageChecker *Checker = mgr.getChecker<CallAndMessageChecker>(); Checker->Check_CallAndMessageUnInitRefArg = true; - Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckName(); + Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckerName(); } bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) { diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 05ece961467fb..51c1d4409929e 100644 --- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -132,7 +132,8 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { BT.reset(new BuiltinBug(this, "Cast region with wrong size.", "Cast a region whose size is not a multiple" " of the destination type size.")); - auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), + errorNode); R->addRange(CE->getSourceRange()); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index ff5d12c27c69f..cc1c9a66b90e2 100644 --- a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -6,178 +6,429 @@ // //===----------------------------------------------------------------------===// // -// This defines CastValueChecker which models casts of custom RTTIs. +// This defines CastValueChecker which models casts of custom RTTIs. +// +// TODO list: +// - It only allows one succesful cast between two types however in the wild +// the object could be casted to multiple types. +// - It needs to check the most likely type information from the dynamic type +// map to increase precision of dynamic casting. // //===----------------------------------------------------------------------===// +#include "clang/AST/DeclTemplate.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "llvm/ADT/Optional.h" +#include <utility> using namespace clang; using namespace ento; namespace { class CastValueChecker : public Checker<eval::Call> { + enum class CallKind { Function, Method, InstanceOf }; + using CastCheck = - std::function<void(const CastValueChecker *, const CallExpr *, + std::function<void(const CastValueChecker *, const CallEvent &Call, DefinedOrUnknownSVal, CheckerContext &)>; public: - // We have three cases to evaluate a cast: - // 1) The parameter is non-null, the return value is non-null - // 2) The parameter is non-null, the return value is null - // 3) The parameter is null, the return value is null - // + // We have five cases to evaluate a cast: + // 1) The parameter is non-null, the return value is non-null. + // 2) The parameter is non-null, the return value is null. + // 3) The parameter is null, the return value is null. // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. + // + // 4) castAs: Has no parameter, the return value is non-null. + // 5) getAs: Has no parameter, the return value is null or non-null. + // + // We have two cases to check the parameter is an instance of the given type. + // 1) isa: The parameter is non-null, returns boolean. + // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean. bool evalCall(const CallEvent &Call, CheckerContext &C) const; private: - // These are known in the LLVM project. - const CallDescriptionMap<CastCheck> CDM = { - {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast}, - {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast}, - {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull}, + // These are known in the LLVM project. The pairs are in the following form: + // {{{namespace, call}, argument-count}, {callback, kind}} + const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = { + {{{"llvm", "cast"}, 1}, + {&CastValueChecker::evalCast, CallKind::Function}}, + {{{"llvm", "dyn_cast"}, 1}, + {&CastValueChecker::evalDynCast, CallKind::Function}}, + {{{"llvm", "cast_or_null"}, 1}, + {&CastValueChecker::evalCastOrNull, CallKind::Function}}, {{{"llvm", "dyn_cast_or_null"}, 1}, - &CastValueChecker::evalDynCastOrNull}}; - - void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + {&CastValueChecker::evalDynCastOrNull, CallKind::Function}}, + {{{"clang", "castAs"}, 0}, + {&CastValueChecker::evalCastAs, CallKind::Method}}, + {{{"clang", "getAs"}, 0}, + {&CastValueChecker::evalGetAs, CallKind::Method}}, + {{{"llvm", "isa"}, 1}, + {&CastValueChecker::evalIsa, CallKind::InstanceOf}}, + {{{"llvm", "isa_and_nonnull"}, 1}, + {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}}; + + void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const; - void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const; - void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const; - void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const; + void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const; + void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const; + void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const; + void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const; }; } // namespace -static std::string getCastName(const Expr *Cast) { - return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString(); +static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, + bool CastSucceeds) { + if (!CastInfo) + return false; + + return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds(); } -static void evalNonNullParamNonNullReturn(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, - CheckerContext &C) { - ProgramStateRef State = C.getState()->assume(ParamDV, true); - if (!State) - return; +static const NoteTag *getNoteTag(CheckerContext &C, + const DynamicCastInfo *CastInfo, + QualType CastToTy, const Expr *Object, + bool CastSucceeds, bool IsKnownCast) { + std::string CastToName = + CastInfo ? CastInfo->to()->getPointeeCXXRecordDecl()->getNameAsString() + : CastToTy->getPointeeCXXRecordDecl()->getNameAsString(); + Object = Object->IgnoreParenImpCasts(); + + return C.getNoteTag( + [=]() -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); - State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false); + if (!IsKnownCast) + Out << "Assuming "; - std::string CastFromName = getCastName(CE->getArg(0)); - std::string CastToName = getCastName(CE); + if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { + Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; + } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) { + Out << (IsKnownCast ? "Field '" : "field '") + << ME->getMemberDecl()->getNameAsString() << '\''; + } else { + Out << (IsKnownCast ? "The object" : "the object"); + } - const NoteTag *CastTag = C.getNoteTag( - [CastFromName, CastToName](BugReport &) -> std::string { - SmallString<128> Msg; - llvm::raw_svector_ostream Out(Msg); + Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName + << '\''; - Out << "Assuming dynamic cast from '" << CastFromName << "' to '" - << CastToName << "' succeeds"; return Out.str(); }, /*IsPrunable=*/true); +} - C.addTransition(State, CastTag); +//===----------------------------------------------------------------------===// +// Main logic to evaluate a cast. +//===----------------------------------------------------------------------===// + +static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards, + ASTContext &ACtx) { + if (alignTowards->isLValueReferenceType() && + alignTowards.isConstQualified()) { + toAlign.addConst(); + return ACtx.getLValueReferenceType(toAlign); + } else if (alignTowards->isLValueReferenceType()) + return ACtx.getLValueReferenceType(toAlign); + else if (alignTowards->isRValueReferenceType()) + return ACtx.getRValueReferenceType(toAlign); + + llvm_unreachable("Must align towards a reference type!"); } -static void evalNonNullParamNullReturn(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, - CheckerContext &C) { - ProgramStateRef State = C.getState()->assume(ParamDV, true); +static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C, bool IsNonNullParam, + bool IsNonNullReturn, + bool IsCheckedCast = false) { + ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam); if (!State) return; - State = State->BindExpr(CE, C.getLocationContext(), - C.getSValBuilder().makeNull(), false); + const Expr *Object; + QualType CastFromTy; + QualType CastToTy = Call.getResultType(); + + if (Call.getNumArgs() > 0) { + Object = Call.getArgExpr(0); + CastFromTy = Call.parameters()[0]->getType(); + } else { + Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr(); + CastFromTy = Object->getType(); + if (CastToTy->isPointerType()) { + if (!CastFromTy->isPointerType()) + return; + } else { + if (!CastFromTy->isReferenceType()) + return; + + CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext()); + } + } + + const MemRegion *MR = DV.getAsRegion(); + const DynamicCastInfo *CastInfo = + getDynamicCastInfo(State, MR, CastFromTy, CastToTy); + + // We assume that every checked cast succeeds. + bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy; + if (!CastSucceeds) { + if (CastInfo) + CastSucceeds = IsNonNullReturn && CastInfo->succeeds(); + else + CastSucceeds = IsNonNullReturn; + } + + // Check for infeasible casts. + if (isInfeasibleCast(CastInfo, CastSucceeds)) { + C.generateSink(State, C.getPredecessor()); + return; + } + + // Store the type and the cast information. + bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy; + if (!IsKnownCast || IsCheckedCast) + State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, + CastSucceeds); + + SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy) + : C.getSValBuilder().makeNull(); + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false), + getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); +} - std::string CastFromName = getCastName(CE->getArg(0)); - std::string CastToName = getCastName(CE); +static void addInstanceOfTransition(const CallEvent &Call, + DefinedOrUnknownSVal DV, + ProgramStateRef State, CheckerContext &C, + bool IsInstanceOf) { + const FunctionDecl *FD = Call.getDecl()->getAsFunction(); + QualType CastFromTy = Call.parameters()[0]->getType(); + QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType(); + if (CastFromTy->isPointerType()) + CastToTy = C.getASTContext().getPointerType(CastToTy); + else if (CastFromTy->isReferenceType()) + CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext()); + else + return; - const NoteTag *CastTag = C.getNoteTag( - [CastFromName, CastToName](BugReport &) -> std::string { - SmallString<128> Msg; - llvm::raw_svector_ostream Out(Msg); + const MemRegion *MR = DV.getAsRegion(); + const DynamicCastInfo *CastInfo = + getDynamicCastInfo(State, MR, CastFromTy, CastToTy); - Out << "Assuming dynamic cast from '" << CastFromName << "' to '" - << CastToName << "' fails"; - return Out.str(); - }, - /*IsPrunable=*/true); + bool CastSucceeds; + if (CastInfo) + CastSucceeds = IsInstanceOf && CastInfo->succeeds(); + else + CastSucceeds = IsInstanceOf || CastFromTy == CastToTy; - C.addTransition(State, CastTag); + if (isInfeasibleCast(CastInfo, CastSucceeds)) { + C.generateSink(State, C.getPredecessor()); + return; + } + + // Store the type and the cast information. + bool IsKnownCast = CastInfo || CastFromTy == CastToTy; + if (!IsKnownCast) + State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, + IsInstanceOf); + + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(CastSucceeds)), + getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds, + IsKnownCast)); } -static void evalNullParamNullReturn(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, - CheckerContext &C) { - ProgramStateRef State = C.getState()->assume(ParamDV, false); - if (!State) - return; +//===----------------------------------------------------------------------===// +// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. +//===----------------------------------------------------------------------===// - State = State->BindExpr(CE, C.getLocationContext(), - C.getSValBuilder().makeNull(), false); +static void evalNonNullParamNonNullReturn(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C, + bool IsCheckedCast = false) { + addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, + /*IsNonNullReturn=*/true, IsCheckedCast); +} - const NoteTag *CastTag = - C.getNoteTag("Assuming null pointer is passed into cast", - /*IsPrunable=*/true); +static void evalNonNullParamNullReturn(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) { + addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, + /*IsNonNullReturn=*/false); +} - C.addTransition(State, CastTag); +static void evalNullParamNullReturn(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) { + if (ProgramStateRef State = C.getState()->assume(DV, false)) + C.addTransition(State->BindExpr(Call.getOriginExpr(), + C.getLocationContext(), + C.getSValBuilder().makeNull(), false), + C.getNoteTag("Assuming null pointer is passed into cast", + /*IsPrunable=*/true)); } -void CastValueChecker::evalCast(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, +void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const { - evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); } -void CastValueChecker::evalDynCast(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, +void CastValueChecker::evalDynCast(const CallEvent &Call, + DefinedOrUnknownSVal DV, CheckerContext &C) const { - evalNonNullParamNonNullReturn(CE, ParamDV, C); - evalNonNullParamNullReturn(CE, ParamDV, C); + evalNonNullParamNonNullReturn(Call, DV, C); + evalNonNullParamNullReturn(Call, DV, C); } -void CastValueChecker::evalCastOrNull(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, +void CastValueChecker::evalCastOrNull(const CallEvent &Call, + DefinedOrUnknownSVal DV, CheckerContext &C) const { - evalNonNullParamNonNullReturn(CE, ParamDV, C); - evalNullParamNullReturn(CE, ParamDV, C); + evalNonNullParamNonNullReturn(Call, DV, C); + evalNullParamNullReturn(Call, DV, C); } -void CastValueChecker::evalDynCastOrNull(const CallExpr *CE, - DefinedOrUnknownSVal ParamDV, +void CastValueChecker::evalDynCastOrNull(const CallEvent &Call, + DefinedOrUnknownSVal DV, CheckerContext &C) const { - evalNonNullParamNonNullReturn(CE, ParamDV, C); - evalNonNullParamNullReturn(CE, ParamDV, C); - evalNullParamNullReturn(CE, ParamDV, C); + evalNonNullParamNonNullReturn(Call, DV, C); + evalNonNullParamNullReturn(Call, DV, C); + evalNullParamNullReturn(Call, DV, C); } -bool CastValueChecker::evalCall(const CallEvent &Call, - CheckerContext &C) const { - const CastCheck *Check = CDM.lookup(Call); - if (!Check) - return false; +//===----------------------------------------------------------------------===// +// Evaluating castAs, getAs. +//===----------------------------------------------------------------------===// - const auto *CE = cast<CallExpr>(Call.getOriginExpr()); - if (!CE) - return false; +static void evalZeroParamNonNullReturn(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C, + bool IsCheckedCast = false) { + addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, + /*IsNonNullReturn=*/true, IsCheckedCast); +} + +static void evalZeroParamNullReturn(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) { + addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, + /*IsNonNullReturn=*/false); +} + +void CastValueChecker::evalCastAs(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) const { + evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); +} + +void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const { + evalZeroParamNonNullReturn(Call, DV, C); + evalZeroParamNullReturn(Call, DV, C); +} - // If we cannot obtain both of the classes we cannot be sure how to model it. - if (!CE->getType()->getPointeeCXXRecordDecl() || - !CE->getArg(0)->getType()->getPointeeCXXRecordDecl()) +//===----------------------------------------------------------------------===// +// Evaluating isa, isa_and_nonnull. +//===----------------------------------------------------------------------===// + +void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const { + ProgramStateRef NonNullState, NullState; + std::tie(NonNullState, NullState) = C.getState()->assume(DV); + + if (NonNullState) { + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); + } + + if (NullState) { + C.generateSink(NullState, C.getPredecessor()); + } +} + +void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) const { + ProgramStateRef NonNullState, NullState; + std::tie(NonNullState, NullState) = C.getState()->assume(DV); + + if (NonNullState) { + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); + } + + if (NullState) { + addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false); + } +} + +//===----------------------------------------------------------------------===// +// Main logic to evaluate a call. +//===----------------------------------------------------------------------===// + +bool CastValueChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *Lookup = CDM.lookup(Call); + if (!Lookup) return false; - SVal ParamV = Call.getArgSVal(0); - auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>(); - if (!ParamDV) + const CastCheck &Check = Lookup->first; + CallKind Kind = Lookup->second; + + Optional<DefinedOrUnknownSVal> DV; + + switch (Kind) { + case CallKind::Function: { + // We only model casts from pointers to pointers or from references + // to references. Other casts are most likely specialized and we + // cannot model them. + QualType ParamT = Call.parameters()[0]->getType(); + QualType ResultT = Call.getResultType(); + if (!(ParamT->isPointerType() && ResultT->isPointerType()) && + !(ParamT->isReferenceType() && ResultT->isReferenceType())) + return false; + + DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); + break; + } + case CallKind::InstanceOf: { + // We need to obtain the only template argument to determinte the type. + const FunctionDecl *FD = Call.getDecl()->getAsFunction(); + if (!FD || !FD->getTemplateSpecializationArgs()) + return false; + + DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); + break; + } + case CallKind::Method: + const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); + if (!InstanceCall) + return false; + + DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>(); + break; + } + + if (!DV) return false; - (*Check)(this, CE, *ParamDV, C); + Check(this, Call, *DV, C); return true; } diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index a7ca814c8f967..50b872bd8682f 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -28,6 +28,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -36,7 +37,6 @@ #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" @@ -576,9 +576,8 @@ void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { OS << " by a synthesized property but not released" " before '[super dealloc]'"; - std::unique_ptr<BugReport> BR( - new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); - + auto BR = std::make_unique<PathSensitiveBugReport>(*MissingReleaseBugType, + OS.str(), ErrNode); C.emitReport(std::move(BR)); } @@ -699,8 +698,8 @@ bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, OS << " property but was released in 'dealloc'"; } - std::unique_ptr<BugReport> BR( - new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); + auto BR = std::make_unique<PathSensitiveBugReport>(*ExtraReleaseBugType, + OS.str(), ErrNode); BR->addRange(M.getOriginExpr()->getSourceRange()); C.emitReport(std::move(BR)); @@ -741,8 +740,8 @@ bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, OS << "'" << *PropImpl->getPropertyIvarDecl() << "' should be released rather than deallocated"; - std::unique_ptr<BugReport> BR( - new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode)); + auto BR = std::make_unique<PathSensitiveBugReport>(*MistakenDeallocBugType, + OS.str(), ErrNode); BR->addRange(M.getOriginExpr()->getSourceRange()); C.emitReport(std::move(BR)); diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index a020d33bfd95f..1694c237cda42 100644 --- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -13,11 +13,11 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 3f1c213a56478..260a2896e78c8 100644 --- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -50,19 +50,19 @@ struct ChecksFilter { DefaultBool check_FloatLoopCounter; DefaultBool check_UncheckedReturn; - CheckName checkName_bcmp; - CheckName checkName_bcopy; - CheckName checkName_bzero; - CheckName checkName_gets; - CheckName checkName_getpw; - CheckName checkName_mktemp; - CheckName checkName_mkstemp; - CheckName checkName_strcpy; - CheckName checkName_DeprecatedOrUnsafeBufferHandling; - CheckName checkName_rand; - CheckName checkName_vfork; - CheckName checkName_FloatLoopCounter; - CheckName checkName_UncheckedReturn; + CheckerNameRef checkName_bcmp; + CheckerNameRef checkName_bcopy; + CheckerNameRef checkName_bzero; + CheckerNameRef checkName_gets; + CheckerNameRef checkName_getpw; + CheckerNameRef checkName_mktemp; + CheckerNameRef checkName_mkstemp; + CheckerNameRef checkName_strcpy; + CheckerNameRef checkName_DeprecatedOrUnsafeBufferHandling; + CheckerNameRef checkName_rand; + CheckerNameRef checkName_vfork; + CheckerNameRef checkName_FloatLoopCounter; + CheckerNameRef checkName_UncheckedReturn; }; class WalkAST : public StmtVisitor<WalkAST> { @@ -204,6 +204,8 @@ void WalkAST::VisitForStmt(ForStmt *FS) { // Implements: CERT security coding advisory FLP-30. //===----------------------------------------------------------------------===// +// Returns either 'x' or 'y', depending on which one of them is incremented +// in 'expr', or nullptr if none of them is incremented. static const DeclRefExpr* getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { expr = expr->IgnoreParenCasts(); @@ -289,14 +291,15 @@ void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { // Does either variable appear in increment? const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS); - if (!drInc) return; + const VarDecl *vdInc = cast<VarDecl>(drInc->getDecl()); + assert(vdInc && (vdInc == vdLHS || vdInc == vdRHS)); + // Emit the error. First figure out which DeclRefExpr in the condition // referenced the compared variable. - assert(drInc->getDecl()); - const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; + const DeclRefExpr *drCond = vdLHS == vdInc ? drLHS : drRHS; SmallVector<SourceRange, 2> ranges; SmallString<256> sbuf; @@ -1012,14 +1015,12 @@ bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) { #define REGISTER_CHECKER(name) \ void ento::register##name(CheckerManager &mgr) { \ - SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ + SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \ checker->filter.check_##name = true; \ - checker->filter.checkName_##name = mgr.getCurrentCheckName(); \ + checker->filter.checkName_##name = mgr.getCurrentCheckerName(); \ } \ \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } + bool ento::shouldRegister##name(const LangOptions &LO) { return true; } REGISTER_CHECKER(bcmp) REGISTER_CHECKER(bcopy) diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index 9fffedfccd871..7a41a7b6b216e 100644 --- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -127,7 +127,7 @@ void ChrootChecker::checkPreCall(const CallEvent &Call, BT_BreakJail.reset(new BuiltinBug( this, "Break out of jail", "No call of chdir(\"/\") immediately " "after chroot")); - C.emitReport(llvm::make_unique<BugReport>( + C.emitReport(std::make_unique<PathSensitiveBugReport>( *BT_BreakJail, BT_BreakJail->getDescription(), N)); } } diff --git a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp index 4fc225056d4c7..ce45b5be34c9f 100644 --- a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp @@ -114,8 +114,8 @@ void CloneChecker::reportClones( for (const CloneDetector::CloneGroup &Group : CloneGroups) { // We group the clones by printing the first as a warning and all others // as a note. - auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected", - makeLocation(Group.front(), Mgr)); + auto R = std::make_unique<BasicBugReport>( + *BT_Exact, "Duplicate code detected", makeLocation(Group.front(), Mgr)); R->addRange(Group.front().getSourceRange()); for (unsigned i = 1; i < Group.size(); ++i) @@ -169,7 +169,7 @@ void CloneChecker::reportSuspiciousClones( // which may confuse the user. // Think how to perform more accurate suggestions? - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<BasicBugReport>( *BT_Suspicious, "Potential copy-paste error; did you really mean to use '" + Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?", diff --git a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp index 5058d101b8e59..8dd3132f07e2b 100644 --- a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -121,7 +121,7 @@ void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C, new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); // Generate a report for this bug. - auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index d5baa2bcba6fc..61441889fc649 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" @@ -119,11 +120,20 @@ LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { } namespace { +class DeadStoresChecker : public Checker<check::ASTCodeBody> { +public: + bool ShowFixIts = false; + bool WarnForDeadNestedAssignments = true; + + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +}; + class DeadStoreObs : public LiveVariables::Observer { const CFG &cfg; ASTContext &Ctx; BugReporter& BR; - const CheckerBase *Checker; + const DeadStoresChecker *Checker; AnalysisDeclContext* AC; ParentMap& Parents; llvm::SmallPtrSet<const VarDecl*, 20> Escaped; @@ -135,9 +145,10 @@ class DeadStoreObs : public LiveVariables::Observer { public: DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br, - const CheckerBase *checker, AnalysisDeclContext *ac, + const DeadStoresChecker *checker, AnalysisDeclContext *ac, ParentMap &parents, - llvm::SmallPtrSet<const VarDecl *, 20> &escaped) + llvm::SmallPtrSet<const VarDecl *, 20> &escaped, + bool warnForDeadNestedAssignments) : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents), Escaped(escaped), currentBlock(nullptr) {} @@ -202,12 +213,32 @@ public: llvm::raw_svector_ostream os(buf); const char *BugType = nullptr; + SmallVector<FixItHint, 1> Fixits; + switch (dsk) { - case DeadInit: + case DeadInit: { BugType = "Dead initialization"; os << "Value stored to '" << *V << "' during its initialization is never read"; + + ASTContext &ACtx = V->getASTContext(); + if (Checker->ShowFixIts) { + if (V->getInit()->HasSideEffects(ACtx, + /*IncludePossibleEffects=*/true)) { + break; + } + SourceManager &SM = ACtx.getSourceManager(); + const LangOptions &LO = ACtx.getLangOpts(); + SourceLocation L1 = + Lexer::findNextToken( + V->getTypeSourceInfo()->getTypeLoc().getEndLoc(), + SM, LO)->getEndLoc(); + SourceLocation L2 = + Lexer::getLocForEndOfToken(V->getInit()->getEndLoc(), 1, SM, LO); + Fixits.push_back(FixItHint::CreateRemoval({L1, L2})); + } break; + } case DeadIncrement: BugType = "Dead increment"; @@ -217,15 +248,20 @@ public: os << "Value stored to '" << *V << "' is never read"; break; + // eg.: f((x = foo())) case Enclosing: - // Don't report issues in this case, e.g.: "if (x = foo())", - // where 'x' is unused later. We have yet to see a case where - // this is a real bug. - return; + if (!Checker->WarnForDeadNestedAssignments) + return; + BugType = "Dead nested assignment"; + os << "Although the value stored to '" << *V + << "' is used in the enclosing expression, the value is never " + "actually read from '" + << *V << "'"; + break; } BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(), - L, R); + L, R, Fixits); } void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val, @@ -471,35 +507,37 @@ public: // DeadStoresChecker //===----------------------------------------------------------------------===// -namespace { -class DeadStoresChecker : public Checker<check::ASTCodeBody> { -public: - void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, - BugReporter &BR) const { - - // Don't do anything for template instantiations. - // Proving that code in a template instantiation is "dead" - // means proving that it is dead in all instantiations. - // This same problem exists with -Wunreachable-code. - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) - if (FD->isTemplateInstantiation()) - return; +void DeadStoresChecker::checkASTCodeBody(const Decl *D, AnalysisManager &mgr, + BugReporter &BR) const { - if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) { - CFG &cfg = *mgr.getCFG(D); - AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D); - ParentMap &pmap = mgr.getParentMap(D); - FindEscaped FS; - cfg.VisitBlockStmts(FS); - DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped); - L->runOnAllBlocks(A); - } + // Don't do anything for template instantiations. + // Proving that code in a template instantiation is "dead" + // means proving that it is dead in all instantiations. + // This same problem exists with -Wunreachable-code. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FD->isTemplateInstantiation()) + return; + + if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) { + CFG &cfg = *mgr.getCFG(D); + AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D); + ParentMap &pmap = mgr.getParentMap(D); + FindEscaped FS; + cfg.VisitBlockStmts(FS); + DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped, + WarnForDeadNestedAssignments); + L->runOnAllBlocks(A); } -}; } -void ento::registerDeadStoresChecker(CheckerManager &mgr) { - mgr.registerChecker<DeadStoresChecker>(); +void ento::registerDeadStoresChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.registerChecker<DeadStoresChecker>(); + + const AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions(); + Chk->WarnForDeadNestedAssignments = + AnOpts.getCheckerBooleanOption(Chk, "WarnForDeadNestedAssignments"); + Chk->ShowFixIts = + AnOpts.getCheckerBooleanOption(Chk, "ShowFixIts"); } bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) { diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp index bb9e8110b6479..0cb4be2c7fdc3 100644 --- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp +++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -333,7 +333,8 @@ public: if (!Node) return; - auto Report = llvm::make_unique<BugReport>(BT_stmtLoc, "Statement", Node); + auto Report = + std::make_unique<PathSensitiveBugReport>(BT_stmtLoc, "Statement", Node); C.emitReport(std::move(Report)); } diff --git a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp index 8bf77c109f8a6..45c1984c5e157 100644 --- a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp @@ -27,7 +27,7 @@ #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" using namespace clang; @@ -45,9 +45,9 @@ class DeleteWithNonVirtualDtorChecker static int X = 0; ID.AddPointer(&X); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: bool Satisfied; @@ -92,23 +92,23 @@ void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, "Logic error")); ExplodedNode *N = C.generateNonFatalErrorNode(); - auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); // Mark region of problematic base class for later use in the BugVisitor. R->markInteresting(BaseClassRegion); - R->addVisitor(llvm::make_unique<DeleteBugVisitor>()); + R->addVisitor(std::make_unique<DeleteBugVisitor>()); C.emitReport(std::move(R)); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( const ExplodedNode *N, BugReporterContext &BRC, - BugReport &BR) { + PathSensitiveBugReport &BR) { // Stop traversal after the first conversion was found on a path. if (Satisfied) return nullptr; - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; @@ -140,8 +140,7 @@ DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( OS << "Conversion from derived to base happened here"; PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, - nullptr); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); } void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 2c264833f2a9f..e3de0b4f4a7ff 100644 --- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -179,7 +179,7 @@ void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, break; } - auto report = llvm::make_unique<BugReport>( + auto report = std::make_unique<PathSensitiveBugReport>( *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); @@ -200,8 +200,8 @@ void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, BT_undef.reset( new BuiltinBug(this, "Dereference of undefined pointer value")); - auto report = - llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); + auto report = std::make_unique<PathSensitiveBugReport>( + *BT_undef, BT_undef->getDescription(), N); bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); C.emitReport(std::move(report)); } diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 33e8fcd8af7bb..8798bde88dcd6 100644 --- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -47,7 +47,7 @@ void DivZeroChecker::reportBug( if (!BT) BT.reset(new BuiltinBug(this, "Division by zero")); - auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); R->addVisitor(std::move(Visitor)); bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); C.emitReport(std::move(R)); @@ -88,7 +88,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B, bool TaintedD = isTainted(C.getState(), *DV); if ((stateNotZero && stateZero && TaintedD)) { reportBug("Division by a tainted value, possibly zero", stateZero, C, - llvm::make_unique<taint::TaintBugVisitor>(*DV)); + std::make_unique<taint::TaintBugVisitor>(*DV)); return; } diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp index 4d979dc9f2400..8cc38f9735f33 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp @@ -21,7 +21,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" @@ -47,9 +47,9 @@ class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> { ID.AddPointer(Reg); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: // The tracked region. @@ -80,18 +80,16 @@ void DynamicTypeChecker::reportTypeError(QualType DynamicType, QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); OS << "'"; - std::unique_ptr<BugReport> R( - new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode())); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT, OS.str(), C.generateNonFatalErrorNode()); R->markInteresting(Reg); - R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg)); + R->addVisitor(std::make_unique<DynamicTypeBugVisitor>(Reg)); R->addRange(ReportedNode->getSourceRange()); C.emitReport(std::move(R)); } -std::shared_ptr<PathDiagnosticPiece> -DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &) { +PathDiagnosticPieceRef DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = N->getFirstPred()->getState(); @@ -105,7 +103,7 @@ DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N, return nullptr; // Retrieve the associated statement. - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; @@ -141,8 +139,7 @@ DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N, // Generate the extra diagnostic. PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, - nullptr); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); } static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) { diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp index 3cfe4dc82a100..cce3449b8873f 100644 --- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp +++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -20,16 +20,16 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/ParentMap.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" using namespace clang; @@ -83,9 +83,9 @@ class DynamicTypePropagation: ID.AddPointer(Sym); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: // The tracked symbol. @@ -113,14 +113,7 @@ public: void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const { - ProgramStateRef State = C.getState(); - DynamicTypeMapTy TypeMap = State->get<DynamicTypeMap>(); - for (DynamicTypeMapTy::iterator I = TypeMap.begin(), E = TypeMap.end(); - I != E; ++I) { - if (!SR.isLiveRegion(I->first)) { - State = State->remove<DynamicTypeMap>(I->first); - } - } + ProgramStateRef State = removeDeadTypes(C.getState(), SR); MostSpecializedTypeArgsMapTy TyArgMap = State->get<MostSpecializedTypeArgsMap>(); @@ -401,11 +394,11 @@ static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl( } const auto *SuperOfTo = - To->getObjectType()->getSuperClassType()->getAs<ObjCObjectType>(); + To->getObjectType()->getSuperClassType()->castAs<ObjCObjectType>(); assert(SuperOfTo); QualType SuperPtrOfToQual = C.getObjCObjectPointerType(QualType(SuperOfTo, 0)); - const auto *SuperPtrOfTo = SuperPtrOfToQual->getAs<ObjCObjectPointerType>(); + const auto *SuperPtrOfTo = SuperPtrOfToQual->castAs<ObjCObjectPointerType>(); if (To->isUnspecialized()) return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, SuperPtrOfTo, C); @@ -834,16 +827,15 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class && Sel.getAsString() == "class") { QualType ReceiverType = MessageExpr->getClassReceiver(); - const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>(); + const auto *ReceiverClassType = ReceiverType->castAs<ObjCObjectType>(); + if (!ReceiverClassType->isSpecialized()) + return; + QualType ReceiverClassPointerType = C.getASTContext().getObjCObjectPointerType( QualType(ReceiverClassType, 0)); - - if (!ReceiverClassType->isSpecialized()) - return; const auto *InferredType = - ReceiverClassPointerType->getAs<ObjCObjectPointerType>(); - assert(InferredType); + ReceiverClassPointerType->castAs<ObjCObjectPointerType>(); State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType); C.addTransition(State); @@ -882,7 +874,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, // When there is an entry available for the return symbol in DynamicTypeMap, // the call was inlined, and the information in the DynamicTypeMap is should // be precise. - if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) { + if (RetRegion && !getRawDynamicTypeInfo(State, RetRegion)) { // TODO: we have duplicated information in DynamicTypeMap and // MostSpecializedTypeArgsMap. We should only store anything in the later if // the stored data differs from the one stored in the former. @@ -919,19 +911,18 @@ void DynamicTypePropagation::reportGenericsBug( OS << "' to incompatible type '"; QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); OS << "'"; - std::unique_ptr<BugReport> R( - new BugReport(*ObjCGenericsBugType, OS.str(), N)); + auto R = std::make_unique<PathSensitiveBugReport>(*ObjCGenericsBugType, + OS.str(), N); R->markInteresting(Sym); - R->addVisitor(llvm::make_unique<GenericsBugVisitor>(Sym)); + R->addVisitor(std::make_unique<GenericsBugVisitor>(Sym)); if (ReportedNode) R->addRange(ReportedNode->getSourceRange()); C.emitReport(std::move(R)); } -std::shared_ptr<PathDiagnosticPiece> -DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) { +PathDiagnosticPieceRef DynamicTypePropagation::GenericsBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramStateRef state = N->getState(); ProgramStateRef statePrev = N->getFirstPred()->getState(); @@ -946,7 +937,7 @@ DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N, return nullptr; // Retrieve the associated statement. - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; @@ -981,8 +972,7 @@ DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N, // Generate the extra diagnostic. PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, - nullptr); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); } /// Register checkers. diff --git a/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp index 736d80ef9ec7e..481a5685a71f6 100644 --- a/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -83,7 +83,7 @@ void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const { new BuiltinBug(this, "Enum cast out of range", "The value provided to the cast expression is not in " "the valid range of values for the enum")); - C.emitReport(llvm::make_unique<BugReport>( + C.emitReport(std::make_unique<PathSensitiveBugReport>( *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(), N)); } @@ -91,6 +91,22 @@ void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const { void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, CheckerContext &C) const { + + // Only perform enum range check on casts where such checks are valid. For + // all other cast kinds (where enum range checks are unnecessary or invalid), + // just return immediately. TODO: The set of casts whitelisted for enum + // range checking may be incomplete. Better to add a missing cast kind to + // enable a missing check than to generate false negatives and have to remove + // those later. + switch (CE->getCastKind()) { + case CK_IntegralCast: + break; + + default: + return; + break; + } + // Get the value of the expression to cast. const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp index f23f784016d88..17c813962a234 100644 --- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -148,7 +148,7 @@ ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, if (!BT) BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); - BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N)); + BR.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, Msg, N)); return N; } @@ -305,7 +305,7 @@ void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, const SourceManager &SM = C.getSourceManager(); FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); std::string HashContent = - GetIssueString(SM, FL, getCheckName().getName(), "Category", + GetIssueString(SM, FL, getCheckerName().getName(), "Category", C.getLocationContext()->getDecl(), Opts); reportBug(HashContent, C); diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index 94542be7dd7ca..b315a84522859 100644 --- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -55,7 +55,8 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, "Using a fixed address is not portable because that " "address will probably not be valid in all " "environments or platforms.")); - auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + auto R = + std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); R->addRange(B->getRHS()->getSourceRange()); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp index d3ab980332365..d442b26b39594 100644 --- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -15,16 +15,18 @@ //===----------------------------------------------------------------------===// #include "Taint.h" -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "Yaml.h" #include "clang/AST/Attr.h" #include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" -#include <climits> -#include <initializer_list> +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/YAMLTraits.h" +#include <limits> #include <utility> using namespace clang; @@ -44,14 +46,51 @@ public: void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; - void printState(raw_ostream &Out, ProgramStateRef State, - const char *NL, const char *Sep) const override; + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; -private: - static const unsigned InvalidArgIndex = UINT_MAX; + using ArgVector = SmallVector<unsigned, 2>; + using SignedArgVector = SmallVector<int, 2>; + + enum class VariadicType { None, Src, Dst }; + + /// Used to parse the configuration file. + struct TaintConfiguration { + using NameArgsPair = std::pair<std::string, ArgVector>; + + struct Propagation { + std::string Name; + ArgVector SrcArgs; + SignedArgVector DstArgs; + VariadicType VarType; + unsigned VarIndex; + }; + + std::vector<Propagation> Propagations; + std::vector<NameArgsPair> Filters; + std::vector<NameArgsPair> Sinks; + + TaintConfiguration() = default; + TaintConfiguration(const TaintConfiguration &) = default; + TaintConfiguration(TaintConfiguration &&) = default; + TaintConfiguration &operator=(const TaintConfiguration &) = default; + TaintConfiguration &operator=(TaintConfiguration &&) = default; + }; + + /// Convert SignedArgVector to ArgVector. + ArgVector convertToArgVector(CheckerManager &Mgr, const std::string &Option, + SignedArgVector Args); + + /// Parse the config. + void parseConfiguration(CheckerManager &Mgr, const std::string &Option, + TaintConfiguration &&Config); + + static const unsigned InvalidArgIndex{std::numeric_limits<unsigned>::max()}; /// Denotes the return vale. - static const unsigned ReturnValueIndex = UINT_MAX - 1; + static const unsigned ReturnValueIndex{std::numeric_limits<unsigned>::max() - + 1}; +private: mutable std::unique_ptr<BugType> BT; void initBugType() const { if (!BT) @@ -76,28 +115,43 @@ private: static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg); /// Check for CWE-134: Uncontrolled Format String. - static const char MsgUncontrolledFormatString[]; + static constexpr llvm::StringLiteral MsgUncontrolledFormatString = + "Untrusted data is used as a format string " + "(CWE-134: Uncontrolled Format String)"; bool checkUncontrolledFormatString(const CallExpr *CE, CheckerContext &C) const; /// Check for: /// CERT/STR02-C. "Sanitize data passed to complex subsystems" /// CWE-78, "Failure to Sanitize Data into an OS Command" - static const char MsgSanitizeSystemArgs[]; + static constexpr llvm::StringLiteral MsgSanitizeSystemArgs = + "Untrusted data is passed to a system call " + "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; bool checkSystemCall(const CallExpr *CE, StringRef Name, CheckerContext &C) const; /// Check if tainted data is used as a buffer size ins strn.. functions, /// and allocators. - static const char MsgTaintedBufferSize[]; + static constexpr llvm::StringLiteral MsgTaintedBufferSize = + "Untrusted data is used to specify the buffer size " + "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " + "for character data and the null terminator)"; bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl, CheckerContext &C) const; + /// Check if tainted data is used as a custom sink's parameter. + static constexpr llvm::StringLiteral MsgCustomSink = + "Untrusted data is passed to a user-defined sink"; + bool checkCustomSinks(const CallExpr *CE, StringRef Name, + CheckerContext &C) const; + /// Generate a report if the expression is tainted or points to tainted data. - bool generateReportIfTainted(const Expr *E, const char Msg[], + bool generateReportIfTainted(const Expr *E, StringRef Msg, CheckerContext &C) const; - using ArgVector = SmallVector<unsigned, 2>; + struct TaintPropagationRule; + using NameRuleMap = llvm::StringMap<TaintPropagationRule>; + using NameArgMap = llvm::StringMap<ArgVector>; /// A struct used to specify taint propagation rules for a function. /// @@ -109,8 +163,6 @@ private: /// ReturnValueIndex is added to the dst list, the return value will be /// tainted. struct TaintPropagationRule { - enum class VariadicType { None, Src, Dst }; - using PropagationFuncType = bool (*)(bool IsTainted, const CallExpr *, CheckerContext &C); @@ -131,8 +183,7 @@ private: : VariadicIndex(InvalidArgIndex), VarType(VariadicType::None), PropagationFunc(nullptr) {} - TaintPropagationRule(std::initializer_list<unsigned> &&Src, - std::initializer_list<unsigned> &&Dst, + TaintPropagationRule(ArgVector &&Src, ArgVector &&Dst, VariadicType Var = VariadicType::None, unsigned VarIndex = InvalidArgIndex, PropagationFuncType Func = nullptr) @@ -141,7 +192,8 @@ private: /// Get the propagation rule for a given function. static TaintPropagationRule - getTaintPropagationRule(const FunctionDecl *FDecl, StringRef Name, + getTaintPropagationRule(const NameRuleMap &CustomPropagations, + const FunctionDecl *FDecl, StringRef Name, CheckerContext &C); void addSrcArg(unsigned A) { SrcArgs.push_back(A); } @@ -176,25 +228,71 @@ private: static bool postSocket(bool IsTainted, const CallExpr *CE, CheckerContext &C); }; + + /// Defines a map between the propagation function's name and + /// TaintPropagationRule. + NameRuleMap CustomPropagations; + + /// Defines a map between the filter function's name and filtering args. + NameArgMap CustomFilters; + + /// Defines a map between the sink function's name and sinking args. + NameArgMap CustomSinks; }; const unsigned GenericTaintChecker::ReturnValueIndex; const unsigned GenericTaintChecker::InvalidArgIndex; -const char GenericTaintChecker::MsgUncontrolledFormatString[] = - "Untrusted data is used as a format string " - "(CWE-134: Uncontrolled Format String)"; +// FIXME: these lines can be removed in C++17 +constexpr llvm::StringLiteral GenericTaintChecker::MsgUncontrolledFormatString; +constexpr llvm::StringLiteral GenericTaintChecker::MsgSanitizeSystemArgs; +constexpr llvm::StringLiteral GenericTaintChecker::MsgTaintedBufferSize; +constexpr llvm::StringLiteral GenericTaintChecker::MsgCustomSink; +} // end of anonymous namespace -const char GenericTaintChecker::MsgSanitizeSystemArgs[] = - "Untrusted data is passed to a system call " - "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; +using TaintConfig = GenericTaintChecker::TaintConfiguration; -const char GenericTaintChecker::MsgTaintedBufferSize[] = - "Untrusted data is used to specify the buffer size " - "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " - "for character data and the null terminator)"; +LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::Propagation) +LLVM_YAML_IS_SEQUENCE_VECTOR(TaintConfig::NameArgsPair) -} // end of anonymous namespace +namespace llvm { +namespace yaml { +template <> struct MappingTraits<TaintConfig> { + static void mapping(IO &IO, TaintConfig &Config) { + IO.mapOptional("Propagations", Config.Propagations); + IO.mapOptional("Filters", Config.Filters); + IO.mapOptional("Sinks", Config.Sinks); + } +}; + +template <> struct MappingTraits<TaintConfig::Propagation> { + static void mapping(IO &IO, TaintConfig::Propagation &Propagation) { + IO.mapRequired("Name", Propagation.Name); + IO.mapOptional("SrcArgs", Propagation.SrcArgs); + IO.mapOptional("DstArgs", Propagation.DstArgs); + IO.mapOptional("VariadicType", Propagation.VarType, + GenericTaintChecker::VariadicType::None); + IO.mapOptional("VariadicIndex", Propagation.VarIndex, + GenericTaintChecker::InvalidArgIndex); + } +}; + +template <> struct ScalarEnumerationTraits<GenericTaintChecker::VariadicType> { + static void enumeration(IO &IO, GenericTaintChecker::VariadicType &Value) { + IO.enumCase(Value, "None", GenericTaintChecker::VariadicType::None); + IO.enumCase(Value, "Src", GenericTaintChecker::VariadicType::Src); + IO.enumCase(Value, "Dst", GenericTaintChecker::VariadicType::Dst); + } +}; + +template <> struct MappingTraits<TaintConfig::NameArgsPair> { + static void mapping(IO &IO, TaintConfig::NameArgsPair &NameArg) { + IO.mapRequired("Name", NameArg.first); + IO.mapRequired("Args", NameArg.second); + } +}; +} // namespace yaml +} // namespace llvm /// A set which is used to pass information from call pre-visit instruction /// to the call post-visit. The values are unsigned integers, which are either @@ -202,9 +300,46 @@ const char GenericTaintChecker::MsgTaintedBufferSize[] = /// points to data, which should be tainted on return. REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned) +GenericTaintChecker::ArgVector GenericTaintChecker::convertToArgVector( + CheckerManager &Mgr, const std::string &Option, SignedArgVector Args) { + ArgVector Result; + for (int Arg : Args) { + if (Arg == -1) + Result.push_back(ReturnValueIndex); + else if (Arg < -1) { + Result.push_back(InvalidArgIndex); + Mgr.reportInvalidCheckerOptionValue( + this, Option, + "an argument number for propagation rules greater or equal to -1"); + } else + Result.push_back(static_cast<unsigned>(Arg)); + } + return Result; +} + +void GenericTaintChecker::parseConfiguration(CheckerManager &Mgr, + const std::string &Option, + TaintConfiguration &&Config) { + for (auto &P : Config.Propagations) { + GenericTaintChecker::CustomPropagations.try_emplace( + P.Name, std::move(P.SrcArgs), + convertToArgVector(Mgr, Option, P.DstArgs), P.VarType, P.VarIndex); + } + + for (auto &F : Config.Filters) { + GenericTaintChecker::CustomFilters.try_emplace(F.first, + std::move(F.second)); + } + + for (auto &S : Config.Sinks) { + GenericTaintChecker::CustomSinks.try_emplace(S.first, std::move(S.second)); + } +} + GenericTaintChecker::TaintPropagationRule GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( - const FunctionDecl *FDecl, StringRef Name, CheckerContext &C) { + const NameRuleMap &CustomPropagations, const FunctionDecl *FDecl, + StringRef Name, CheckerContext &C) { // TODO: Currently, we might lose precision here: we always mark a return // value as tainted even if it's just a pointer, pointing to tainted data. @@ -218,7 +353,8 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( .Case("freopen", TaintPropagationRule({}, {ReturnValueIndex})) .Case("getch", TaintPropagationRule({}, {ReturnValueIndex})) .Case("getchar", TaintPropagationRule({}, {ReturnValueIndex})) - .Case("getchar_unlocked", TaintPropagationRule({}, {ReturnValueIndex})) + .Case("getchar_unlocked", + TaintPropagationRule({}, {ReturnValueIndex})) .Case("getenv", TaintPropagationRule({}, {ReturnValueIndex})) .Case("gets", TaintPropagationRule({}, {0, ReturnValueIndex})) .Case("scanf", TaintPropagationRule({}, {}, VariadicType::Dst, 1)) @@ -297,6 +433,10 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( // or smart memory copy: // - memccpy - copying until hitting a special character. + auto It = CustomPropagations.find(Name); + if (It != CustomPropagations.end()) + return It->getValue(); + return TaintPropagationRule(); } @@ -336,8 +476,8 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE, return; // First, try generating a propagation rule for this function. - TaintPropagationRule Rule = - TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C); + TaintPropagationRule Rule = TaintPropagationRule::getTaintPropagationRule( + this->CustomPropagations, FDecl, Name, C); if (!Rule.isNull()) { State = Rule.process(CE, C); if (!State) @@ -409,6 +549,9 @@ bool GenericTaintChecker::checkPre(const CallExpr *CE, if (checkTaintedBufferSize(CE, FDecl, C)) return true; + if (checkCustomSinks(CE, Name, C)) + return true; + return false; } @@ -446,7 +589,8 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, bool IsTainted = true; for (unsigned ArgNum : SrcArgs) { if (ArgNum >= CE->getNumArgs()) - return State; + continue; + if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C))) break; } @@ -454,7 +598,7 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, // Check for taint in variadic arguments. if (!IsTainted && VariadicType::Src == VarType) { // Check if any of the arguments is tainted - for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) { + for (unsigned i = VariadicIndex; i < CE->getNumArgs(); ++i) { if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C))) break; } @@ -474,8 +618,10 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, continue; } + if (ArgNum >= CE->getNumArgs()) + continue; + // Mark the given argument. - assert(ArgNum < CE->getNumArgs()); State = State->add<TaintArgsOnPostVisit>(ArgNum); } @@ -485,7 +631,7 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, // If they are not pointing to const data, mark data as tainted. // TODO: So far we are just going one level down; ideally we'd need to // recurse here. - for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) { + for (unsigned i = VariadicIndex; i < CE->getNumArgs(); ++i) { const Expr *Arg = CE->getArg(i); // Process pointer argument. const Type *ArgTy = Arg->getType().getTypePtr(); @@ -550,7 +696,7 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { static bool getPrintfFormatArgumentNum(const CallExpr *CE, const CheckerContext &C, - unsigned int &ArgNum) { + unsigned &ArgNum) { // Find if the function contains a format string argument. // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, // vsnprintf, syslog, custom annotated functions. @@ -572,8 +718,7 @@ static bool getPrintfFormatArgumentNum(const CallExpr *CE, return false; } -bool GenericTaintChecker::generateReportIfTainted(const Expr *E, - const char Msg[], +bool GenericTaintChecker::generateReportIfTainted(const Expr *E, StringRef Msg, CheckerContext &C) const { assert(E); @@ -591,9 +736,9 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, // Generate diagnostic. if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); - auto report = llvm::make_unique<BugReport>(*BT, Msg, N); + auto report = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); report->addRange(E->getSourceRange()); - report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal)); + report->addVisitor(std::make_unique<TaintBugVisitor>(TaintedSVal)); C.emitReport(std::move(report)); return true; } @@ -603,7 +748,7 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E, bool GenericTaintChecker::checkUncontrolledFormatString( const CallExpr *CE, CheckerContext &C) const { // Check if the function contains a format string argument. - unsigned int ArgNum = 0; + unsigned ArgNum = 0; if (!getPrintfFormatArgumentNum(CE, C, ArgNum)) return false; @@ -629,9 +774,9 @@ bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name, .Case("execvP", 0) .Case("execve", 0) .Case("dlopen", 0) - .Default(UINT_MAX); + .Default(InvalidArgIndex); - if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1)) + if (ArgNum == InvalidArgIndex || CE->getNumArgs() < (ArgNum + 1)) return false; return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C); @@ -676,8 +821,33 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C); } -void ento::registerGenericTaintChecker(CheckerManager &mgr) { - mgr.registerChecker<GenericTaintChecker>(); +bool GenericTaintChecker::checkCustomSinks(const CallExpr *CE, StringRef Name, + CheckerContext &C) const { + auto It = CustomSinks.find(Name); + if (It == CustomSinks.end()) + return false; + + const GenericTaintChecker::ArgVector &Args = It->getValue(); + for (unsigned ArgNum : Args) { + if (ArgNum >= CE->getNumArgs()) + continue; + + if (generateReportIfTainted(CE->getArg(ArgNum), MsgCustomSink, C)) + return true; + } + + return false; +} + +void ento::registerGenericTaintChecker(CheckerManager &Mgr) { + auto *Checker = Mgr.registerChecker<GenericTaintChecker>(); + std::string Option{"Config"}; + StringRef ConfigFile = + Mgr.getAnalyzerOptions().getCheckerStringOption(Checker, Option); + llvm::Optional<TaintConfig> Config = + getConfiguration<TaintConfig>(Mgr, Checker, Option, ConfigFile); + if (Config) + Checker->parseConfiguration(Mgr, Option, std::move(Config.getValue())); } bool ento::shouldRegisterGenericTaintChecker(const LangOptions &LO) { diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp index e3270f1f7be27..b0d101c88517f 100644 --- a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -54,9 +54,9 @@ public: ID.AddPointer(getTag()); } - virtual std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + virtual PathDiagnosticPieceRef + VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; // FIXME: Scan the map once in the visitor's constructor and do a direct // lookup by region. @@ -261,7 +261,7 @@ namespace ento { namespace allocation_state { std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) { - return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym); + return std::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym); } const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { @@ -278,15 +278,13 @@ const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { } // end namespace ento } // end namespace clang -std::shared_ptr<PathDiagnosticPiece> -InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &) { +PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { if (!isSymbolTracked(N->getState(), PtrToBuf) || isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) return nullptr; - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; @@ -301,8 +299,7 @@ InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, << "' obtained here"; PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, - nullptr); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); } void ento::registerInnerPointerChecker(CheckerManager &Mgr) { diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp index 6f1060b5f26d9..97ace68569ef2 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -71,7 +71,7 @@ #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include <utility> @@ -248,7 +248,7 @@ public: }; DefaultBool ChecksEnabled[CK_NumCheckKinds]; - CheckName CheckNames[CK_NumCheckKinds]; + CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostCall(const CallEvent &Call, CheckerContext &C) const; @@ -356,14 +356,12 @@ bool isZero(ProgramStateRef State, const NonLoc &Val); IteratorChecker::IteratorChecker() { OutOfRangeBugType.reset( - new BugType(this, "Iterator out of range", "Misuse of STL APIs", - /*SuppressOnSink=*/true)); + new BugType(this, "Iterator out of range", "Misuse of STL APIs")); MismatchedBugType.reset( new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs", /*SuppressOnSink=*/true)); InvalidatedBugType.reset( - new BugType(this, "Iterator invalidated", "Misuse of STL APIs", - /*SuppressOnSink=*/true)); + new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); } void IteratorChecker::checkPreCall(const CallEvent &Call, @@ -406,13 +404,15 @@ void IteratorChecker::checkPreCall(const CallEvent &Call, } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { // Check for out-of-range incrementions and decrementions - if (Call.getNumArgs() >= 1) { + if (Call.getNumArgs() >= 1 && + Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), InstCall->getCXXThisVal(), Call.getArgSVal(0)); } } else { - if (Call.getNumArgs() >= 2) { + if (Call.getNumArgs() >= 2 && + Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), Call.getArgSVal(0), Call.getArgSVal(1)); } @@ -565,7 +565,8 @@ void IteratorChecker::checkPostCall(const CallEvent &Call, if (Func->isOverloadedOperator()) { const auto Op = Func->getOverloadedOperator(); if (isAssignmentOperator(Op)) { - const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call); + // Overloaded 'operator=' must be a non-static member function. + const auto *InstCall = cast<CXXInstanceCall>(&Call); if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) { handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), Call.getArgSVal(0)); @@ -590,14 +591,16 @@ void IteratorChecker::checkPostCall(const CallEvent &Call, return; } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { - if (Call.getNumArgs() >= 1) { + if (Call.getNumArgs() >= 1 && + Call.getArgExpr(0)->getType()->isIntegralOrEnumerationType()) { handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), Call.getReturnValue(), InstCall->getCXXThisVal(), Call.getArgSVal(0)); return; } } else { - if (Call.getNumArgs() >= 2) { + if (Call.getNumArgs() >= 2 && + Call.getArgExpr(1)->getType()->isIntegralOrEnumerationType()) { handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), Call.getReturnValue(), Call.getArgSVal(0), Call.getArgSVal(1)); @@ -923,7 +926,7 @@ void IteratorChecker::verifyDereference(CheckerContext &C, auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Val); if (Pos && isPastTheEnd(State, *Pos)) { - auto *N = C.generateNonFatalErrorNode(State); + auto *N = C.generateErrorNode(State); if (!N) return; reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N); @@ -935,7 +938,7 @@ void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const { auto State = C.getState(); const auto *Pos = getIteratorPosition(State, Val); if (Pos && !Pos->isValid()) { - auto *N = C.generateNonFatalErrorNode(State); + auto *N = C.generateErrorNode(State); if (!N) { return; } @@ -1043,14 +1046,14 @@ void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C, // The result may be the past-end iterator of the container, but any other // out of range position is undefined behaviour if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) { - auto *N = C.generateNonFatalErrorNode(State); + auto *N = C.generateErrorNode(State); if (!N) return; reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS, C, N); } if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) { - auto *N = C.generateNonFatalErrorNode(State); + auto *N = C.generateErrorNode(State); if (!N) return; reportOutOfRangeBug("Iterator incremented behind the past-the-end " @@ -1587,7 +1590,8 @@ IteratorPosition IteratorChecker::advancePosition(CheckerContext &C, void IteratorChecker::reportOutOfRangeBug(const StringRef &Message, const SVal &Val, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>(*OutOfRangeBugType, Message, + ErrNode); R->markInteresting(Val); C.emitReport(std::move(R)); } @@ -1596,7 +1600,8 @@ void IteratorChecker::reportMismatchedBug(const StringRef &Message, const SVal &Val1, const SVal &Val2, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, + ErrNode); R->markInteresting(Val1); R->markInteresting(Val2); C.emitReport(std::move(R)); @@ -1606,7 +1611,8 @@ void IteratorChecker::reportMismatchedBug(const StringRef &Message, const SVal &Val, const MemRegion *Reg, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>(*MismatchedBugType, Message, + ErrNode); R->markInteresting(Val); R->markInteresting(Reg); C.emitReport(std::move(R)); @@ -1615,7 +1621,8 @@ void IteratorChecker::reportMismatchedBug(const StringRef &Message, void IteratorChecker::reportInvalidatedBug(const StringRef &Message, const SVal &Val, CheckerContext &C, ExplodedNode *ErrNode) const { - auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>(*InvalidatedBugType, + Message, ErrNode); R->markInteresting(Val); C.emitReport(std::move(R)); } @@ -2373,12 +2380,10 @@ bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) { auto *checker = Mgr.getChecker<IteratorChecker>(); \ checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \ checker->CheckNames[IteratorChecker::CK_##name] = \ - Mgr.getCurrentCheckName(); \ + Mgr.getCurrentCheckerName(); \ } \ \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } + bool ento::shouldRegister##name(const LangOptions &LO) { return true; } REGISTER_CHECKER(IteratorRangeChecker) REGISTER_CHECKER(MismatchedIteratorChecker) diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp index 2b75f3acc9228..0d64fbd6f62ec 100644 --- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -48,8 +48,8 @@ struct ChecksFilter { /// Check that all ivars are invalidated. DefaultBool check_InstanceVariableInvalidation; - CheckName checkName_MissingInvalidationMethod; - CheckName checkName_InstanceVariableInvalidation; + CheckerNameRef checkName_MissingInvalidationMethod; + CheckerNameRef checkName_InstanceVariableInvalidation; }; class IvarInvalidationCheckerImpl { @@ -199,7 +199,7 @@ class IvarInvalidationCheckerImpl { const ObjCIvarDecl *IvarDecl, const IvarToPropMapTy &IvarToPopertyMap); - void reportNoInvalidationMethod(CheckName CheckName, + void reportNoInvalidationMethod(CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl, const IvarToPropMapTy &IvarToPopertyMap, const ObjCInterfaceDecl *InterfaceD, @@ -526,7 +526,7 @@ visit(const ObjCImplementationDecl *ImplD) const { } void IvarInvalidationCheckerImpl::reportNoInvalidationMethod( - CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl, + CheckerNameRef CheckName, const ObjCIvarDecl *FirstIvarDecl, const IvarToPropMapTy &IvarToPopertyMap, const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const { SmallString<128> sbuf; @@ -748,12 +748,10 @@ bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) { IvarInvalidationChecker *checker = \ mgr.getChecker<IvarInvalidationChecker>(); \ checker->Filter.check_##name = true; \ - checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ + checker->Filter.checkName_##name = mgr.getCurrentCheckerName(); \ } \ \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } + bool ento::shouldRegister##name(const LangOptions &LO) { return true; } REGISTER_CHECKER(InstanceVariableInvalidation) REGISTER_CHECKER(MissingInvalidationMethod) diff --git a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp index 46067ecbca991..a81015b6e524b 100644 --- a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -120,12 +120,12 @@ class NonLocalizedStringBRVisitor final : public BugReporterVisitor { public: NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString) : NonLocalizedString(NonLocalizedString), Satisfied(false) { - assert(NonLocalizedString); + assert(NonLocalizedString); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(NonLocalizedString); @@ -761,8 +761,8 @@ void NonLocalizedStringChecker::reportLocalizationError( return; // Generate the bug report. - std::unique_ptr<BugReport> R(new BugReport( - *BT, "User-facing text should use localized string macro", ErrNode)); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT, "User-facing text should use localized string macro", ErrNode); if (argumentNumber) { R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange()); } else { @@ -772,7 +772,7 @@ void NonLocalizedStringChecker::reportLocalizationError( const MemRegion *StringRegion = S.getAsRegion(); if (StringRegion) - R->addVisitor(llvm::make_unique<NonLocalizedStringBRVisitor>(StringRegion)); + R->addVisitor(std::make_unique<NonLocalizedStringBRVisitor>(StringRegion)); C.emitReport(std::move(R)); } @@ -882,18 +882,17 @@ void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - const Decl *D = Call.getDecl(); - if (D && isa<FunctionDecl>(D)) { - const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); - auto formals = FD->parameters(); - for (unsigned i = 0, - ei = std::min(unsigned(formals.size()), Call.getNumArgs()); - i != ei; ++i) { - if (isAnnotatedAsTakingLocalized(formals[i])) { - auto actual = Call.getArgSVal(i); - if (hasNonLocalizedState(actual, C)) { - reportLocalizationError(actual, Call, C, i + 1); - } + const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!FD) + return; + + auto formals = FD->parameters(); + for (unsigned i = 0, ei = std::min(static_cast<unsigned>(formals.size()), + Call.getNumArgs()); i != ei; ++i) { + if (isAnnotatedAsTakingLocalized(formals[i])) { + auto actual = Call.getArgSVal(i); + if (hasNonLocalizedState(actual, C)) { + reportLocalizationError(actual, Call, C, i + 1); } } } @@ -998,9 +997,10 @@ void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL, setNonLocalizedState(sv, C); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { if (Satisfied) return nullptr; diff --git a/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/lib/StaticAnalyzer/Checkers/MIGChecker.cpp index 6e7776bb484e5..d8fd125f4003d 100644 --- a/lib/StaticAnalyzer/Checkers/MIGChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MIGChecker.cpp @@ -274,7 +274,7 @@ void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const { if (!N) return; - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( BT, "MIG callback fails with error after deallocating argument value. " "This is a use-after-free vulnerability because the caller will try to " @@ -282,7 +282,8 @@ void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const { N); R->addRange(RS->getSourceRange()); - bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, false); + bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, + bugreporter::TrackingKind::Thorough, false); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp index b250d3f8795e7..bbf2ddec57620 100644 --- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp +++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -30,8 +30,8 @@ void MPIBugReporter::reportDoubleNonblocking( ErrorText = "Double nonblocking on request " + RequestRegion->getDescriptiveName() + ". "; - auto Report = llvm::make_unique<BugReport>(*DoubleNonblockingBugType, - ErrorText, ExplNode); + auto Report = std::make_unique<PathSensitiveBugReport>( + *DoubleNonblockingBugType, ErrorText, ExplNode); Report->addRange(MPICallEvent.getSourceRange()); SourceRange Range = RequestRegion->sourceRange(); @@ -39,7 +39,7 @@ void MPIBugReporter::reportDoubleNonblocking( if (Range.isValid()) Report->addRange(Range); - Report->addVisitor(llvm::make_unique<RequestNodeVisitor>( + Report->addVisitor(std::make_unique<RequestNodeVisitor>( RequestRegion, "Request is previously used by nonblocking call here. ")); Report->markInteresting(RequestRegion); @@ -53,13 +53,13 @@ void MPIBugReporter::reportMissingWait( std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + " has no matching wait. "}; - auto Report = - llvm::make_unique<BugReport>(*MissingWaitBugType, ErrorText, ExplNode); + auto Report = std::make_unique<PathSensitiveBugReport>(*MissingWaitBugType, + ErrorText, ExplNode); SourceRange Range = RequestRegion->sourceRange(); if (Range.isValid()) Report->addRange(Range); - Report->addVisitor(llvm::make_unique<RequestNodeVisitor>( + Report->addVisitor(std::make_unique<RequestNodeVisitor>( RequestRegion, "Request is previously used by nonblocking call here. ")); Report->markInteresting(RequestRegion); @@ -73,8 +73,8 @@ void MPIBugReporter::reportUnmatchedWait( std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + " has no matching nonblocking call. "}; - auto Report = - llvm::make_unique<BugReport>(*UnmatchedWaitBugType, ErrorText, ExplNode); + auto Report = std::make_unique<PathSensitiveBugReport>(*UnmatchedWaitBugType, + ErrorText, ExplNode); Report->addRange(CE.getSourceRange()); SourceRange Range = RequestRegion->sourceRange(); @@ -84,20 +84,22 @@ void MPIBugReporter::reportUnmatchedWait( BReporter.emitReport(std::move(Report)); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef MPIBugReporter::RequestNodeVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - BugReport &BR) { + PathSensitiveBugReport &BR) { if (IsNodeFound) return nullptr; const Request *const Req = N->getState()->get<RequestMap>(RequestRegion); + assert(Req && "The region must be tracked and alive, given that we've " + "just emitted a report against it"); const Request *const PrevReq = N->getFirstPred()->getState()->get<RequestMap>(RequestRegion); // Check if request was previously unused or in a different state. - if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) { + if (!PrevReq || (Req->CurrentState != PrevReq->CurrentState)) { IsNodeFound = true; ProgramPoint P = N->getFirstPred()->getLocation(); diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h index 6fbc30288655c..9871da026b042 100644 --- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h +++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -89,9 +89,9 @@ private: ID.AddPointer(RequestRegion); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: const MemRegion *const RequestRegion; diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp index 32ba9bc8e2ef5..e064ca6bd88f6 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -113,11 +113,14 @@ private: const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, CheckerContext &C) const; - std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( - const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; + std::unique_ptr<PathSensitiveBugReport> + generateAllocatedDataNotReleasedReport(const AllocationPair &AP, + ExplodedNode *N, + CheckerContext &C) const; /// Mark an AllocationPair interesting for diagnostic reporting. - void markInteresting(BugReport *R, const AllocationPair &AP) const { + void markInteresting(PathSensitiveBugReport *R, + const AllocationPair &AP) const { R->markInteresting(AP.first); R->markInteresting(AP.second->Region); } @@ -139,9 +142,9 @@ private: ID.AddPointer(Sym); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; }; } @@ -236,8 +239,8 @@ void MacOSKeychainAPIChecker:: os << "Deallocator doesn't match the allocator: '" << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; - auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); - Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); Report->addRange(ArgExpr->getSourceRange()); markInteresting(Report.get(), AP); C.emitReport(std::move(Report)); @@ -280,8 +283,9 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, << "the allocator: missing a call to '" << FunctionsToTrack[DIdx].Name << "'."; - auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); - Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); + Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(V)); Report->addRange(ArgExpr->getSourceRange()); Report->markInteresting(AS->Region); C.emitReport(std::move(Report)); @@ -334,7 +338,7 @@ void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, if (!N) return; initBugType(); - auto Report = llvm::make_unique<BugReport>( + auto Report = std::make_unique<PathSensitiveBugReport>( *BT, "Trying to free data which has not been allocated.", N); Report->addRange(ArgExpr->getSourceRange()); if (AS) @@ -465,7 +469,7 @@ MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, return AllocNode; } -std::unique_ptr<BugReport> +std::unique_ptr<PathSensitiveBugReport> MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; @@ -480,18 +484,18 @@ MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( // allocated, and only report a single path. PathDiagnosticLocation LocUsedForUniqueing; const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); - const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); + const Stmt *AllocStmt = AllocNode->getStmtForDiagnostics(); if (AllocStmt) LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, C.getSourceManager(), AllocNode->getLocationContext()); - auto Report = - llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing, - AllocNode->getLocationContext()->getDecl()); + auto Report = std::make_unique<PathSensitiveBugReport>( + *BT, os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); - Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); + Report->addVisitor(std::make_unique<SecKeychainBugVisitor>(AP.first)); markInteresting(Report.get(), AP); return Report; } @@ -613,9 +617,10 @@ ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape( return State; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( - const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { + const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); if (!AS) return nullptr; diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 1c52d20d09914..d964a1668eaaa 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -139,7 +139,8 @@ void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'", "API Misuse (Apple)")); - auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(report)); } diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a79b341890655..a824499518730 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -6,8 +6,41 @@ // //===----------------------------------------------------------------------===// // -// This file defines malloc/free checker, which checks for potential memory -// leaks, double free, and use-after-free problems. +// This file defines a variety of memory management related checkers, such as +// leak, double free, and use-after-free. +// +// The following checkers are defined here: +// +// * MallocChecker +// Despite its name, it models all sorts of memory allocations and +// de- or reallocation, including but not limited to malloc, free, +// relloc, new, delete. It also reports on a variety of memory misuse +// errors. +// Many other checkers interact very closely with this checker, in fact, +// most are merely options to this one. Other checkers may register +// MallocChecker, but do not enable MallocChecker's reports (more details +// to follow around its field, ChecksEnabled). +// It also has a boolean "Optimistic" checker option, which if set to true +// will cause the checker to model user defined memory management related +// functions annotated via the attribute ownership_takes, ownership_holds +// and ownership_returns. +// +// * NewDeleteChecker +// Enables the modeling of new, new[], delete, delete[] in MallocChecker, +// and checks for related double-free and use-after-free errors. +// +// * NewDeleteLeaksChecker +// Checks for leaks related to new, new[], delete, delete[]. +// Depends on NewDeleteChecker. +// +// * MismatchedDeallocatorChecker +// Enables checking whether memory is deallocated with the correspending +// allocation function in MallocChecker, such as malloc() allocated +// regions are only freed by free(), new by delete, new[] by delete[]. +// +// InnerPointerChecker interacts very closely with MallocChecker, but unlike +// the above checkers, it has it's own file, hence the many InnerPointerChecker +// related headers and non-static functions. // //===----------------------------------------------------------------------===// @@ -37,6 +70,10 @@ using namespace clang; using namespace ento; +//===----------------------------------------------------------------------===// +// The types of allocation we're modeling. +//===----------------------------------------------------------------------===// + namespace { // Used to check correspondence between allocators and deallocators. @@ -50,57 +87,88 @@ enum AllocationFamily { AF_InnerBuffer }; +struct MemFunctionInfoTy; + +} // end of anonymous namespace + +/// Determine family of a deallocation expression. +static AllocationFamily +getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, + const Stmt *S); + +/// Print names of allocators and deallocators. +/// +/// \returns true on success. +static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E); + +/// Print expected name of an allocator based on the deallocator's +/// family derived from the DeallocExpr. +static void printExpectedAllocName(raw_ostream &os, + const MemFunctionInfoTy &MemFunctionInfo, + CheckerContext &C, const Expr *E); + +/// Print expected name of a deallocator based on the allocator's +/// family. +static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family); + +//===----------------------------------------------------------------------===// +// The state of a symbol, in terms of memory management. +//===----------------------------------------------------------------------===// + +namespace { + class RefState { - enum Kind { // Reference to allocated memory. - Allocated, - // Reference to zero-allocated memory. - AllocatedOfSizeZero, - // Reference to released/freed memory. - Released, - // The responsibility for freeing resources has transferred from - // this reference. A relinquished symbol should not be freed. - Relinquished, - // We are no longer guaranteed to have observed all manipulations - // of this pointer/memory. For example, it could have been - // passed as a parameter to an opaque function. - Escaped + enum Kind { + // Reference to allocated memory. + Allocated, + // Reference to zero-allocated memory. + AllocatedOfSizeZero, + // Reference to released/freed memory. + Released, + // The responsibility for freeing resources has transferred from + // this reference. A relinquished symbol should not be freed. + Relinquished, + // We are no longer guaranteed to have observed all manipulations + // of this pointer/memory. For example, it could have been + // passed as a parameter to an opaque function. + Escaped }; const Stmt *S; - unsigned K : 3; // Kind enum, but stored as a bitfield. - unsigned Family : 29; // Rest of 32-bit word, currently just an allocation - // family. - RefState(Kind k, const Stmt *s, unsigned family) - : S(s), K(k), Family(family) { + Kind K; + AllocationFamily Family; + + RefState(Kind k, const Stmt *s, AllocationFamily family) + : S(s), K(k), Family(family) { assert(family != AF_None); } + public: bool isAllocated() const { return K == Allocated; } bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; } bool isReleased() const { return K == Released; } bool isRelinquished() const { return K == Relinquished; } bool isEscaped() const { return K == Escaped; } - AllocationFamily getAllocationFamily() const { - return (AllocationFamily)Family; - } + AllocationFamily getAllocationFamily() const { return Family; } const Stmt *getStmt() const { return S; } bool operator==(const RefState &X) const { return K == X.K && S == X.S && Family == X.Family; } - static RefState getAllocated(unsigned family, const Stmt *s) { + static RefState getAllocated(AllocationFamily family, const Stmt *s) { return RefState(Allocated, s, family); } static RefState getAllocatedOfSizeZero(const RefState *RS) { return RefState(AllocatedOfSizeZero, RS->getStmt(), RS->getAllocationFamily()); } - static RefState getReleased(unsigned family, const Stmt *s) { + static RefState getReleased(AllocationFamily family, const Stmt *s) { return RefState(Released, s, family); } - static RefState getRelinquished(unsigned family, const Stmt *s) { + static RefState getRelinquished(AllocationFamily family, const Stmt *s) { return RefState(Relinquished, s, family); } static RefState getEscaped(const RefState *RS) { @@ -113,8 +181,8 @@ public: ID.AddInteger(Family); } - void dump(raw_ostream &OS) const { - switch (static_cast<Kind>(K)) { + LLVM_DUMP_METHOD void dump(raw_ostream &OS) const { + switch (K) { #define CASE(ID) case ID: OS << #ID; break; CASE(Allocated) CASE(AllocatedOfSizeZero) @@ -127,24 +195,62 @@ public: LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } }; -enum ReallocPairKind { - RPToBeFreedAfterFailure, - // The symbol has been freed when reallocation failed. - RPIsFreeOnFailure, - // The symbol does not need to be freed after reallocation fails. - RPDoNotTrackAfterFailure +} // end of anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) + +/// Check if the memory associated with this symbol was released. +static bool isReleased(SymbolRef Sym, CheckerContext &C); + +/// Update the RefState to reflect the new memory allocation. +/// The optional \p RetVal parameter specifies the newly allocated pointer +/// value; if unspecified, the value of expression \p E is used. +static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, + ProgramStateRef State, + AllocationFamily Family = AF_Malloc, + Optional<SVal> RetVal = None); + +//===----------------------------------------------------------------------===// +// The modeling of memory reallocation. +// +// The terminology 'toPtr' and 'fromPtr' will be used: +// toPtr = realloc(fromPtr, 20); +//===----------------------------------------------------------------------===// + +REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) + +namespace { + +/// The state of 'fromPtr' after reallocation is known to have failed. +enum OwnershipAfterReallocKind { + // The symbol needs to be freed (e.g.: realloc) + OAR_ToBeFreedAfterFailure, + // The symbol has been freed (e.g.: reallocf) + OAR_FreeOnFailure, + // The symbol doesn't have to freed (e.g.: we aren't sure if, how and where + // 'fromPtr' was allocated: + // void Haha(int *ptr) { + // ptr = realloc(ptr, 67); + // // ... + // } + // ). + OAR_DoNotTrackAfterFailure }; -/// \class ReallocPair -/// Stores information about the symbol being reallocated by a call to -/// 'realloc' to allow modeling failed reallocation later in the path. +/// Stores information about the 'fromPtr' symbol after reallocation. +/// +/// This is important because realloc may fail, and that needs special modeling. +/// Whether reallocation failed or not will not be known until later, so we'll +/// store whether upon failure 'fromPtr' will be freed, or needs to be freed +/// later, etc. struct ReallocPair { - // The symbol which realloc reallocated. + + // The 'fromPtr'. SymbolRef ReallocatedSym; - ReallocPairKind Kind; + OwnershipAfterReallocKind Kind; - ReallocPair(SymbolRef S, ReallocPairKind K) : - ReallocatedSym(S), Kind(K) {} + ReallocPair(SymbolRef S, OwnershipAfterReallocKind K) + : ReallocatedSym(S), Kind(K) {} void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Kind); ID.AddPointer(ReallocatedSym); @@ -155,42 +261,88 @@ struct ReallocPair { } }; -typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo; - -class MallocChecker : public Checker<check::DeadSymbols, - check::PointerEscape, - check::ConstPointerEscape, - check::PreStmt<ReturnStmt>, - check::EndFunction, - check::PreCall, - check::PostStmt<CallExpr>, - check::PostStmt<CXXNewExpr>, - check::NewAllocator, - check::PreStmt<CXXDeleteExpr>, - check::PostStmt<BlockExpr>, - check::PostObjCMessage, - check::Location, - eval::Assume> -{ -public: - MallocChecker() - : II_alloca(nullptr), II_win_alloca(nullptr), II_malloc(nullptr), - II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), - II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), - II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), - II_kfree(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_try_realloc_n(nullptr) {} +} // end of anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) + +//===----------------------------------------------------------------------===// +// Kinds of memory operations, information about resource managing functions. +//===----------------------------------------------------------------------===// + +namespace { + +enum class MemoryOperationKind { MOK_Allocate, MOK_Free, MOK_Any }; +struct MemFunctionInfoTy { + /// The value of the MallocChecker:Optimistic is stored in this variable. + /// /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. + /// In optimistic mode, the checker assumes that all user-defined functions + /// which might free a pointer are annotated. + DefaultBool ShouldIncludeOwnershipAnnotatedFunctions; + + // TODO: Change these to CallDescription, and get rid of lazy initialization. + mutable IdentifierInfo *II_alloca = nullptr, *II_win_alloca = nullptr, + *II_malloc = nullptr, *II_free = nullptr, + *II_realloc = nullptr, *II_calloc = nullptr, + *II_valloc = nullptr, *II_reallocf = nullptr, + *II_strndup = nullptr, *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_kfree = nullptr, + *II_g_try_realloc_n = nullptr; + + void initIdentifierInfo(ASTContext &C) const; + + ///@{ + /// Check if this is one of the functions which can allocate/reallocate + /// memory pointed to by one of its arguments. + bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isCMemFunction(const FunctionDecl *FD, ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const; + + /// Tells if the callee is one of the builtin new/delete operators, including + /// placement operators and other standard overloads. + bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; + ///@} +}; + +} // end of anonymous namespace + +//===----------------------------------------------------------------------===// +// Definition of the MallocChecker class. +//===----------------------------------------------------------------------===// + +namespace { + +class MallocChecker + : public Checker<check::DeadSymbols, check::PointerEscape, + check::ConstPointerEscape, check::PreStmt<ReturnStmt>, + check::EndFunction, check::PreCall, + check::PostStmt<CallExpr>, check::PostStmt<CXXNewExpr>, + check::NewAllocator, check::PreStmt<CXXDeleteExpr>, + check::PostStmt<BlockExpr>, check::PostObjCMessage, + check::Location, eval::Assume> { +public: + MemFunctionInfoTy MemFunctionInfo; + + /// Many checkers are essentially built into this one, so enabling them will + /// make MallocChecker perform additional modeling and reporting. enum CheckKind { + /// When a subchecker is enabled but MallocChecker isn't, model memory + /// management but do not emit warnings emitted with MallocChecker only + /// enabled. CK_MallocChecker, CK_NewDeleteChecker, CK_NewDeleteLeaksChecker, @@ -199,16 +351,10 @@ public: CK_NumCheckKinds }; - enum class MemoryOperationKind { - MOK_Allocate, - MOK_Free, - MOK_Any - }; - - DefaultBool IsOptimistic; + using LeakInfo = std::pair<const ExplodedNode *, const MemRegion *>; DefaultBool ChecksEnabled[CK_NumCheckKinds]; - CheckName CheckNames[CK_NumCheckKinds]; + CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; @@ -248,47 +394,9 @@ private: mutable std::unique_ptr<BugType> BT_MismatchedDealloc; mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds]; mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds]; - mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, - *II_realloc, *II_calloc, *II_valloc, *II_reallocf, - *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, - *II_kfree, *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_g_try_realloc_n; - mutable Optional<uint64_t> KernelZeroFlagVal; - - void initIdentifierInfo(ASTContext &C) const; - - /// Determine family of a deallocation expression. - AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const; - - /// Print names of allocators and deallocators. - /// - /// \returns true on success. - bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, - const Expr *E) const; - - /// Print expected name of an allocator based on the deallocator's - /// family derived from the DeallocExpr. - void printExpectedAllocName(raw_ostream &os, CheckerContext &C, - const Expr *DeallocExpr) const; - /// Print expected name of a deallocator based on the allocator's - /// family. - void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const; - ///@{ - /// Check if this is one of the functions which can allocate/reallocate memory - /// pointed to by one of its arguments. - bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; - bool isCMemFunction(const FunctionDecl *FD, - ASTContext &C, - AllocationFamily Family, - MemoryOperationKind MemKind) const; - bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; - ///@} + // TODO: Remove mutable by moving the initializtaion to the registry function. + mutable Optional<uint64_t> KernelZeroFlagVal; /// Process C++ operator new()'s allocation, which is the part of C++ /// new-expression that goes before the constructor. @@ -296,23 +404,64 @@ private: SVal Target) const; /// Perform a zero-allocation check. - /// The optional \p RetVal parameter specifies the newly allocated pointer - /// value; if unspecified, the value of expression \p E is used. - ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E, - const unsigned AllocationSizeArg, - ProgramStateRef State, - Optional<SVal> RetVal = None) const; - + /// + /// \param [in] E The expression that allocates memory. + /// \param [in] IndexOfSizeArg Index of the argument that specifies the size + /// of the memory that needs to be allocated. E.g. for malloc, this would be + /// 0. + /// \param [in] RetVal Specifies the newly allocated pointer value; + /// if unspecified, the value of expression \p E is used. + static ProgramStateRef ProcessZeroAllocCheck(CheckerContext &C, const Expr *E, + const unsigned IndexOfSizeArg, + ProgramStateRef State, + Optional<SVal> RetVal = None); + + /// Model functions with the ownership_returns attribute. + /// + /// User-defined function may have the ownership_returns attribute, which + /// annotates that the function returns with an object that was allocated on + /// the heap, and passes the ownertship to the callee. + /// + /// void __attribute((ownership_returns(malloc, 1))) *my_malloc(size_t); + /// + /// It has two parameters: + /// - first: name of the resource (e.g. 'malloc') + /// - (OPTIONAL) second: size of the allocated region + /// + /// \param [in] CE The expression that allocates memory. + /// \param [in] Att The ownership_returns attribute. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after allocation. ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att, ProgramStateRef State) const; + + /// Models memory allocation. + /// + /// \param [in] CE The expression that allocates memory. + /// \param [in] SizeEx Size of the memory that needs to be allocated. + /// \param [in] Init The value the allocated memory needs to be initialized. + /// with. For example, \c calloc initializes the allocated memory to 0, + /// malloc leaves it undefined. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after allocation. static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, const Expr *SizeEx, SVal Init, ProgramStateRef State, AllocationFamily Family = AF_Malloc); + + /// Models memory allocation. + /// + /// \param [in] CE The expression that allocates memory. + /// \param [in] Size Size of the memory that needs to be allocated. + /// \param [in] Init The value the allocated memory needs to be initialized. + /// with. For example, \c calloc initializes the allocated memory to 0, + /// malloc leaves it undefined. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after allocation. static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, - SVal SizeEx, SVal Init, + SVal Size, SVal Init, ProgramStateRef State, AllocationFamily Family = AF_Malloc); @@ -325,54 +474,125 @@ private: performKernelMalloc(const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const; - /// Update the RefState to reflect the new memory allocation. - /// The optional \p RetVal parameter specifies the newly allocated pointer - /// value; if unspecified, the value of expression \p E is used. - static ProgramStateRef - MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, - AllocationFamily Family = AF_Malloc, - Optional<SVal> RetVal = None); - + /// Model functions with the ownership_takes and ownership_holds attributes. + /// + /// User-defined function may have the ownership_takes and/or ownership_holds + /// attributes, which annotates that the function frees the memory passed as a + /// parameter. + /// + /// void __attribute((ownership_takes(malloc, 1))) my_free(void *); + /// void __attribute((ownership_holds(malloc, 1))) my_hold(void *); + /// + /// They have two parameters: + /// - first: name of the resource (e.g. 'malloc') + /// - second: index of the parameter the attribute applies to + /// + /// \param [in] CE The expression that frees memory. + /// \param [in] Att The ownership_takes or ownership_holds attribute. + /// \param [in] State The \c ProgramState right before allocation. + /// \returns The ProgramState right after deallocation. ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, const OwnershipAttr* Att, ProgramStateRef State) const; + + /// Models memory deallocation. + /// + /// \param [in] CE The expression that frees memory. + /// \param [in] State The \c ProgramState right before allocation. + /// \param [in] Num Index of the argument that needs to be freed. This is + /// normally 0, but for custom free functions it may be different. + /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds + /// attribute. + /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known + /// to have been allocated, or in other words, the symbol to be freed was + /// registered as allocated by this checker. In the following case, \c ptr + /// isn't known to be allocated. + /// void Haha(int *ptr) { + /// ptr = realloc(ptr, 67); + /// // ... + /// } + /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function + /// we're modeling returns with Null on failure. + /// \returns The ProgramState right after deallocation. ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, - ProgramStateRef state, unsigned Num, - bool Hold, - bool &ReleasedAllocated, + ProgramStateRef State, unsigned Num, bool Hold, + bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure = false) const; - ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg, - const Expr *ParentExpr, - ProgramStateRef State, - bool Hold, - bool &ReleasedAllocated, + + /// Models memory deallocation. + /// + /// \param [in] ArgExpr The variable who's pointee needs to be freed. + /// \param [in] ParentExpr The expression that frees the memory. + /// \param [in] State The \c ProgramState right before allocation. + /// normally 0, but for custom free functions it may be different. + /// \param [in] Hold Whether the parameter at \p Index has the ownership_holds + /// attribute. + /// \param [out] IsKnownToBeAllocated Whether the memory to be freed is known + /// to have been allocated, or in other words, the symbol to be freed was + /// registered as allocated by this checker. In the following case, \c ptr + /// isn't known to be allocated. + /// void Haha(int *ptr) { + /// ptr = realloc(ptr, 67); + /// // ... + /// } + /// \param [in] ReturnsNullOnFailure Whether the memory deallocation function + /// we're modeling returns with Null on failure. + /// \returns The ProgramState right after deallocation. + ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *ArgExpr, + const Expr *ParentExpr, ProgramStateRef State, + bool Hold, bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure = false) const; + // TODO: Needs some refactoring, as all other deallocation modeling + // functions are suffering from out parameters and messy code due to how + // realloc is handled. + // + /// Models memory reallocation. + /// + /// \param [in] CE The expression that reallocated memory + /// \param [in] ShouldFreeOnFail Whether if reallocation fails, the supplied + /// memory should be freed. + /// \param [in] State The \c ProgramState right before reallocation. + /// \param [in] SuffixWithN Whether the reallocation function we're modeling + /// has an '_n' suffix, such as g_realloc_n. + /// \returns The ProgramState right after reallocation. ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, - bool FreesMemOnFailure, - ProgramStateRef State, + bool ShouldFreeOnFail, ProgramStateRef State, bool SuffixWithN = false) const; + + /// Evaluates the buffer size that needs to be allocated. + /// + /// \param [in] Blocks The amount of blocks that needs to be allocated. + /// \param [in] BlockBytes The size of a block. + /// \returns The symbolic value of \p Blocks * \p BlockBytes. static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, const Expr *BlockBytes); + + /// Models zero initialized array allocation. + /// + /// \param [in] CE The expression that reallocated memory + /// \param [in] State The \c ProgramState right before reallocation. + /// \returns The ProgramState right after allocation. static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, ProgramStateRef State); - /// Check if the memory associated with this symbol was released. - bool isReleased(SymbolRef Sym, CheckerContext &C) const; - /// See if deallocation happens in a suspicious context. If so, escape the /// pointers that otherwise would have been deallocated and return true. bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE, CheckerContext &C) const; + /// If in \p S \p Sym is used, check whether \p Sym was already freed. bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; + /// If in \p S \p Sym is used, check whether \p Sym was allocated as a zero + /// sized memory region. void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; + /// If in \p S \p Sym is being freed, check whether \p Sym was already freed. bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const; - /// Check if the function is known free memory, or if it is + /// Check if the function is known to free memory, or if it is /// "interesting" and should be modeled explicitly. /// /// \param [out] EscapingSymbol A function might not free memory in general, @@ -386,12 +606,12 @@ private: ProgramStateRef State, SymbolRef &EscapingSymbol) const; - // Implementation of the checkPointerEscape callbacks. + /// Implementation of the checkPointerEscape callbacks. ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - bool(*CheckRefState)(const RefState*)) const; + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool IsConstPointerEscape) const; // Implementation of the checkPreStmt and checkEndFunction callbacks. void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const; @@ -410,6 +630,7 @@ private: ///@} static bool SummarizeValue(raw_ostream &os, SVal V); static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); + void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, const Expr *DeallocExpr) const; void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, @@ -435,143 +656,142 @@ private: /// Find the location of the allocation for Sym on the path leading to the /// exploded node N. - LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, - CheckerContext &C) const; + static LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C); void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const; +}; - /// The bug visitor which allows us to print extra diagnostics along the - /// BugReport path. For example, showing the allocation site of the leaked - /// region. - class MallocBugVisitor final : public BugReporterVisitor { - protected: - enum NotificationMode { - Normal, - ReallocationFailed - }; - - // The allocated region symbol tracked by the main analysis. - SymbolRef Sym; - - // The mode we are in, i.e. what kind of diagnostics will be emitted. - NotificationMode Mode; - - // A symbol from when the primary region should have been reallocated. - SymbolRef FailedReallocSymbol; - - // A C++ destructor stack frame in which memory was released. Used for - // miscellaneous false positive suppression. - const StackFrameContext *ReleaseDestructorLC; - - bool IsLeak; - - public: - MallocBugVisitor(SymbolRef S, bool isLeak = false) - : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), - ReleaseDestructorLC(nullptr), IsLeak(isLeak) {} - - static void *getTag() { - static int Tag = 0; - return &Tag; - } +//===----------------------------------------------------------------------===// +// Definition of MallocBugVisitor. +//===----------------------------------------------------------------------===// - void Profile(llvm::FoldingSetNodeID &ID) const override { - ID.AddPointer(getTag()); - ID.AddPointer(Sym); - } +/// The bug visitor which allows us to print extra diagnostics along the +/// BugReport path. For example, showing the allocation site of the leaked +/// region. +class MallocBugVisitor final : public BugReporterVisitor { +protected: + enum NotificationMode { Normal, ReallocationFailed }; - inline bool isAllocated(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // Did not track -> allocated. Other state (released) -> allocated. - return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && - (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && - (!SPrev || !(SPrev->isAllocated() || - SPrev->isAllocatedOfSizeZero()))); - } + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; - inline bool isReleased(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // Did not track -> released. Other state (allocated) -> released. - // The statement associated with the release might be missing. - bool IsReleased = (S && S->isReleased()) && - (!SPrev || !SPrev->isReleased()); - assert(!IsReleased || - (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) || - (!Stmt && S->getAllocationFamily() == AF_InnerBuffer)); - return IsReleased; - } + // The mode we are in, i.e. what kind of diagnostics will be emitted. + NotificationMode Mode; - inline bool isRelinquished(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // Did not track -> relinquished. Other state (allocated) -> relinquished. - return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || - isa<ObjCPropertyRefExpr>(Stmt)) && - (S && S->isRelinquished()) && - (!SPrev || !SPrev->isRelinquished())); - } + // A symbol from when the primary region should have been reallocated. + SymbolRef FailedReallocSymbol; - inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, - const Stmt *Stmt) { - // If the expression is not a call, and the state change is - // released -> allocated, it must be the realloc return value - // check. If we have to handle more cases here, it might be cleaner just - // to track this extra bit in the state itself. - return ((!Stmt || !isa<CallExpr>(Stmt)) && - (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && - (SPrev && !(SPrev->isAllocated() || - SPrev->isAllocatedOfSizeZero()))); - } + // A C++ destructor stack frame in which memory was released. Used for + // miscellaneous false positive suppression. + const StackFrameContext *ReleaseDestructorLC; - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + bool IsLeak; - std::shared_ptr<PathDiagnosticPiece> - getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, - BugReport &BR) override { - if (!IsLeak) - return nullptr; +public: + MallocBugVisitor(SymbolRef S, bool isLeak = false) + : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), + ReleaseDestructorLC(nullptr), IsLeak(isLeak) {} + + static void *getTag() { + static int Tag = 0; + return &Tag; + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + ID.AddPointer(Sym); + } + + /// Did not track -> allocated. Other state (released) -> allocated. + static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev, + const Stmt *Stmt) { + return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && + (RSCurr && + (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) && + (!RSPrev || + !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero()))); + } + + /// Did not track -> released. Other state (allocated) -> released. + /// The statement associated with the release might be missing. + static inline bool isReleased(const RefState *RSCurr, const RefState *RSPrev, + const Stmt *Stmt) { + bool IsReleased = + (RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased()); + assert(!IsReleased || + (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) || + (!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer)); + return IsReleased; + } + + /// Did not track -> relinquished. Other state (allocated) -> relinquished. + static inline bool isRelinquished(const RefState *RSCurr, + const RefState *RSPrev, const Stmt *Stmt) { + return (Stmt && + (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || + isa<ObjCPropertyRefExpr>(Stmt)) && + (RSCurr && RSCurr->isRelinquished()) && + (!RSPrev || !RSPrev->isRelinquished())); + } + + /// If the expression is not a call, and the state change is + /// released -> allocated, it must be the realloc return value + /// check. If we have to handle more cases here, it might be cleaner just + /// to track this extra bit in the state itself. + static inline bool hasReallocFailed(const RefState *RSCurr, + const RefState *RSPrev, + const Stmt *Stmt) { + return ((!Stmt || !isa<CallExpr>(Stmt)) && + (RSCurr && + (RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) && + (RSPrev && + !(RSPrev->isAllocated() || RSPrev->isAllocatedOfSizeZero()))); + } + + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; + + PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + PathSensitiveBugReport &BR) override { + if (!IsLeak) + return nullptr; - PathDiagnosticLocation L = - PathDiagnosticLocation::createEndOfPath(EndPathNode, - BRC.getSourceManager()); - // Do not add the statement itself as a range in case of leak. - return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), - false); - } + PathDiagnosticLocation L = BR.getLocation(); + // Do not add the statement itself as a range in case of leak. + return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), + false); + } - private: - class StackHintGeneratorForReallocationFailed - : public StackHintGeneratorForSymbol { - public: - StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) +private: + class StackHintGeneratorForReallocationFailed + : public StackHintGeneratorForSymbol { + public: + StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) : StackHintGeneratorForSymbol(S, M) {} - std::string getMessageForArg(const Expr *ArgE, - unsigned ArgIndex) override { - // Printed parameters start at 1, not 0. - ++ArgIndex; + std::string getMessageForArg(const Expr *ArgE, unsigned ArgIndex) override { + // Printed parameters start at 1, not 0. + ++ArgIndex; - SmallString<200> buf; - llvm::raw_svector_ostream os(buf); + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); - os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) - << " parameter failed"; + os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) + << " parameter failed"; - return os.str(); - } + return os.str(); + } - std::string getMessageForReturn(const CallExpr *CallExpr) override { - return "Reallocation of returned value failed"; - } - }; + std::string getMessageForReturn(const CallExpr *CallExpr) override { + return "Reallocation of returned value failed"; + } }; }; -} // end anonymous namespace -REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) -REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) -REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) +} // end anonymous namespace // A map from the freed symbol to the symbol representing the return value of // the free function. @@ -591,7 +811,11 @@ public: }; } // end anonymous namespace -void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { +//===----------------------------------------------------------------------===// +// Methods of MemFunctionInfoTy. +//===----------------------------------------------------------------------===// + +void MemFunctionInfoTy::initIdentifierInfo(ASTContext &Ctx) const { if (II_malloc) return; II_alloca = &Ctx.Idents.get("alloca"); @@ -631,7 +855,8 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n"); } -bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { +bool MemFunctionInfoTy::isMemFunction(const FunctionDecl *FD, + ASTContext &C) const { if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any)) return true; @@ -647,10 +872,9 @@ bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { return false; } -bool MallocChecker::isCMemFunction(const FunctionDecl *FD, - ASTContext &C, - AllocationFamily Family, - MemoryOperationKind MemKind) const { +bool MemFunctionInfoTy::isCMemFunction(const FunctionDecl *FD, ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const { if (!FD) return false; @@ -703,7 +927,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, if (Family != AF_Malloc) return false; - if (IsOptimistic && FD->hasAttrs()) { + if (ShouldIncludeOwnershipAnnotatedFunctions && FD->hasAttrs()) { for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { @@ -718,11 +942,8 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD, return false; } - -// Tells if the callee is one of the builtin new/delete operators, including -// placement operators and other standard overloads. -bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, - ASTContext &C) const { +bool MemFunctionInfoTy::isStandardNewDelete(const FunctionDecl *FD, + ASTContext &C) const { if (!FD) return false; @@ -738,6 +959,10 @@ bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, return !L.isValid() || C.getSourceManager().isInSystemHeader(L); } +//===----------------------------------------------------------------------===// +// Methods of MallocChecker and MallocBugVisitor. +//===----------------------------------------------------------------------===// + llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc( const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const { // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: @@ -836,28 +1061,35 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { return; ProgramStateRef State = C.getState(); - bool ReleasedAllocatedMemory = false; + bool IsKnownToBeAllocatedMemory = false; if (FD->getKind() == Decl::Function) { - initIdentifierInfo(C.getASTContext()); + MemFunctionInfo.initIdentifierInfo(C.getASTContext()); IdentifierInfo *FunI = FD->getIdentifier(); - if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) { - if (CE->getNumArgs() < 1) + if (FunI == MemFunctionInfo.II_malloc || + FunI == MemFunctionInfo.II_g_malloc || + FunI == MemFunctionInfo.II_g_try_malloc) { + switch (CE->getNumArgs()) { + default: return; - if (CE->getNumArgs() < 3) { + case 1: + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = ProcessZeroAllocCheck(C, CE, 0, State); + break; + case 2: State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - if (CE->getNumArgs() == 1) - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (CE->getNumArgs() == 3) { + break; + case 3: llvm::Optional<ProgramStateRef> MaybeState = performKernelMalloc(CE, C, State); if (MaybeState.hasValue()) State = MaybeState.getValue(); else State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + break; } - } else if (FunI == II_kmalloc) { + } else if (FunI == MemFunctionInfo.II_kmalloc) { if (CE->getNumArgs() < 1) return; llvm::Optional<ProgramStateRef> MaybeState = @@ -866,100 +1098,116 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { State = MaybeState.getValue(); else State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); - } else if (FunI == II_valloc) { + } else if (FunI == MemFunctionInfo.II_valloc) { if (CE->getNumArgs() < 1) 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 || - FunI == II_g_try_realloc) { - State = ReallocMemAux(C, CE, false, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_reallocf) { - State = ReallocMemAux(C, CE, true, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_calloc) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + } else if (FunI == MemFunctionInfo.II_realloc || + FunI == MemFunctionInfo.II_g_realloc || + FunI == MemFunctionInfo.II_g_try_realloc) { + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_reallocf) { + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ true, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_calloc) { State = CallocMem(C, CE, State); - State = ProcessZeroAllocation(C, CE, 0, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_free || FunI == II_g_free || FunI == II_kfree) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_free || + FunI == MemFunctionInfo.II_g_free || + FunI == MemFunctionInfo.II_kfree) { if (suppressDeallocationsInSuspiciousContexts(CE, C)) return; - State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - } else if (FunI == II_strdup || FunI == II_win_strdup || - FunI == II_wcsdup || FunI == II_win_wcsdup) { + State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + } else if (FunI == MemFunctionInfo.II_strdup || + FunI == MemFunctionInfo.II_win_strdup || + FunI == MemFunctionInfo.II_wcsdup || + FunI == MemFunctionInfo.II_win_wcsdup) { State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_strndup) { + } else if (FunI == MemFunctionInfo.II_strndup) { State = MallocUpdateRefState(C, CE, State); - } else if (FunI == II_alloca || FunI == II_win_alloca) { + } else if (FunI == MemFunctionInfo.II_alloca || + FunI == MemFunctionInfo.II_win_alloca) { if (CE->getNumArgs() < 1) return; State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_Alloca); - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (isStandardNewDelete(FD, C.getASTContext())) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + } else if (MemFunctionInfo.isStandardNewDelete(FD, C.getASTContext())) { // Process direct calls to operator new/new[]/delete/delete[] functions // as distinct from new/new[]/delete/delete[] expressions that are // processed by the checkPostStmt callbacks for CXXNewExpr and // CXXDeleteExpr. - OverloadedOperatorKind K = FD->getOverloadedOperator(); - if (K == OO_New) { + switch (FD->getOverloadedOperator()) { + case OO_New: State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNew); - State = ProcessZeroAllocation(C, CE, 0, State); - } - else if (K == OO_Array_New) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + break; + case OO_Array_New: State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, AF_CXXNewArray); - State = ProcessZeroAllocation(C, CE, 0, State); - } - else if (K == OO_Delete || K == OO_Array_Delete) - State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - else + State = ProcessZeroAllocCheck(C, CE, 0, State); + break; + case OO_Delete: + case OO_Array_Delete: + State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + break; + default: llvm_unreachable("not a new/delete operator"); - } else if (FunI == II_if_nameindex) { + } + } else if (FunI == MemFunctionInfo.II_if_nameindex) { // Should we model this differently? We can allocate a fixed number of // elements with zeros in the last one. State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, AF_IfNameIndex); - } else if (FunI == II_if_freenameindex) { - State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); - } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) { + } else if (FunI == MemFunctionInfo.II_if_freenameindex) { + State = FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocatedMemory); + } else if (FunI == MemFunctionInfo.II_g_malloc0 || + FunI == MemFunctionInfo.II_g_try_malloc0) { if (CE->getNumArgs() < 1) return; SValBuilder &svalBuilder = C.getSValBuilder(); SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); - State = ProcessZeroAllocation(C, CE, 0, State); - } else if (FunI == II_g_memdup) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + } else if (FunI == MemFunctionInfo.II_g_memdup) { if (CE->getNumArgs() < 2) 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 || - FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_g_malloc_n || + FunI == MemFunctionInfo.II_g_try_malloc_n || + FunI == MemFunctionInfo.II_g_malloc0_n || + FunI == MemFunctionInfo.II_g_try_malloc0_n) { if (CE->getNumArgs() < 2) return; SVal Init = UndefinedVal(); - if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + if (FunI == MemFunctionInfo.II_g_malloc0_n || + FunI == MemFunctionInfo.II_g_try_malloc0_n) { SValBuilder &SB = C.getSValBuilder(); Init = SB.makeZeroVal(SB.getContext().CharTy); } SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); State = MallocMemAux(C, CE, TotalSize, Init, State); - State = ProcessZeroAllocation(C, CE, 0, State); - State = ProcessZeroAllocation(C, CE, 1, State); - } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) { + State = ProcessZeroAllocCheck(C, CE, 0, State); + State = ProcessZeroAllocCheck(C, CE, 1, State); + } else if (FunI == MemFunctionInfo.II_g_realloc_n || + FunI == MemFunctionInfo.II_g_try_realloc_n) { if (CE->getNumArgs() < 3) return; - State = ReallocMemAux(C, CE, false, State, true); - State = ProcessZeroAllocation(C, CE, 1, State); - State = ProcessZeroAllocation(C, CE, 2, State); + State = ReallocMemAux(C, CE, /*ShouldFreeOnFail*/ false, State, + /*SuffixWithN*/ true); + State = ProcessZeroAllocCheck(C, CE, 1, State); + State = ProcessZeroAllocCheck(C, CE, 2, State); } } - if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) { + if (MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions || + ChecksEnabled[CK_MismatchedDeallocatorChecker]) { // Check all the attributes, if there are any. // There can be multiple of these attributes. if (FD->hasAttrs()) @@ -979,9 +1227,9 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { } // Performs a 0-sized allocations check. -ProgramStateRef MallocChecker::ProcessZeroAllocation( - CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg, - ProgramStateRef State, Optional<SVal> RetVal) const { +ProgramStateRef MallocChecker::ProcessZeroAllocCheck( + CheckerContext &C, const Expr *E, const unsigned IndexOfSizeArg, + ProgramStateRef State, Optional<SVal> RetVal) { if (!State) return nullptr; @@ -991,7 +1239,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation( const Expr *Arg = nullptr; if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { - Arg = CE->getArg(AllocationSizeArg); + Arg = CE->getArg(IndexOfSizeArg); } else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { if (NE->isArray()) @@ -1053,7 +1301,9 @@ static QualType getDeepPointeeType(QualType T) { return Result; } -static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { +/// \returns true if the constructor invoked by \p NE has an argument of a +/// pointer/reference to a record type. +static bool hasNonTrivialConstructorCall(const CXXNewExpr *NE) { const CXXConstructExpr *ConstructE = NE->getConstructExpr(); if (!ConstructE) @@ -1083,11 +1333,17 @@ static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { void MallocChecker::processNewAllocation(const CXXNewExpr *NE, CheckerContext &C, SVal Target) const { - if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) + if (!MemFunctionInfo.isStandardNewDelete(NE->getOperatorNew(), + C.getASTContext())) return; - ParentMap &PM = C.getLocationContext()->getParentMap(); - if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE)) + const ParentMap &PM = C.getLocationContext()->getParentMap(); + + // Non-trivial constructors have a chance to escape 'this', but marking all + // invocations of trivial constructors as escaped would cause too great of + // reduction of true positives, so let's just do that for constructors that + // have an argument of a pointer-to-record type. + if (!PM.isConsumedExpr(NE) && hasNonTrivialConstructorCall(NE)) return; ProgramStateRef State = C.getState(); @@ -1098,7 +1354,7 @@ void MallocChecker::processNewAllocation(const CXXNewExpr *NE, State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray : AF_CXXNew, Target); State = addExtentSize(C, NE, State, Target); - State = ProcessZeroAllocation(C, NE, 0, State, Target); + State = ProcessZeroAllocCheck(C, NE, 0, State, Target); C.addTransition(State); } @@ -1132,14 +1388,13 @@ ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C, // Store the extent size for the (symbolic)region // containing the elements. Region = Target.getAsRegion() - ->getAs<SubRegion>() + ->castAs<SubRegion>() ->StripCasts() - ->getAs<SubRegion>(); + ->castAs<SubRegion>(); } else { ElementCount = svalBuilder.makeIntVal(1, true); - Region = Target.getAsRegion()->getAs<SubRegion>(); + Region = Target.getAsRegion()->castAs<SubRegion>(); } - assert(Region); // Set the region's extent equal to the Size in Bytes. QualType ElementType = NE->getAllocatedType(); @@ -1167,13 +1422,14 @@ void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) checkUseAfterFree(Sym, C, DE->getArgument()); - if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext())) + if (!MemFunctionInfo.isStandardNewDelete(DE->getOperatorDelete(), + C.getASTContext())) return; ProgramStateRef State = C.getState(); - bool ReleasedAllocated; + bool IsKnownToBeAllocated; State = FreeMemAux(C, DE->getArgument(), DE, State, - /*Hold*/false, ReleasedAllocated); + /*Hold*/ false, IsKnownToBeAllocated); C.addTransition(State); } @@ -1213,11 +1469,11 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, if (!*FreeWhenDone) return; - bool ReleasedAllocatedMemory; - ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), - Call.getOriginExpr(), C.getState(), - /*Hold=*/true, ReleasedAllocatedMemory, - /*ReturnsNullOnFailure=*/true); + bool IsKnownToBeAllocatedMemory; + ProgramStateRef State = + FreeMemAux(C, Call.getArgExpr(0), Call.getOriginExpr(), C.getState(), + /*Hold=*/true, IsKnownToBeAllocatedMemory, + /*RetNullOnFailure=*/true); C.addTransition(State); } @@ -1229,7 +1485,7 @@ MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, if (!State) return nullptr; - if (Att->getModule() != II_malloc) + if (Att->getModule() != MemFunctionInfo.II_malloc) return nullptr; OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); @@ -1295,11 +1551,10 @@ ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, return MallocUpdateRefState(C, CE, State, Family); } -ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, - const Expr *E, - ProgramStateRef State, - AllocationFamily Family, - Optional<SVal> RetVal) { +static ProgramStateRef MallocUpdateRefState(CheckerContext &C, const Expr *E, + ProgramStateRef State, + AllocationFamily Family, + Optional<SVal> RetVal) { if (!State) return nullptr; @@ -1327,27 +1582,24 @@ ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, if (!State) return nullptr; - if (Att->getModule() != II_malloc) + if (Att->getModule() != MemFunctionInfo.II_malloc) return nullptr; - bool ReleasedAllocated = false; + bool IsKnownToBeAllocated = false; for (const auto &Arg : Att->args()) { ProgramStateRef StateI = FreeMemAux( C, CE, State, Arg.getASTIndex(), - Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated); + Att->getOwnKind() == OwnershipAttr::Holds, IsKnownToBeAllocated); if (StateI) State = StateI; } return State; } -ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, - const CallExpr *CE, - ProgramStateRef State, - unsigned Num, - bool Hold, - bool &ReleasedAllocated, +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State, unsigned Num, + bool Hold, bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure) const { if (!State) return nullptr; @@ -1355,8 +1607,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, if (CE->getNumArgs() < (Num + 1)) return nullptr; - return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, - ReleasedAllocated, ReturnsNullOnFailure); + return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, IsKnownToBeAllocated, + ReturnsNullOnFailure); } /// Checks if the previous call to free on the given symbol failed - if free @@ -1374,8 +1626,10 @@ static bool didPreviousFreeFail(ProgramStateRef State, return false; } -AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, - const Stmt *S) const { +static AllocationFamily +getAllocationFamily(const MemFunctionInfoTy &MemFunctionInfo, CheckerContext &C, + const Stmt *S) { + if (!S) return AF_None; @@ -1387,10 +1641,11 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, ASTContext &Ctx = C.getASTContext(); - if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any)) + if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc, + MemoryOperationKind::MOK_Any)) return AF_Malloc; - if (isStandardNewDelete(FD, Ctx)) { + if (MemFunctionInfo.isStandardNewDelete(FD, Ctx)) { OverloadedOperatorKind Kind = FD->getOverloadedOperator(); if (Kind == OO_New || Kind == OO_Delete) return AF_CXXNew; @@ -1398,10 +1653,12 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_CXXNewArray; } - if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) + if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Any)) return AF_IfNameIndex; - if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any)) + if (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Alloca, + MemoryOperationKind::MOK_Any)) return AF_Alloca; return AF_None; @@ -1419,8 +1676,8 @@ AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, return AF_None; } -bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, - const Expr *E) const { +static bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E) { if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { // FIXME: This doesn't handle indirect calls. const FunctionDecl *FD = CE->getDirectCallee(); @@ -1459,9 +1716,10 @@ bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, return false; } -void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, - const Expr *E) const { - AllocationFamily Family = getAllocationFamily(C, E); +static void printExpectedAllocName(raw_ostream &os, + const MemFunctionInfoTy &MemFunctionInfo, + CheckerContext &C, const Expr *E) { + AllocationFamily Family = getAllocationFamily(MemFunctionInfo, C, E); switch(Family) { case AF_Malloc: os << "malloc()"; return; @@ -1474,8 +1732,7 @@ void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, } } -void MallocChecker::printExpectedDeallocName(raw_ostream &os, - AllocationFamily Family) const { +static void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) { switch(Family) { case AF_Malloc: os << "free()"; return; case AF_CXXNew: os << "'delete'"; return; @@ -1490,9 +1747,8 @@ void MallocChecker::printExpectedDeallocName(raw_ostream &os, ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const Expr *ArgExpr, const Expr *ParentExpr, - ProgramStateRef State, - bool Hold, - bool &ReleasedAllocated, + ProgramStateRef State, bool Hold, + bool &IsKnownToBeAllocated, bool ReturnsNullOnFailure) const { if (!State) @@ -1566,6 +1822,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, const RefState *RsBase = State->get<RegionState>(SymBase); SymbolRef PreviousRetStatusSymbol = nullptr; + IsKnownToBeAllocated = + RsBase && (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero()); + if (RsBase) { // Memory returned by alloca() shouldn't be freed. @@ -1588,7 +1847,8 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, // Check if an expected deallocation function matches the real one. bool DeallocMatchesAlloc = - RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); + RsBase->getAllocationFamily() == + getAllocationFamily(MemFunctionInfo, C, ParentExpr); if (!DeallocMatchesAlloc) { ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), ParentExpr, RsBase, SymBase, Hold); @@ -1614,9 +1874,6 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, return nullptr; } - ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() || - RsBase->isAllocatedOfSizeZero()); - // Clean out the info on previous call to free return info. State = State->remove<FreeReturnValue>(SymBase); @@ -1631,8 +1888,9 @@ ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, } } - AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() - : getAllocationFamily(C, ParentExpr); + AllocationFamily Family = + RsBase ? RsBase->getAllocationFamily() + : getAllocationFamily(MemFunctionInfo, C, ParentExpr); // Normal free. if (Hold) return State->set<RegionState>(SymBase, @@ -1682,8 +1940,8 @@ Optional<MallocChecker::CheckKind> MallocChecker::getCheckIfTracked(CheckerContext &C, const Stmt *AllocDeallocStmt, bool IsALeakCheck) const { - return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt), - IsALeakCheck); + return getCheckIfTracked( + getAllocationFamily(MemFunctionInfo, C, AllocDeallocStmt), IsALeakCheck); } Optional<MallocChecker::CheckKind> @@ -1821,9 +2079,10 @@ void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, else os << "not memory allocated by "; - printExpectedAllocName(os, C, DeallocExpr); + printExpectedAllocName(os, MemFunctionInfo, C, DeallocExpr); - auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind], + os.str(), N); R->markInteresting(MR); R->addRange(Range); C.emitReport(std::move(R)); @@ -1847,7 +2106,7 @@ void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, BT_FreeAlloca[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Free alloca()", categories::MemoryError)); - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *BT_FreeAlloca[*CheckKind], "Memory allocated by alloca() should not be deallocated", N); R->markInteresting(ArgVal.getAsRegion()); @@ -1903,10 +2162,11 @@ void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, os << ", not " << DeallocOs.str(); } - auto R = llvm::make_unique<BugReport>(*BT_MismatchedDealloc, os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT_MismatchedDealloc, + os.str(), N); R->markInteresting(Sym); R->addRange(Range); - R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); C.emitReport(std::move(R)); } } @@ -1962,7 +2222,8 @@ void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, else os << "allocated memory"; - auto R = llvm::make_unique<BugReport>(*BT_OffsetFree[*CheckKind], os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT_OffsetFree[*CheckKind], + os.str(), N); R->markInteresting(MR->getBaseRegion()); R->addRange(Range); C.emitReport(std::move(R)); @@ -1988,15 +2249,16 @@ void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, AllocationFamily AF = C.getState()->get<RegionState>(Sym)->getAllocationFamily(); - auto R = llvm::make_unique<BugReport>(*BT_UseFree[*CheckKind], + auto R = std::make_unique<PathSensitiveBugReport>( + *BT_UseFree[*CheckKind], AF == AF_InnerBuffer - ? "Inner pointer of container used after re/deallocation" - : "Use of memory after it is freed", + ? "Inner pointer of container used after re/deallocation" + : "Use of memory after it is freed", N); R->markInteresting(Sym); R->addRange(Range); - R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); if (AF == AF_InnerBuffer) R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); @@ -2022,7 +2284,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, BT_DoubleFree[*CheckKind].reset(new BugType( CheckNames[*CheckKind], "Double free", categories::MemoryError)); - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *BT_DoubleFree[*CheckKind], (Released ? "Attempt to free released memory" : "Attempt to free non-owned memory"), @@ -2031,7 +2293,7 @@ void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, R->markInteresting(Sym); if (PrevSym) R->markInteresting(PrevSym); - R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); C.emitReport(std::move(R)); } } @@ -2051,11 +2313,11 @@ void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { "Double delete", categories::MemoryError)); - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *BT_DoubleDelete, "Attempt to delete released memory", N); R->markInteresting(Sym); - R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); C.emitReport(std::move(R)); } } @@ -2079,13 +2341,13 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C, new BugType(CheckNames[*CheckKind], "Use of zero allocated", categories::MemoryError)); - auto R = llvm::make_unique<BugReport>(*BT_UseZerroAllocated[*CheckKind], - "Use of zero-allocated memory", N); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT_UseZerroAllocated[*CheckKind], "Use of zero-allocated memory", N); R->addRange(Range); if (Sym) { R->markInteresting(Sym); - R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + R->addVisitor(std::make_unique<MallocBugVisitor>(Sym)); } C.emitReport(std::move(R)); } @@ -2119,7 +2381,8 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, Os << " is a function pointer"; - auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], Os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT_BadFree[*CheckKind], + Os.str(), N); R->markInteresting(MR); R->addRange(Range); C.emitReport(std::move(R)); @@ -2128,7 +2391,7 @@ void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, const CallExpr *CE, - bool FreesOnFail, + bool ShouldFreeOnFail, ProgramStateRef State, bool SuffixWithN) const { if (!State) @@ -2193,33 +2456,32 @@ ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, if (!FromPtr || !ToPtr) return nullptr; - bool ReleasedAllocated = false; + bool IsKnownToBeAllocated = false; // If the size is 0, free the memory. if (SizeIsZero) - 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 - // any constrains on the output pointer. + // 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 + // any constrains on the output pointer. + if (ProgramStateRef stateFree = + FreeMemAux(C, CE, StateSizeIsZero, 0, false, IsKnownToBeAllocated)) return stateFree; - } // Default behavior. if (ProgramStateRef stateFree = - FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) { + FreeMemAux(C, CE, State, 0, false, IsKnownToBeAllocated)) { ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize, UnknownVal(), stateFree); if (!stateRealloc) return nullptr; - ReallocPairKind Kind = RPToBeFreedAfterFailure; - if (FreesOnFail) - Kind = RPIsFreeOnFailure; - else if (!ReleasedAllocated) - Kind = RPDoNotTrackAfterFailure; + OwnershipAfterReallocKind Kind = OAR_ToBeFreedAfterFailure; + if (ShouldFreeOnFail) + Kind = OAR_FreeOnFailure; + else if (!IsKnownToBeAllocated) + Kind = OAR_DoNotTrackAfterFailure; // Record the info about the reallocated symbol so that we could properly // process failed reallocation. @@ -2247,9 +2509,9 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, return MallocMemAux(C, CE, TotalSize, zeroVal, State); } -LeakInfo -MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, - CheckerContext &C) const { +MallocChecker::LeakInfo MallocChecker::getAllocationSite(const ExplodedNode *N, + SymbolRef Sym, + CheckerContext &C) { const LocationContext *LeakContext = N->getLocationContext(); // Walk the ExplodedGraph backwards and find the first node that referred to // the tracked symbol. @@ -2329,7 +2591,7 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, const MemRegion *Region = nullptr; std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C); - const Stmt *AllocationStmt = PathDiagnosticLocation::getStmt(AllocNode); + const Stmt *AllocationStmt = AllocNode->getStmtForDiagnostics(); if (AllocationStmt) LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt, C.getSourceManager(), @@ -2344,11 +2606,11 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, os << "Potential memory leak"; } - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, AllocNode->getLocationContext()->getDecl()); R->markInteresting(Sym); - R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym, true)); + R->addVisitor(std::make_unique<MallocBugVisitor>(Sym, true)); C.emitReport(std::move(R)); } @@ -2430,9 +2692,10 @@ void MallocChecker::checkPreCall(const CallEvent &Call, ASTContext &Ctx = C.getASTContext(); if (ChecksEnabled[CK_MallocChecker] && - (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || - isCMemFunction(FD, Ctx, AF_IfNameIndex, - MemoryOperationKind::MOK_Free))) + (MemFunctionInfo.isCMemFunction(FD, Ctx, AF_Malloc, + MemoryOperationKind::MOK_Free) || + MemFunctionInfo.isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Free))) return; } @@ -2535,7 +2798,7 @@ void MallocChecker::checkPostStmt(const BlockExpr *BE, C.addTransition(state); } -bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { +static bool isReleased(SymbolRef Sym, CheckerContext &C) { assert(Sym); const RefState *RS = C.getState()->get<RegionState>(Sym); return (RS && RS->isReleased()); @@ -2640,13 +2903,17 @@ ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, SymbolRef ReallocSym = I.getData().ReallocatedSym; if (const RefState *RS = state->get<RegionState>(ReallocSym)) { if (RS->isReleased()) { - if (I.getData().Kind == RPToBeFreedAfterFailure) + switch (I.getData().Kind) { + case OAR_ToBeFreedAfterFailure: state = state->set<RegionState>(ReallocSym, RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt())); - else if (I.getData().Kind == RPDoNotTrackAfterFailure) + break; + case OAR_DoNotTrackAfterFailure: state = state->remove<RegionState>(ReallocSym); - else - assert(I.getData().Kind == RPIsFreeOnFailure); + break; + default: + assert(I.getData().Kind == OAR_FreeOnFailure); + } } } state = state->remove<ReallocPairs>(I.getKey()); @@ -2729,7 +2996,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( // If it's one of the allocation functions we can reason about, we model // its behavior explicitly. - if (isMemFunction(FD, ASTC)) + if (MemFunctionInfo.isMemFunction(FD, ASTC)) return false; // If it's not a system call, assume it frees memory. @@ -2821,35 +3088,32 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( return false; } -static bool retTrue(const RefState *RS) { - return true; -} - -static bool checkIfNewOrNewArrayFamily(const RefState *RS) { - return (RS->getAllocationFamily() == AF_CXXNewArray || - RS->getAllocationFamily() == AF_CXXNew); -} - ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const { - return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue); + return checkPointerEscapeAux(State, Escaped, Call, Kind, + /*IsConstPointerEscape*/ false); } ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State, const InvalidatedSymbols &Escaped, const CallEvent *Call, PointerEscapeKind Kind) const { + // If a const pointer escapes, it may not be freed(), but it could be deleted. return checkPointerEscapeAux(State, Escaped, Call, Kind, - &checkIfNewOrNewArrayFamily); + /*IsConstPointerEscape*/ true); } -ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, - const InvalidatedSymbols &Escaped, - const CallEvent *Call, - PointerEscapeKind Kind, - bool(*CheckRefState)(const RefState*)) const { +static bool checkIfNewOrNewArrayFamily(const RefState *RS) { + return (RS->getAllocationFamily() == AF_CXXNewArray || + RS->getAllocationFamily() == AF_CXXNew); +} + +ProgramStateRef MallocChecker::checkPointerEscapeAux( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind, + bool IsConstPointerEscape) const { // If we know that the call does not free memory, or we want to process the // call later, keep tracking the top level arguments. SymbolRef EscapingSymbol = nullptr; @@ -2868,12 +3132,10 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, if (EscapingSymbol && EscapingSymbol != sym) continue; - if (const RefState *RS = State->get<RegionState>(sym)) { - if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && - CheckRefState(RS)) { - State = State->set<RegionState>(sym, RefState::getEscaped(RS)); - } - } + if (const RefState *RS = State->get<RegionState>(sym)) + if (RS->isAllocated() || RS->isAllocatedOfSizeZero()) + if (!IsConstPointerEscape || checkIfNewOrNewArrayFamily(RS)) + State = State->set<RegionState>(sym, RefState::getEscaped(RS)); } return State; } @@ -2883,9 +3145,8 @@ static SymbolRef findFailedReallocSymbol(ProgramStateRef currState, ReallocPairsTy currMap = currState->get<ReallocPairs>(); ReallocPairsTy prevMap = prevState->get<ReallocPairs>(); - for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end(); - I != E; ++I) { - SymbolRef sym = I.getKey(); + for (const ReallocPairsTy::value_type &Pair : prevMap) { + SymbolRef sym = Pair.first; if (!currMap.lookup(sym)) return sym; } @@ -2906,19 +3167,19 @@ static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) { return false; } -std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( - const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { - +PathDiagnosticPieceRef MallocBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramStateRef state = N->getState(); ProgramStateRef statePrev = N->getFirstPred()->getState(); - const RefState *RS = state->get<RegionState>(Sym); + const RefState *RSCurr = state->get<RegionState>(Sym); const RefState *RSPrev = statePrev->get<RegionState>(Sym); - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); // When dealing with containers, we sometimes want to give a note // even if the statement is missing. - if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer)) + if (!S && (!RSCurr || RSCurr->getAllocationFamily() != AF_InnerBuffer)) return nullptr; const LocationContext *CurrentLC = N->getLocationContext(); @@ -2948,17 +3209,17 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( // Find out if this is an interesting point and what is the kind. StringRef Msg; - StackHintGeneratorForSymbol *StackHint = nullptr; + std::unique_ptr<StackHintGeneratorForSymbol> StackHint = nullptr; SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); if (Mode == Normal) { - if (isAllocated(RS, RSPrev, S)) { + if (isAllocated(RSCurr, RSPrev, S)) { Msg = "Memory is allocated"; - StackHint = new StackHintGeneratorForSymbol(Sym, - "Returned allocated memory"); - } else if (isReleased(RS, RSPrev, S)) { - const auto Family = RS->getAllocationFamily(); + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returned allocated memory"); + } else if (isReleased(RSCurr, RSPrev, S)) { + const auto Family = RSCurr->getAllocationFamily(); switch (Family) { case AF_Alloca: case AF_Malloc: @@ -2966,8 +3227,8 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( case AF_CXXNewArray: case AF_IfNameIndex: Msg = "Memory is released"; - StackHint = new StackHintGeneratorForSymbol(Sym, - "Returning; memory was released"); + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returning; memory was released"); break; case AF_InnerBuffer: { const MemRegion *ObjRegion = @@ -2978,11 +3239,11 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { OS << "deallocated by call to destructor"; - StackHint = new StackHintGeneratorForSymbol(Sym, - "Returning; inner buffer was deallocated"); + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returning; inner buffer was deallocated"); } else { OS << "reallocated by call to '"; - const Stmt *S = RS->getStmt(); + const Stmt *S = RSCurr->getStmt(); if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) { OS << MemCallE->getMethodDecl()->getNameAsString(); } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { @@ -2994,8 +3255,8 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( OS << (D ? D->getNameAsString() : "unknown"); } OS << "'"; - StackHint = new StackHintGeneratorForSymbol(Sym, - "Returning; inner buffer was reallocated"); + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returning; inner buffer was reallocated"); } Msg = OS.str(); break; @@ -3033,14 +3294,14 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( } } } - } else if (isRelinquished(RS, RSPrev, S)) { + } else if (isRelinquished(RSCurr, RSPrev, S)) { Msg = "Memory ownership is transferred"; - StackHint = new StackHintGeneratorForSymbol(Sym, ""); - } else if (isReallocFailedCheck(RS, RSPrev, S)) { + StackHint = std::make_unique<StackHintGeneratorForSymbol>(Sym, ""); + } else if (hasReallocFailed(RSCurr, RSPrev, S)) { Mode = ReallocationFailed; Msg = "Reallocation failed"; - StackHint = new StackHintGeneratorForReallocationFailed(Sym, - "Reallocation failed"); + StackHint = std::make_unique<StackHintGeneratorForReallocationFailed>( + Sym, "Reallocation failed"); if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) { // Is it possible to fail two reallocs WITHOUT testing in between? @@ -3059,21 +3320,24 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( if (!statePrev->get<RegionState>(FailedReallocSymbol)) { // We're at the reallocation point. Msg = "Attempt to reallocate memory"; - StackHint = new StackHintGeneratorForSymbol(Sym, - "Returned reallocated memory"); + StackHint = std::make_unique<StackHintGeneratorForSymbol>( + Sym, "Returned reallocated memory"); FailedReallocSymbol = nullptr; Mode = Normal; } } - if (Msg.empty()) + if (Msg.empty()) { + assert(!StackHint); return nullptr; + } + assert(StackHint); // Generate the extra diagnostic. PathDiagnosticLocation Pos; if (!S) { - assert(RS->getAllocationFamily() == AF_InnerBuffer); + assert(RSCurr->getAllocationFamily() == AF_InnerBuffer); auto PostImplCall = N->getLocation().getAs<PostImplicitCall>(); if (!PostImplCall) return nullptr; @@ -3084,7 +3348,9 @@ std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( N->getLocationContext()); } - return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint); + auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true); + BR.addCallStackHint(P, std::move(StackHint)); + return P; } void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, @@ -3131,13 +3397,13 @@ void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { MallocChecker *checker = mgr.getChecker<MallocChecker>(); checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = - mgr.getCurrentCheckName(); + mgr.getCurrentCheckerName(); } void ento::registerDynamicMemoryModeling(CheckerManager &mgr) { auto *checker = mgr.registerChecker<MallocChecker>(); - checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( - checker, "Optimistic"); + checker->MemFunctionInfo.ShouldIncludeOwnershipAnnotatedFunctions = + mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic"); } bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) { @@ -3148,12 +3414,11 @@ bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) { void ento::register##name(CheckerManager &mgr) { \ MallocChecker *checker = mgr.getChecker<MallocChecker>(); \ checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ - checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ + checker->CheckNames[MallocChecker::CK_##name] = \ + mgr.getCurrentCheckerName(); \ } \ \ - bool ento::shouldRegister##name(const LangOptions &LO) { \ - return true; \ - } + bool ento::shouldRegister##name(const LangOptions &LO) { return true; } REGISTER_CHECKER(MallocChecker) REGISTER_CHECKER(NewDeleteChecker) diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp index 2eb4d7141e284..b5881a9e65338 100644 --- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -183,7 +183,7 @@ public: QualType CastedType = i->CastedExpr->getType(); if (!CastedType->isPointerType()) continue; - QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType(); + QualType PointeeType = CastedType->getPointeeType(); if (PointeeType->isVoidType()) continue; diff --git a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp index 270efede83858..ceea62160545e 100644 --- a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -67,7 +67,7 @@ void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, if (!N) return; - auto Report = llvm::make_unique<BugReport>( + auto Report = std::make_unique<PathSensitiveBugReport>( *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can " "lead to exploitable memory regions, which could be overwritten " "with malicious code", N); diff --git a/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/lib/StaticAnalyzer/Checkers/MoveChecker.cpp index d8a9af78536a0..1473c05d7e3fc 100644 --- a/lib/StaticAnalyzer/Checkers/MoveChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -169,9 +169,9 @@ private: // in the first place. } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: const MoveChecker &Chk; @@ -270,9 +270,10 @@ static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { return MR; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { // We need only the last move of the reported object's region. // The visitor walks the ExplodedGraph backwards. if (Found) @@ -288,7 +289,7 @@ MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, return nullptr; // Retrieve the associated statement. - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; Found = true; @@ -400,7 +401,7 @@ ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, PathDiagnosticLocation LocUsedForUniqueing; const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); - if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode)) + if (const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics()) LocUsedForUniqueing = PathDiagnosticLocation::createBegin( MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); @@ -428,10 +429,10 @@ ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, break; } - auto R = - llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing, - MoveNode->getLocationContext()->getDecl()); - R->addVisitor(llvm::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT, OS.str(), N, LocUsedForUniqueing, + MoveNode->getLocationContext()->getDecl()); + R->addVisitor(std::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); C.emitReport(std::move(R)); return N; } diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index 6fc7c17bc42fb..41b7fe5e43b6f 100644 --- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -67,9 +67,11 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, return; } - auto Report = llvm::make_unique<BugReport>( - *BT, "Use -drain instead of -release when using NSAutoreleasePool and " - "garbage collection", N); + auto Report = std::make_unique<PathSensitiveBugReport>( + *BT, + "Use -drain instead of -release when using NSAutoreleasePool and " + "garbage collection", + N); Report->addRange(msg.getSourceRange()); C.emitReport(std::move(Report)); } diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index 5cec012258c15..85370bf133cd7 100644 --- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -273,7 +273,8 @@ void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { CFBT.reset(new CFErrorDerefBug(this)); bug = CFBT.get(); } - BR.emitReport(llvm::make_unique<BugReport>(*bug, os.str(), event.SinkNode)); + BR.emitReport( + std::make_unique<PathSensitiveBugReport>(*bug, os.str(), event.SinkNode)); } static bool IsNSError(QualType T, IdentifierInfo *II) { diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index bf6b3e3e87cf8..6ffc89745365c 100644 --- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -35,9 +35,11 @@ public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; - std::unique_ptr<BugReport> - genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; - std::unique_ptr<BugReport> + std::unique_ptr<PathSensitiveBugReport> + genReportNullAttrNonNull(const ExplodedNode *ErrorN, + const Expr *ArgE, + unsigned IdxOfArg) const; + std::unique_ptr<PathSensitiveBugReport> genReportReferenceToNullPointer(const ExplodedNode *ErrorN, const Expr *ArgE) const; }; @@ -143,7 +145,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, std::unique_ptr<BugReport> R; if (haveAttrNonNull) - R = genReportNullAttrNonNull(errorNode, ArgE); + R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1); else if (haveRefTypeParam) R = genReportReferenceToNullPointer(errorNode, ArgE); @@ -177,9 +179,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, C.addTransition(state); } -std::unique_ptr<BugReport> +std::unique_ptr<PathSensitiveBugReport> NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, - const Expr *ArgE) const { + const Expr *ArgE, + unsigned IdxOfArg) const { // Lazily allocate the BugType object if it hasn't already been // created. Ownership is transferred to the BugReporter object once // the BugReport is passed to 'EmitWarning'. @@ -187,21 +190,27 @@ NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, BTAttrNonNull.reset(new BugType( this, "Argument with 'nonnull' attribute passed null", "API")); - auto R = llvm::make_unique<BugReport>( - *BTAttrNonNull, - "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); + llvm::SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Null pointer passed to " + << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg) + << " parameter expecting 'nonnull'"; + + auto R = + std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode); if (ArgE) bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); return R; } -std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( +std::unique_ptr<PathSensitiveBugReport> +NonNullParamChecker::genReportReferenceToNullPointer( const ExplodedNode *ErrorNode, const Expr *ArgE) const { if (!BTNullRefArg) BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *BTNullRefArg, "Forming reference to null pointer", ErrorNode); if (ArgE) { const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp index af21c84b995b4..4322ac207112c 100644 --- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -112,11 +112,11 @@ public: DefaultBool CheckNullablePassedToNonnull; DefaultBool CheckNullableReturnedFromNonnull; - CheckName CheckNameNullPassedToNonnull; - CheckName CheckNameNullReturnedFromNonnull; - CheckName CheckNameNullableDereferenced; - CheckName CheckNameNullablePassedToNonnull; - CheckName CheckNameNullableReturnedFromNonnull; + CheckerNameRef CheckNameNullPassedToNonnull; + CheckerNameRef CheckNameNullReturnedFromNonnull; + CheckerNameRef CheckNameNullableDereferenced; + CheckerNameRef CheckNameNullablePassedToNonnull; + CheckerNameRef CheckNameNullableReturnedFromNonnull; }; NullabilityChecksFilter Filter; @@ -137,9 +137,9 @@ private: ID.AddPointer(Region); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: // The tracked region. @@ -163,10 +163,10 @@ private: if (!BT) BT.reset(new BugType(this, "Nullability", categories::MemoryError)); - auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N); if (Region) { R->markInteresting(Region); - R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region)); + R->addVisitor(std::make_unique<NullabilityBugVisitor>(Region)); } if (ValueExpr) { R->addRange(ValueExpr->getSourceRange()); @@ -290,10 +290,9 @@ NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { return dyn_cast<SymbolicRegion>(Region); } -std::shared_ptr<PathDiagnosticPiece> -NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) { +PathDiagnosticPieceRef NullabilityChecker::NullabilityBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = N->getFirstPred()->getState(); @@ -310,7 +309,7 @@ NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N, // Retrieve the associated statement. const Stmt *S = TrackedNullab->getNullabilitySource(); if (!S || S->getBeginLoc().isInvalid()) { - S = PathDiagnosticLocation::getStmt(N); + S = N->getStmtForDiagnostics(); } if (!S) @@ -324,8 +323,7 @@ NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N, // Generate the extra diagnostic. PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true, - nullptr); + return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); } /// Returns true when the value stored at the given location has been @@ -1203,12 +1201,12 @@ bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) { void ento::register##name##Checker(CheckerManager &mgr) { \ NullabilityChecker *checker = mgr.getChecker<NullabilityChecker>(); \ checker->Filter.Check##name = true; \ - checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + checker->Filter.CheckName##name = mgr.getCurrentCheckerName(); \ checker->NeedTracking = checker->NeedTracking || trackingRequired; \ checker->NoDiagnoseCallsToSystemHeaders = \ checker->NoDiagnoseCallsToSystemHeaders || \ mgr.getAnalyzerOptions().getCheckerBooleanOption( \ - checker, "NoDiagnoseCallsToSystemHeaders", true); \ + checker, "NoDiagnoseCallsToSystemHeaders", true); \ } \ \ bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \ diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index bd8cfb14680fe..0e25817c87932 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -46,8 +46,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, if (!BT_undef) BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " "for @synchronized")); - auto report = - llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); + auto report = std::make_unique<PathSensitiveBugReport>( + *BT_undef, BT_undef->getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *report); C.emitReport(std::move(report)); } @@ -70,8 +70,8 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, BT_null.reset(new BuiltinBug( this, "Nil value used as mutex for @synchronized() " "(no synchronization will occur)")); - auto report = - llvm::make_unique<BugReport>(*BT_null, BT_null->getDescription(), N); + auto report = std::make_unique<PathSensitiveBugReport>( + *BT_null, BT_null->getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *report); C.emitReport(std::move(report)); diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp index f69a3944a56ca..8abb926d4862f 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -144,10 +144,11 @@ void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, if (!N) return; initBugType(); - auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT, "Index is out of bounds", N); R->addRange(IdxExpr->getSourceRange()); - bugreporter::trackExpressionValue(N, IdxExpr, *R, - /*EnableNullFPSuppression=*/false); + bugreporter::trackExpressionValue( + N, IdxExpr, *R, bugreporter::TrackingKind::Thorough, false); C.emitReport(std::move(R)); return; } diff --git a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp index 33e4d2af000dd..1870c08432de3 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -13,12 +13,12 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "llvm/ADT/SmallSet.h" diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp index 767b7bf4063c8..344285750f0e8 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -159,7 +159,7 @@ void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C, if (!BT) BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"", categories::CoreFoundationObjectiveC)); - C.emitReport(llvm::make_unique<BugReport>(*BT, errorStr, N)); + C.emitReport(std::make_unique<PathSensitiveBugReport>(*BT, errorStr, N)); } void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp index f435f00c08e79..0575be8453743 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -67,12 +67,11 @@ class SuperDeallocBRVisitor final : public BugReporterVisitor { public: SuperDeallocBRVisitor(SymbolRef ReceiverSymbol) - : ReceiverSymbol(ReceiverSymbol), - Satisfied(false) {} + : ReceiverSymbol(ReceiverSymbol), Satisfied(false) {} - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(ReceiverSymbol); @@ -188,10 +187,10 @@ void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, Desc = "Use of 'self' after it has been deallocated"; // Generate the report. - std::unique_ptr<BugReport> BR( - new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode)); + auto BR = std::make_unique<PathSensitiveBugReport>(*DoubleSuperDeallocBugType, + Desc, ErrNode); BR->addRange(S->getSourceRange()); - BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym)); + BR->addVisitor(std::make_unique<SuperDeallocBRVisitor>(Sym)); C.emitReport(std::move(BR)); } @@ -243,9 +242,10 @@ ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { return M.getSelector() == SELdealloc; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &) { + BugReporterContext &BRC, + PathSensitiveBugReport &) { if (Satisfied) return nullptr; diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index 4b39a97c7e8d1..cb47704515723 100644 --- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" @@ -20,7 +21,6 @@ #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" using namespace clang; @@ -94,7 +94,7 @@ static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) { } static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, - SourceManager &SM) { + const SourceManager &SM) { for (const auto *I : C->decls()) if (const auto *FD = dyn_cast<FunctionDecl>(I)) { SourceLocation L = FD->getBeginLoc(); @@ -148,7 +148,7 @@ static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, // FIXME: In the future hopefully we can just use the lexical DeclContext // to go from the ObjCImplementationDecl to the lexically "nested" // C functions. - SourceManager &SM = BR.getSourceManager(); + const SourceManager &SM = BR.getSourceManager(); Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); // Find ivars that are unused. diff --git a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp index 0aa410de15ff2..4a3c2b8cd40e2 100644 --- a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -306,7 +306,7 @@ public: const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const { if (!PaddingBug) PaddingBug = - llvm::make_unique<BugType>(this, "Excessive Padding", "Performance"); + std::make_unique<BugType>(this, "Excessive Padding", "Performance"); SmallString<100> Buf; llvm::raw_svector_ostream Os(Buf); @@ -335,7 +335,8 @@ public: PathDiagnosticLocation CELoc = PathDiagnosticLocation::create(RD, BR->getSourceManager()); - auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc); + auto Report = + std::make_unique<BasicBugReport>(*PaddingBug, Os.str(), CELoc); Report->setDeclWithIssue(RD); Report->addRange(RD->getSourceRange()); BR->emitReport(std::move(Report)); diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 03c3f4dd23570..259f23abdc958 100644 --- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -119,12 +119,12 @@ const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region, AllocKind &AKind, CheckerContext &C) const { assert(Region); - while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) { - Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion(); + while (const auto *BaseRegion = dyn_cast<CXXBaseObjectRegion>(Region)) { + Region = BaseRegion->getSuperRegion(); Polymorphic = true; } - if (Region->getKind() == MemRegion::Kind::ElementRegionKind) { - Region = Region->getAs<ElementRegion>()->getSuperRegion(); + if (const auto *ElemRegion = dyn_cast<ElementRegion>(Region)) { + Region = ElemRegion->getSuperRegion(); } ProgramStateRef State = C.getState(); @@ -137,7 +137,7 @@ const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region, } // When the region is symbolic and we do not have any information about it, // assume that this is an array to avoid false positives. - if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind) + if (isa<SymbolicRegion>(Region)) return Region; // No AllocKind stored and not symbolic, assume that it points to a single @@ -173,8 +173,8 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E, this, "Dangerous pointer arithmetic", "Pointer arithmetic on a pointer to base class is dangerous " "because derived and base class may have different size.")); - auto R = llvm::make_unique<BugReport>(*BT_polyArray, - BT_polyArray->getDescription(), N); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT_polyArray, BT_polyArray->getDescription(), N); R->addRange(E->getSourceRange()); R->markInteresting(ArrayRegion); C.emitReport(std::move(R)); @@ -196,8 +196,8 @@ void PointerArithChecker::reportPointerArithMisuse(const Expr *E, "Pointer arithmetic on non-array " "variables relies on memory layout, " "which is dangerous.")); - auto R = llvm::make_unique<BugReport>(*BT_pointerArith, - BT_pointerArith->getDescription(), N); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT_pointerArith, BT_pointerArith->getDescription(), N); R->addRange(SR); R->markInteresting(Region); C.emitReport(std::move(R)); diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index c9512f4fc42ff..88d0eb2ae7484 100644 --- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -63,7 +63,8 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B, new BuiltinBug(this, "Pointer subtraction", "Subtraction of two pointers that do not point to " "the same memory chunk may cause incorrect result.")); - auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + auto R = + std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); R->addRange(B->getSourceRange()); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 33f677e1c2583..8649b8b96dd03 100644 --- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -240,7 +240,7 @@ void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto report = llvm::make_unique<BugReport>( + auto report = std::make_unique<PathSensitiveBugReport>( *BT_doublelock, "This lock has already been acquired", N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(report)); @@ -305,7 +305,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto Report = llvm::make_unique<BugReport>( + auto Report = std::make_unique<PathSensitiveBugReport>( *BT_doubleunlock, "This lock has already been unlocked", N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); @@ -328,7 +328,7 @@ void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto report = llvm::make_unique<BugReport>( + auto report = std::make_unique<PathSensitiveBugReport>( *BT_lor, "This was not the most recently acquired lock. Possible " "lock order reversal", N); report->addRange(CE->getArg(0)->getSourceRange()); @@ -399,7 +399,8 @@ void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_destroylock, Message, N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); } @@ -438,7 +439,8 @@ void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_initlock, Message, N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); } @@ -451,7 +453,7 @@ void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, ExplodedNode *N = C.generateErrorNode(); if (!N) return; - auto Report = llvm::make_unique<BugReport>( + auto Report = std::make_unique<PathSensitiveBugReport>( *BT_destroylock, "This lock has already been destroyed", N); Report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(Report)); diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp index 4a3a8dae23a7f..6f8cb1432bb11 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -875,7 +875,7 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St, if (!N) return; - auto report = llvm::make_unique<RefCountReport>( + auto report = std::make_unique<RefCountReport>( errorKindToBugKind(ErrorKind, Sym), C.getASTContext().getLangOpts(), N, Sym); report->addRange(ErrorRange); @@ -1095,7 +1095,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, if (N) { const LangOptions &LOpts = C.getASTContext().getLangOpts(); auto R = - llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C); + std::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C); C.emitReport(std::move(R)); } return N; @@ -1119,7 +1119,7 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); if (N) { - auto R = llvm::make_unique<RefCountReport>( + auto R = std::make_unique<RefCountReport>( returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); C.emitReport(std::move(R)); } @@ -1273,7 +1273,7 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, os << "has a +" << V.getCount() << " retain count"; const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); - auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym, + auto R = std::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym, os.str()); Ctx.emitReport(std::move(R)); } @@ -1321,7 +1321,7 @@ RetainCountChecker::processLeaks(ProgramStateRef state, if (N) { for (SymbolRef L : Leaked) { const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn; - Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx)); + Ctx.emitReport(std::make_unique<RefLeakReport>(BT, LOpts, N, L, Ctx)); } } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h index 124c0e5040b99..dd79bbef321c3 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -21,12 +21,12 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ParentMap.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/RetainSummaryManager.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Analysis/SelectorExtras.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp index 796fd882ffd5e..9853758f7f2c0 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -325,22 +325,22 @@ public: ID.AddPointer(Sym); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; - std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override; + PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + PathSensitiveBugReport &BR) override; }; class RefLeakReportVisitor : public RefCountReportVisitor { public: RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {} - std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, - const ExplodedNode *N, - BugReport &BR) override; + PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + PathSensitiveBugReport &BR) override; }; } // end namespace retaincountchecker @@ -448,9 +448,9 @@ annotateStartParameter(const ExplodedNode *N, SymbolRef Sym, return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } -std::shared_ptr<PathDiagnosticPiece> -RefCountReportVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { const auto &BT = static_cast<const RefCountBug&>(BR.getBugType()); const auto *Checker = @@ -709,21 +709,22 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, LeakContext) FirstBinding = nullptr; - return AllocationInfo(AllocationNodeInCurrentOrParentContext, - FirstBinding, + return AllocationInfo(AllocationNodeInCurrentOrParentContext, FirstBinding, InterestingMethodContext); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef RefCountReportVisitor::getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndN, BugReport &BR) { + const ExplodedNode *EndN, + PathSensitiveBugReport &BR) { BR.markInteresting(Sym); return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, - const ExplodedNode *EndN, BugReport &BR) { + const ExplodedNode *EndN, + PathSensitiveBugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. @@ -737,13 +738,7 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, const MemRegion* FirstBinding = AllocI.R; BR.markInteresting(AllocI.InterestingMethodContext); - SourceManager& SM = BRC.getSourceManager(); - - // Compute an actual location for the leak. Sometimes a leak doesn't - // occur at an actual statement (e.g., transition between blocks; end - // of function) so we need to walk the graph and compute a real location. - const ExplodedNode *LeakN = EndN; - PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); + PathDiagnosticLocation L = cast<RefLeakReport>(BR).getEndOfPath(); std::string sbuf; llvm::raw_string_ostream os(sbuf); @@ -814,19 +809,19 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, } RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, - ExplodedNode *n, SymbolRef sym, - bool isLeak) - : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) { + ExplodedNode *n, SymbolRef sym, bool isLeak) + : PathSensitiveBugReport(D, D.getDescription(), n), Sym(sym), + isLeak(isLeak) { if (!isLeak) - addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); + addVisitor(std::make_unique<RefCountReportVisitor>(sym)); } RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, StringRef endText) - : BugReport(D, D.getDescription(), endText, n) { + : PathSensitiveBugReport(D, D.getDescription(), endText, n) { - addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); + addVisitor(std::make_unique<RefCountReportVisitor>(sym)); } void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { @@ -873,7 +868,7 @@ void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx, // FIXME: This will crash the analyzer if an allocation comes from an // implicit call (ex: a destructor call). // (Currently there are no such allocations in Cocoa, though.) - AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); + AllocStmt = AllocNode->getStmtForDiagnostics(); if (!AllocStmt) { AllocBinding = nullptr; @@ -918,5 +913,5 @@ RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, createDescription(Ctx); - addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym)); + addVisitor(std::make_unique<RefLeakReportVisitor>(sym)); } diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h index ef3c75f87af5d..e9e2777540548 100644 --- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h +++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -14,10 +14,10 @@ #ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H #define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/RetainSummaryManager.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" namespace clang { @@ -53,7 +53,7 @@ private: static StringRef bugTypeToName(RefCountBugType BT); }; -class RefCountReport : public BugReport { +class RefCountReport : public PathSensitiveBugReport { protected: SymbolRef Sym; bool isLeak = false; @@ -67,16 +67,17 @@ public: ExplodedNode *n, SymbolRef sym, StringRef endText); - llvm::iterator_range<ranges_iterator> getRanges() override { + ArrayRef<SourceRange> getRanges() const override { if (!isLeak) - return BugReport::getRanges(); - return llvm::make_range(ranges_iterator(), ranges_iterator()); + return PathSensitiveBugReport::getRanges(); + return {}; } }; class RefLeakReport : public RefCountReport { const MemRegion* AllocBinding; const Stmt *AllocStmt; + PathDiagnosticLocation Location; // Finds the function declaration where a leak warning for the parameter // 'sym' should be raised. @@ -89,11 +90,14 @@ class RefLeakReport : public RefCountReport { public: RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, SymbolRef sym, CheckerContext &Ctx); - - PathDiagnosticLocation getLocation(const SourceManager &SM) const override { + PathDiagnosticLocation getLocation() const override { assert(Location.isValid()); return Location; } + + PathDiagnosticLocation getEndOfPath() const { + return PathSensitiveBugReport::getLocation(); + } }; } // end namespace retaincountchecker diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 9eb47e0526dc5..abd1a074b4871 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -79,7 +79,8 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, // reference is outside the range. // Generate a report for this bug. - auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); report->addRange(RetE->getSourceRange()); C.emitReport(std::move(report)); diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index f55c369da67eb..fbd15d864424e 100644 --- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -83,7 +83,8 @@ static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, if (!N) return; - auto Report = llvm::make_unique<BugReport>(BT, BT.getDescription(), N); + auto Report = + std::make_unique<PathSensitiveBugReport>(BT, BT.getDescription(), N); Report->addRange(RetE->getSourceRange()); bugreporter::trackExpressionValue(N, TrackingE ? TrackingE : RetE, *Report); diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp index ec5e9622c236f..8193bcbef4cdf 100644 --- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -206,8 +206,8 @@ void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, return; // Generate the report. - auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType, - "Closing a previously closed file stream", ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>( + *DoubleCloseBugType, "Closing a previously closed file stream", ErrNode); R->addRange(Call.getSourceRange()); R->markInteresting(FileDescSym); C.emitReport(std::move(R)); @@ -219,8 +219,9 @@ void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, // Attach bug reports to the leak node. // TODO: Identify the leaked file descriptor. for (SymbolRef LeakedStream : LeakedStreams) { - auto R = llvm::make_unique<BugReport>(*LeakBugType, - "Opened file is never closed; potential resource leak", ErrNode); + auto R = std::make_unique<PathSensitiveBugReport>( + *LeakBugType, "Opened file is never closed; potential resource leak", + ErrNode); R->markInteresting(LeakedStream); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index b93bed5c30973..7285d27495a7c 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -155,14 +155,15 @@ void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, if (!N) return; if (!BT_returnstack) - BT_returnstack = llvm::make_unique<BuiltinBug>( + BT_returnstack = std::make_unique<BuiltinBug>( this, "Return of address to stack-allocated memory"); // Generate a report for this bug. SmallString<128> buf; llvm::raw_svector_ostream os(buf); SourceRange range = genName(os, R, C.getASTContext()); os << " returned to caller"; - auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT_returnstack, os.str(), N); report->addRange(RetE->getSourceRange()); if (range.isValid()) report->addRange(range); @@ -193,14 +194,14 @@ void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( if (!N) continue; if (!BT_capturedstackasync) - BT_capturedstackasync = llvm::make_unique<BuiltinBug>( + BT_capturedstackasync = std::make_unique<BuiltinBug>( this, "Address of stack-allocated memory is captured"); SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); SourceRange Range = genName(Out, Region, C.getASTContext()); Out << " is captured by an asynchronously-executed block"; - auto Report = - llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N); + auto Report = std::make_unique<PathSensitiveBugReport>( + *BT_capturedstackasync, Out.str(), N); if (Range.isValid()) Report->addRange(Range); C.emitReport(std::move(Report)); @@ -216,14 +217,14 @@ void StackAddrEscapeChecker::checkReturnedBlockCaptures( if (!N) continue; if (!BT_capturedstackret) - BT_capturedstackret = llvm::make_unique<BuiltinBug>( + BT_capturedstackret = std::make_unique<BuiltinBug>( this, "Address of stack-allocated memory is captured"); SmallString<128> Buf; llvm::raw_svector_ostream Out(Buf); SourceRange Range = genName(Out, Region, C.getASTContext()); Out << " is captured by a returned block"; - auto Report = - llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT_capturedstackret, + Out.str(), N); if (Range.isValid()) Report->addRange(Range); C.emitReport(std::move(Report)); @@ -331,7 +332,7 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, return; if (!BT_stackleak) - BT_stackleak = llvm::make_unique<BuiltinBug>( + BT_stackleak = std::make_unique<BuiltinBug>( this, "Stack address stored into global variable", "Stack address was saved into a global variable. " "This is dangerous because the address will become " @@ -351,7 +352,8 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion()); Out << *VR->getDecl() << "' upon returning to the caller. This will be a dangling reference"; - auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N); + auto Report = + std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N); if (Range.isValid()) Report->addRange(Range); diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp index 1ea5e07695133..c254408351c81 100644 --- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -277,7 +277,7 @@ void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { new BuiltinBug(this, "Illegal whence argument", "The whence argument to fseek() should be " "SEEK_SET, SEEK_END, or SEEK_CUR.")); - C.emitReport(llvm::make_unique<BugReport>( + C.emitReport(std::make_unique<PathSensitiveBugReport>( *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); } } @@ -345,7 +345,7 @@ ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, if (!BT_nullfp) BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", "Stream pointer might be NULL.")); - C.emitReport(llvm::make_unique<BugReport>( + C.emitReport(std::make_unique<PathSensitiveBugReport>( *BT_nullfp, BT_nullfp->getDescription(), N)); } return nullptr; @@ -375,7 +375,7 @@ ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, BT_doubleclose.reset(new BuiltinBug( this, "Double fclose", "Try to close a file Descriptor already" " closed. Cause undefined behaviour.")); - C.emitReport(llvm::make_unique<BugReport>( + C.emitReport(std::make_unique<PathSensitiveBugReport>( *BT_doubleclose, BT_doubleclose->getDescription(), N)); } return nullptr; @@ -405,7 +405,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, BT_ResourceLeak.reset( new BuiltinBug(this, "Resource Leak", "Opened File never closed. Potential Resource leak.")); - C.emitReport(llvm::make_unique<BugReport>( + C.emitReport(std::make_unique<PathSensitiveBugReport>( *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); } } diff --git a/lib/StaticAnalyzer/Checkers/Taint.cpp b/lib/StaticAnalyzer/Checkers/Taint.cpp index bc120949ee4f1..574d4ed1e600c 100644 --- a/lib/StaticAnalyzer/Checkers/Taint.cpp +++ b/lib/StaticAnalyzer/Checkers/Taint.cpp @@ -204,16 +204,16 @@ bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) { return false; } -std::shared_ptr<PathDiagnosticPiece> -TaintBugVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - BugReport &BR) { +PathDiagnosticPieceRef TaintBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { // Find the ExplodedNode where the taint was first introduced if (!isTainted(N->getState(), V) || isTainted(N->getFirstPred()->getState(), V)) return nullptr; - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; diff --git a/lib/StaticAnalyzer/Checkers/Taint.h b/lib/StaticAnalyzer/Checkers/Taint.h index 72cf6a79d52c4..8940916c19331 100644 --- a/lib/StaticAnalyzer/Checkers/Taint.h +++ b/lib/StaticAnalyzer/Checkers/Taint.h @@ -89,9 +89,9 @@ public: TaintBugVisitor(const SVal V) : V(V) {} void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; } // namespace taint diff --git a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp index 094762e2faf83..f81705304f3ab 100644 --- a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -52,7 +52,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E, if (isTainted(State, E, C.getLocationContext())) { if (ExplodedNode *N = C.generateNonFatalErrorNode()) { initBugType(); - auto report = llvm::make_unique<BugReport>(*BT, "tainted",N); + auto report = std::make_unique<PathSensitiveBugReport>(*BT, "tainted", N); report->addRange(E->getSourceRange()); C.emitReport(std::move(report)); } diff --git a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp index 7a33845a6a266..3663b09636924 100644 --- a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -69,9 +69,9 @@ public: ID.Add(SFC); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; class TestAfterDivZeroChecker @@ -92,9 +92,9 @@ public: REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState) -std::shared_ptr<PathDiagnosticPiece> -DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { if (Satisfied) return nullptr; @@ -167,12 +167,12 @@ void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { if (!DivZeroBug) DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *DivZeroBug, "Value being compared against zero has already been used " "for division", N); - R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), + R->addVisitor(std::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), C.getStackFrame())); C.emitReport(std::move(R)); } diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 3a4a1dbf641bb..247cba7dc9333 100644 --- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -96,7 +96,8 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, Ex = FindIt.FindExpr(Ex); // Emit the bug report. - auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + auto R = std::make_unique<PathSensitiveBugReport>( + *BT, BT->getDescription(), N); bugreporter::trackExpressionValue(N, Ex, *R); R->addRange(Ex->getSourceRange()); diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index c787ef58660ab..7b581bef39001 100644 --- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -83,11 +83,12 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, os << "Variable '" << VD->getName() << "' is uninitialized when captured by block"; - auto R = llvm::make_unique<BugReport>(*BT, os.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) R->addRange(Ex->getSourceRange()); - R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *V, VR, /*EnableNullFPSuppression*/ false)); + R->addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *V, VR, /*EnableNullFPSuppression*/ false, + bugreporter::TrackingKind::Thorough)); R->disablePathPruning(); // need location of block C.emitReport(std::move(R)); diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index 1ae287d39f11a..a2f3e0da13fb0 100644 --- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -170,7 +170,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B, << "' expression is undefined"; } } - auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N); + auto report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); if (Ex) { report->addRange(Ex->getSourceRange()); bugreporter::trackExpressionValue(N, Ex, *report); diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index 4c517d6f05622..2f075eaeb03bf 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -52,7 +52,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, BT.reset(new BuiltinBug(this, "Array subscript is undefined")); // Generate a report for this bug. - auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N); R->addRange(A->getIdx()->getSourceRange()); bugreporter::trackExpressionValue(N, A->getIdx(), *R); C.emitReport(std::move(R)); diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index d32d2a4042de7..277a8a1433282 100644 --- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -85,7 +85,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, } if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { - const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl()); ex = VD->getInit(); } @@ -108,7 +108,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, if (OS.str().empty()) OS << DefaultMsg; - auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); if (ex) { R->addRange(ex->getSourceRange()); bugreporter::trackExpressionValue(N, ex, *R); diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp index 9d608c12d19be..020df8a1bb8c7 100644 --- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -24,7 +24,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" using namespace clang; using namespace clang::ento; @@ -187,7 +187,7 @@ void UninitializedObjectChecker::checkEndFunction( if (Opts.ShouldConvertNotesToWarnings) { for (const auto &Pair : UninitFields) { - auto Report = llvm::make_unique<BugReport>( + auto Report = std::make_unique<PathSensitiveBugReport>( *BT_uninitField, Pair.second, Node, LocUsedForUniqueing, Node->getLocationContext()->getDecl()); Context.emitReport(std::move(Report)); @@ -201,7 +201,7 @@ void UninitializedObjectChecker::checkEndFunction( << (UninitFields.size() == 1 ? "" : "s") << " at the end of the constructor call"; - auto Report = llvm::make_unique<BugReport>( + auto Report = std::make_unique<PathSensitiveBugReport>( *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, Node->getLocationContext()->getDecl()); diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp index a5dc250104f32..f0dd0bf813aff 100644 --- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp +++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -18,7 +18,7 @@ #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" using namespace clang; using namespace clang::ento; @@ -260,12 +260,13 @@ static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, break; } - while (R->getAs<CXXBaseObjectRegion>()) { + while (isa<CXXBaseObjectRegion>(R)) { NeedsCastBack = true; - - if (!isa<TypedValueRegion>(R->getSuperRegion())) + const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion()); + if (!SuperR) break; - R = R->getSuperRegion()->getAs<TypedValueRegion>(); + + R = SuperR; } return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false}; diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 2ccb519891f37..f4e225d836f38 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -135,7 +135,7 @@ void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, LazyInitialize(this, BT_open, "Improper use of 'open'"); - auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N); Report->addRange(SR); C.emitReport(std::move(Report)); } @@ -304,7 +304,8 @@ void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); - auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.emitReport(std::move(report)); } @@ -347,7 +348,8 @@ bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; - auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N); + auto report = + std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N); report->addRange(arg->getSourceRange()); bugreporter::trackExpressionValue(N, arg, *report); diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 0b0bf8465c9dd..65dd82675df9b 100644 --- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -55,7 +55,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, const Decl *D = nullptr; CFG *C = nullptr; - ParentMap *PM = nullptr; + const ParentMap *PM = nullptr; const LocationContext *LC = nullptr; // Iterate over ExplodedGraph for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 1630896c3b607..b92757312dc65 100644 --- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -72,7 +72,7 @@ void VLASizeChecker::reportBug( break; } - auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); + auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); report->addVisitor(std::move(Visitor)); report->addRange(SizeE->getSourceRange()); bugreporter::trackExpressionValue(N, SizeE, *report); @@ -110,7 +110,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { // Check if the size is tainted. if (isTainted(state, sizeV)) { reportBug(VLA_Tainted, SE, nullptr, C, - llvm::make_unique<TaintBugVisitor>(sizeV)); + std::make_unique<TaintBugVisitor>(sizeV)); return; } diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 13ad3d98e8ebb..a3610514a924b 100644 --- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -46,7 +46,7 @@ public: }; DefaultBool ChecksEnabled[CK_NumCheckKinds]; - CheckName CheckNames[CK_NumCheckKinds]; + CheckerNameRef CheckNames[CK_NumCheckKinds]; void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const; void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -77,20 +77,20 @@ private: ID.AddPointer(&X); ID.AddPointer(Reg); } - std::shared_ptr<PathDiagnosticPiece> - getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, - BugReport &BR) override { + PathDiagnosticPieceRef getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + PathSensitiveBugReport &BR) override { if (!IsLeak) return nullptr; - PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath( - EndPathNode, BRC.getSourceManager()); + PathDiagnosticLocation L = BR.getLocation(); // Do not add the statement itself as a range in case of leak. - return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), false); + return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), + false); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; private: const MemRegion *Reg; @@ -115,7 +115,9 @@ const SmallVector<ValistChecker::VAListAccepter, 15> // vswprintf is the wide version of vsnprintf, // vsprintf has no wide version {{"vswscanf", 3}, 2}}; -const CallDescription ValistChecker::VaStart("__builtin_va_start", 2), + +const CallDescription + ValistChecker::VaStart("__builtin_va_start", /*Args=*/2, /*Params=*/1), ValistChecker::VaCopy("__builtin_va_copy", 2), ValistChecker::VaEnd("__builtin_va_end", 1); } // end anonymous namespace @@ -253,9 +255,9 @@ void ValistChecker::reportUninitializedAccess(const MemRegion *VAList, BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized], "Uninitialized va_list", categories::MemoryError)); - auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N); + auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N); R->markInteresting(VAList); - R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList)); + R->addVisitor(std::make_unique<ValistBugVisitor>(VAList)); C.emitReport(std::move(R)); } } @@ -282,7 +284,7 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, const ExplodedNode *StartNode = getStartCallSite(N, Reg); PathDiagnosticLocation LocUsedForUniqueing; - if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode)) + if (const Stmt *StartCallStmt = StartNode->getStmtForDiagnostics()) LocUsedForUniqueing = PathDiagnosticLocation::createBegin( StartCallStmt, C.getSourceManager(), StartNode->getLocationContext()); @@ -294,11 +296,11 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, OS << " " << VariableName; OS << Msg2; - auto R = llvm::make_unique<BugReport>( + auto R = std::make_unique<PathSensitiveBugReport>( *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing, StartNode->getLocationContext()->getDecl()); R->markInteresting(Reg); - R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true)); + R->addVisitor(std::make_unique<ValistBugVisitor>(Reg, true)); C.emitReport(std::move(R)); } } @@ -373,13 +375,12 @@ void ValistChecker::checkVAListEndCall(const CallEvent &Call, C.addTransition(State); } -std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode( - const ExplodedNode *N, BugReporterContext &BRC, - BugReport &) { +PathDiagnosticPieceRef ValistChecker::ValistBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = N->getFirstPred()->getState(); - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); if (!S) return nullptr; @@ -411,7 +412,8 @@ bool ento::shouldRegisterValistBase(const LangOptions &LO) { void ento::register##name##Checker(CheckerManager &mgr) { \ ValistChecker *checker = mgr.getChecker<ValistChecker>(); \ checker->ChecksEnabled[ValistChecker::CK_##name] = true; \ - checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \ + checker->CheckNames[ValistChecker::CK_##name] = \ + mgr.getCurrentCheckerName(); \ } \ \ bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \ diff --git a/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/lib/StaticAnalyzer/Checkers/VforkChecker.cpp index 40d14aa5c7d44..6724eead50720 100644 --- a/lib/StaticAnalyzer/Checkers/VforkChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -132,7 +132,7 @@ void VforkChecker::reportBug(const char *What, CheckerContext &C, if (Details) os << "; " << Details; - auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N); // TODO: mark vfork call in BugReportVisitor C.emitReport(std::move(Report)); } diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp index 762c9c1c8d7aa..12cee5f8d4f74 100644 --- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -6,7 +6,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines a checker that checks virtual function calls during +// This file defines a checker that checks virtual method calls during // construction or destruction of C++ objects. // //===----------------------------------------------------------------------===// @@ -40,11 +40,10 @@ template <> struct FoldingSetTrait<ObjectState> { namespace { class VirtualCallChecker : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> { - mutable std::unique_ptr<BugType> BT; - public: - // The flag to determine if pure virtual functions should be issued only. - DefaultBool IsPureOnly; + // These are going to be null if the respective check is disabled. + mutable std::unique_ptr<BugType> BT_Pure, BT_Impure; + bool ShowFixIts = false; void checkBeginFunction(CheckerContext &C) const; void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; @@ -53,87 +52,13 @@ public: private: void registerCtorDtorCallInState(bool IsBeginFunction, CheckerContext &C) const; - void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg, - CheckerContext &C) const; - - class VirtualBugVisitor : public BugReporterVisitor { - private: - const MemRegion *ObjectRegion; - bool Found; - - public: - VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {} - - void Profile(llvm::FoldingSetNodeID &ID) const override { - static int X = 0; - ID.AddPointer(&X); - ID.AddPointer(ObjectRegion); - } - - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; - }; }; } // end namespace // GDM (generic data map) to the memregion of this for the ctor and dtor. REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState) -std::shared_ptr<PathDiagnosticPiece> -VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &) { - // We need the last ctor/dtor which call the virtual function. - // The visitor walks the ExplodedGraph backwards. - if (Found) - return nullptr; - - ProgramStateRef State = N->getState(); - const LocationContext *LCtx = N->getLocationContext(); - const CXXConstructorDecl *CD = - dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl()); - const CXXDestructorDecl *DD = - dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl()); - - if (!CD && !DD) - return nullptr; - - ProgramStateManager &PSM = State->getStateManager(); - auto &SVB = PSM.getSValBuilder(); - const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); - if (!MD) - return nullptr; - auto ThiSVal = - State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); - const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion(); - if (!Reg) - return nullptr; - if (Reg != ObjectRegion) - return nullptr; - - const Stmt *S = PathDiagnosticLocation::getStmt(N); - if (!S) - return nullptr; - Found = true; - - std::string InfoText; - if (CD) - InfoText = "This constructor of an object of type '" + - CD->getNameAsString() + - "' has not returned when the virtual method was called"; - else - InfoText = "This destructor of an object of type '" + - DD->getNameAsString() + - "' has not returned when the virtual method was called"; - - // Generate the extra diagnostic. - PathDiagnosticLocation Pos(S, BRC.getSourceManager(), - N->getLocationContext()); - return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); -} - -// The function to check if a callexpr is a virtual function. +// The function to check if a callexpr is a virtual method call. static bool isVirtualCall(const CallExpr *CE) { bool CallIsNonVirtual = false; @@ -178,11 +103,10 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call, const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); if (!MD) return; - ProgramStateRef State = C.getState(); - const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); - if (IsPureOnly && !MD->isPure()) - return; + ProgramStateRef State = C.getState(); + // Member calls are always represented by a call-expression. + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); if (!isVirtualCall(CE)) return; @@ -190,29 +114,51 @@ void VirtualCallChecker::checkPreCall(const CallEvent &Call, const ObjectState *ObState = State->get<CtorDtorMap>(Reg); if (!ObState) return; - // Check if a virtual method is called. - // The GDM of constructor and destructor should be true. - if (*ObState == ObjectState::CtorCalled) { - if (IsPureOnly && MD->isPure()) - reportBug("Call to pure virtual function during construction", true, Reg, - C); - else if (!MD->isPure()) - reportBug("Call to virtual function during construction", false, Reg, C); - else - reportBug("Call to pure virtual function during construction", false, Reg, - C); + + bool IsPure = MD->isPure(); + + // At this point we're sure that we're calling a virtual method + // during construction or destruction, so we'll emit a report. + SmallString<128> Msg; + llvm::raw_svector_ostream OS(Msg); + OS << "Call to "; + if (IsPure) + OS << "pure "; + OS << "virtual method '" << MD->getParent()->getNameAsString() + << "::" << MD->getNameAsString() << "' during "; + if (*ObState == ObjectState::CtorCalled) + OS << "construction "; + else + OS << "destruction "; + if (IsPure) + OS << "has undefined behavior"; + else + OS << "bypasses virtual dispatch"; + + ExplodedNode *N = + IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode(); + if (!N) + return; + + const std::unique_ptr<BugType> &BT = IsPure ? BT_Pure : BT_Impure; + if (!BT) { + // The respective check is disabled. + return; } - if (*ObState == ObjectState::DtorCalled) { - if (IsPureOnly && MD->isPure()) - reportBug("Call to pure virtual function during destruction", true, Reg, - C); - else if (!MD->isPure()) - reportBug("Call to virtual function during destruction", false, Reg, C); - else - reportBug("Call to pure virtual function during construction", false, Reg, - C); + auto Report = std::make_unique<PathSensitiveBugReport>(*BT, OS.str(), N); + + if (ShowFixIts && !IsPure) { + // FIXME: These hints are valid only when the virtual call is made + // directly from the constructor/destructor. Otherwise the dispatch + // will work just fine from other callees, and the fix may break + // the otherwise correct program. + FixItHint Fixit = FixItHint::CreateInsertion( + CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::"); + Report->addFixItHint(Fixit); } + + C.emitReport(std::move(Report)); } void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, @@ -254,34 +200,37 @@ void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, } } -void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink, - const MemRegion *Reg, - CheckerContext &C) const { - ExplodedNode *N; - if (IsSink) - N = C.generateErrorNode(); - else - N = C.generateNonFatalErrorNode(); +void ento::registerVirtualCallModeling(CheckerManager &Mgr) { + Mgr.registerChecker<VirtualCallChecker>(); +} - if (!N) - return; - if (!BT) - BT.reset(new BugType( - this, "Call to virtual function during construction or destruction", - "C++ Object Lifecycle")); - - auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N); - Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg)); - C.emitReport(std::move(Reporter)); +void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.getChecker<VirtualCallChecker>(); + Chk->BT_Pure = std::make_unique<BugType>(Mgr.getCurrentCheckerName(), + "Pure virtual method call", + categories::CXXObjectLifecycle); } -void ento::registerVirtualCallChecker(CheckerManager &mgr) { - VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>(); +void ento::registerVirtualCallChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.getChecker<VirtualCallChecker>(); + if (!Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "PureOnly")) { + Chk->BT_Impure = std::make_unique<BugType>( + Mgr.getCurrentCheckerName(), "Unexpected loss of virtual dispatch", + categories::CXXObjectLifecycle); + Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Mgr.getCurrentCheckerName(), "ShowFixIts"); + } +} + +bool ento::shouldRegisterVirtualCallModeling(const LangOptions &LO) { + return LO.CPlusPlus; +} - checker->IsPureOnly = - mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "PureOnly"); +bool ento::shouldRegisterPureVirtualCallChecker(const LangOptions &LO) { + return LO.CPlusPlus; } bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) { - return true; + return LO.CPlusPlus; } diff --git a/lib/StaticAnalyzer/Checkers/Yaml.h b/lib/StaticAnalyzer/Checkers/Yaml.h new file mode 100755 index 0000000000000..968c50e33f6d6 --- /dev/null +++ b/lib/StaticAnalyzer/Checkers/Yaml.h @@ -0,0 +1,59 @@ +//== Yaml.h ---------------------------------------------------- -*- C++ -*--=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines convenience functions for handling YAML configuration files +// for checkers/packages. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKER_YAML_H + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "llvm/Support/YAMLTraits.h" + +namespace clang { +namespace ento { + +/// Read the given file from the filesystem and parse it as a yaml file. The +/// template parameter must have a yaml MappingTraits. +/// Emit diagnostic error in case of any failure. +template <class T, class Checker> +llvm::Optional<T> getConfiguration(CheckerManager &Mgr, Checker *Chk, + StringRef Option, StringRef ConfigFile) { + if (ConfigFile.trim().empty()) + return None; + + llvm::vfs::FileSystem *FS = llvm::vfs::getRealFileSystem().get(); + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Buffer = + FS->getBufferForFile(ConfigFile.str()); + + if (std::error_code ec = Buffer.getError()) { + Mgr.reportInvalidCheckerOptionValue(Chk, Option, + "a valid filename instead of '" + + std::string(ConfigFile) + "'"); + return None; + } + + llvm::yaml::Input Input(Buffer.get()->getBuffer()); + T Config; + Input >> Config; + + if (std::error_code ec = Input.error()) { + Mgr.reportInvalidCheckerOptionValue(Chk, Option, + "a valid yaml file: " + ec.message()); + return None; + } + + return Config; +} + +} // namespace ento +} // namespace clang + +#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp index 1b1ffff5ade82..73e1a0d0000ff 100644 --- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -13,7 +13,7 @@ using namespace ento; void AnalysisManager::anchor() { } -AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, +AnalysisManager::AnalysisManager(ASTContext &ASTCtx, const PathDiagnosticConsumers &PDC, StoreManagerCreator storemgr, ConstraintManagerCreator constraintmgr, @@ -38,7 +38,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, Options.ShouldElideConstructors, /*addVirtualBaseBranches=*/true, injector), - Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), + Ctx(ASTCtx), LangOpts(ASTCtx.getLangOpts()), PathConsumers(PDC), CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), options(Options) { diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp index 71abe2ae6c0e8..01ac2bc83bb6b 100644 --- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp +++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -30,25 +30,6 @@ using namespace clang; using namespace ento; using namespace llvm; -std::vector<StringRef> -AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { - static const StringRef StaticAnalyzerChecks[] = { -#define GET_CHECKERS -#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \ - FULLNAME, -#include "clang/StaticAnalyzer/Checkers/Checkers.inc" -#undef CHECKER -#undef GET_CHECKERS - }; - std::vector<StringRef> Result; - for (StringRef CheckName : StaticAnalyzerChecks) { - if (!CheckName.startswith("debug.") && - (IncludeExperimental || !CheckName.startswith("alpha."))) - Result.push_back(CheckName); - } - return Result; -} - void AnalyzerOptions::printFormattedEntry( llvm::raw_ostream &Out, std::pair<StringRef, StringRef> EntryDescPair, diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp index e5a0794f10e2c..1864bcef9b873 100644 --- a/lib/StaticAnalyzer/Core/BugReporter.cpp +++ b/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -24,6 +24,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/SourceLocation.h" @@ -31,7 +32,6 @@ #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" @@ -71,6 +71,7 @@ using namespace clang; using namespace ento; +using namespace llvm; #define DEBUG_TYPE "BugReporter" @@ -85,23 +86,252 @@ BugReporterVisitor::~BugReporterVisitor() = default; void BugReporterContext::anchor() {} //===----------------------------------------------------------------------===// -// Helper routines for walking the ExplodedGraph and fetching statements. +// PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// -static const Stmt *GetPreviousStmt(const ExplodedNode *N) { - for (N = N->getFirstPred(); N; N = N->getFirstPred()) - if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) - return S; +namespace { - return nullptr; +/// A (CallPiece, node assiciated with its CallEnter) pair. +using CallWithEntry = + std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>; +using CallWithEntryStack = SmallVector<CallWithEntry, 6>; + +/// Map from each node to the diagnostic pieces visitors emit for them. +using VisitorsDiagnosticsTy = + llvm::DenseMap<const ExplodedNode *, std::vector<PathDiagnosticPieceRef>>; + +/// A map from PathDiagnosticPiece to the LocationContext of the inlined +/// function call it represents. +using LocationContextMap = + llvm::DenseMap<const PathPieces *, const LocationContext *>; + +/// A helper class that contains everything needed to construct a +/// PathDiagnostic object. It does no much more then providing convenient +/// getters and some well placed asserts for extra security. +class PathDiagnosticConstruct { + /// The consumer we're constructing the bug report for. + const PathDiagnosticConsumer *Consumer; + /// Our current position in the bug path, which is owned by + /// PathDiagnosticBuilder. + const ExplodedNode *CurrentNode; + /// A mapping from parts of the bug path (for example, a function call, which + /// would span backwards from a CallExit to a CallEnter with the nodes in + /// between them) with the location contexts it is associated with. + LocationContextMap LCM; + const SourceManager &SM; + +public: + /// We keep stack of calls to functions as we're ascending the bug path. + /// TODO: PathDiagnostic has a stack doing the same thing, shouldn't we use + /// that instead? + CallWithEntryStack CallStack; + /// The bug report we're constructing. For ease of use, this field is kept + /// public, though some "shortcut" getters are provided for commonly used + /// methods of PathDiagnostic. + std::unique_ptr<PathDiagnostic> PD; + +public: + PathDiagnosticConstruct(const PathDiagnosticConsumer *PDC, + const ExplodedNode *ErrorNode, + const PathSensitiveBugReport *R); + + /// \returns the location context associated with the current position in the + /// bug path. + const LocationContext *getCurrLocationContext() const { + assert(CurrentNode && "Already reached the root!"); + return CurrentNode->getLocationContext(); + } + + /// Same as getCurrLocationContext (they should always return the same + /// location context), but works after reaching the root of the bug path as + /// well. + const LocationContext *getLocationContextForActivePath() const { + return LCM.find(&PD->getActivePath())->getSecond(); + } + + const ExplodedNode *getCurrentNode() const { return CurrentNode; } + + /// Steps the current node to its predecessor. + /// \returns whether we reached the root of the bug path. + bool ascendToPrevNode() { + CurrentNode = CurrentNode->getFirstPred(); + return static_cast<bool>(CurrentNode); + } + + const ParentMap &getParentMap() const { + return getCurrLocationContext()->getParentMap(); + } + + const SourceManager &getSourceManager() const { return SM; } + + const Stmt *getParent(const Stmt *S) const { + return getParentMap().getParent(S); + } + + void updateLocCtxMap(const PathPieces *Path, const LocationContext *LC) { + assert(Path && LC); + LCM[Path] = LC; + } + + const LocationContext *getLocationContextFor(const PathPieces *Path) const { + assert(LCM.count(Path) && + "Failed to find the context associated with these pieces!"); + return LCM.find(Path)->getSecond(); + } + + bool isInLocCtxMap(const PathPieces *Path) const { return LCM.count(Path); } + + PathPieces &getActivePath() { return PD->getActivePath(); } + PathPieces &getMutablePieces() { return PD->getMutablePieces(); } + + bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); } + bool shouldGenerateDiagnostics() const { + return Consumer->shouldGenerateDiagnostics(); + } + bool supportsLogicalOpControlFlow() const { + return Consumer->supportsLogicalOpControlFlow(); + } +}; + +/// Contains every contextual information needed for constructing a +/// PathDiagnostic object for a given bug report. This class and its fields are +/// immutable, and passes a BugReportConstruct object around during the +/// construction. +class PathDiagnosticBuilder : public BugReporterContext { + /// A linear path from the error node to the root. + std::unique_ptr<const ExplodedGraph> BugPath; + /// The bug report we're describing. Visitors create their diagnostics with + /// them being the last entities being able to modify it (for example, + /// changing interestingness here would cause inconsistencies as to how this + /// file and visitors construct diagnostics), hence its const. + const PathSensitiveBugReport *R; + /// The leaf of the bug path. This isn't the same as the bug reports error + /// node, which refers to the *original* graph, not the bug path. + const ExplodedNode *const ErrorNode; + /// The diagnostic pieces visitors emitted, which is expected to be collected + /// by the time this builder is constructed. + std::unique_ptr<const VisitorsDiagnosticsTy> VisitorsDiagnostics; + +public: + /// Find a non-invalidated report for a given equivalence class, and returns + /// a PathDiagnosticBuilder able to construct bug reports for different + /// consumers. Returns None if no valid report is found. + static Optional<PathDiagnosticBuilder> + findValidReport(ArrayRef<PathSensitiveBugReport *> &bugReports, + PathSensitiveBugReporter &Reporter); + + PathDiagnosticBuilder( + BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath, + PathSensitiveBugReport *r, const ExplodedNode *ErrorNode, + std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics); + + /// This function is responsible for generating diagnostic pieces that are + /// *not* provided by bug report visitors. + /// These diagnostics may differ depending on the consumer's settings, + /// and are therefore constructed separately for each consumer. + /// + /// There are two path diagnostics generation modes: with adding edges (used + /// for plists) and without (used for HTML and text). When edges are added, + /// the path is modified to insert artificially generated edges. + /// Otherwise, more detailed diagnostics is emitted for block edges, + /// explaining the transitions in words. + std::unique_ptr<PathDiagnostic> + generate(const PathDiagnosticConsumer *PDC) const; + +private: + void updateStackPiecesWithMessage(PathDiagnosticPieceRef P, + const CallWithEntryStack &CallStack) const; + void generatePathDiagnosticsForNode(PathDiagnosticConstruct &C, + PathDiagnosticLocation &PrevLoc) const; + + void generateMinimalDiagForBlockEdge(PathDiagnosticConstruct &C, + BlockEdge BE) const; + + PathDiagnosticPieceRef + generateDiagForGotoOP(const PathDiagnosticConstruct &C, const Stmt *S, + PathDiagnosticLocation &Start) const; + + PathDiagnosticPieceRef + generateDiagForSwitchOP(const PathDiagnosticConstruct &C, const CFGBlock *Dst, + PathDiagnosticLocation &Start) const; + + PathDiagnosticPieceRef + generateDiagForBinaryOP(const PathDiagnosticConstruct &C, const Stmt *T, + const CFGBlock *Src, const CFGBlock *DstC) const; + + PathDiagnosticLocation + ExecutionContinues(const PathDiagnosticConstruct &C) const; + + PathDiagnosticLocation + ExecutionContinues(llvm::raw_string_ostream &os, + const PathDiagnosticConstruct &C) const; + + const PathSensitiveBugReport *getBugReport() const { return R; } +}; + +} // namespace + +//===----------------------------------------------------------------------===// +// Base implementation of stack hint generators. +//===----------------------------------------------------------------------===// + +StackHintGenerator::~StackHintGenerator() = default; + +std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + if (!N) + return getMessageForSymbolNotFound(); + + ProgramPoint P = N->getLocation(); + CallExitEnd CExit = P.castAs<CallExitEnd>(); + + // FIXME: Use CallEvent to abstract this over all calls. + const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); + const auto *CE = dyn_cast_or_null<CallExpr>(CallSite); + if (!CE) + return {}; + + // Check if one of the parameters are set to the interesting symbol. + unsigned ArgIndex = 0; + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I, ++ArgIndex){ + SVal SV = N->getSVal(*I); + + // Check if the variable corresponding to the symbol is passed by value. + SymbolRef AS = SV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + + // Check if the parameter is a pointer to the symbol. + if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { + // Do not attempt to dereference void*. + if ((*I)->getType()->isVoidPointerType()) + continue; + SVal PSV = N->getState()->getSVal(Reg->getRegion()); + SymbolRef AS = PSV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + } + } + + // Check if we are returning the interesting symbol. + SVal SV = N->getSVal(CE); + SymbolRef RetSym = SV.getAsLocSymbol(); + if (RetSym == Sym) { + return getMessageForReturn(CE); + } + + return getMessageForSymbolNotFound(); } -static inline const Stmt* -GetCurrentOrPreviousStmt(const ExplodedNode *N) { - if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) - return S; +std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) { + // Printed parameters start at 1, not 0. + ++ArgIndex; - return GetPreviousStmt(N); + return (llvm::Twine(Msg) + " via " + std::to_string(ArgIndex) + + llvm::getOrdinalSuffix(ArgIndex) + " parameter").str(); } //===----------------------------------------------------------------------===// @@ -182,16 +412,12 @@ static void removeRedundantMsgs(PathPieces &path) { } } -/// A map from PathDiagnosticPiece to the LocationContext of the inlined -/// function call it represents. -using LocationContextMap = - llvm::DenseMap<const PathPieces *, const LocationContext *>; - /// Recursively scan through a path and prune out calls and macros pieces /// that aren't needed. Return true if afterwards the path contains /// "interesting stuff" which means it shouldn't be pruned from the parent path. -static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, - LocationContextMap &LCM, +static bool removeUnneededCalls(const PathDiagnosticConstruct &C, + PathPieces &pieces, + const PathSensitiveBugReport *R, bool IsInteresting = false) { bool containsSomethingInteresting = IsInteresting; const unsigned N = pieces.size(); @@ -206,9 +432,9 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, case PathDiagnosticPiece::Call: { auto &call = cast<PathDiagnosticCallPiece>(*piece); // Check if the location context is interesting. - assert(LCM.count(&call.path)); - if (!removeUnneededCalls(call.path, R, LCM, - R->isInteresting(LCM[&call.path]))) + if (!removeUnneededCalls( + C, call.path, R, + R->isInteresting(C.getLocationContextFor(&call.path)))) continue; containsSomethingInteresting = true; @@ -216,7 +442,7 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, } case PathDiagnosticPiece::Macro: { auto ¯o = cast<PathDiagnosticMacroPiece>(*piece); - if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting)) + if (!removeUnneededCalls(C, macro.subPieces, R, IsInteresting)) continue; containsSomethingInteresting = true; break; @@ -345,70 +571,23 @@ static void removePiecesWithInvalidLocations(PathPieces &Pieces) { } } -//===----------------------------------------------------------------------===// -// PathDiagnosticBuilder and its associated routines and helper objects. -//===----------------------------------------------------------------------===// - -namespace { - -class PathDiagnosticBuilder : public BugReporterContext { - BugReport *R; - PathDiagnosticConsumer *PDC; - -public: - const LocationContext *LC; - - PathDiagnosticBuilder(GRBugReporter &br, - BugReport *r, InterExplodedGraphMap &Backmap, - PathDiagnosticConsumer *pdc) - : BugReporterContext(br, Backmap), R(r), PDC(pdc), - LC(r->getErrorNode()->getLocationContext()) {} +PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues( + const PathDiagnosticConstruct &C) const { + if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics()) + return PathDiagnosticLocation(S, getSourceManager(), + C.getCurrLocationContext()); - PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); - - PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os, - const ExplodedNode *N); - - BugReport *getBugReport() { return R; } - - Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } - - ParentMap& getParentMap() { return LC->getParentMap(); } - - const Stmt *getParent(const Stmt *S) { - return getParentMap().getParent(S); - } - - PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); - - PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const { - return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal; - } - - bool supportsLogicalOpControlFlow() const { - return PDC ? PDC->supportsLogicalOpControlFlow() : true; - } -}; - -} // namespace - -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { - if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) - return PathDiagnosticLocation(S, getSourceManager(), LC); - - return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), + return PathDiagnosticLocation::createDeclEnd(C.getCurrLocationContext(), getSourceManager()); } -PathDiagnosticLocation -PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, - const ExplodedNode *N) { +PathDiagnosticLocation PathDiagnosticBuilder::ExecutionContinues( + llvm::raw_string_ostream &os, const PathDiagnosticConstruct &C) const { // Slow, but probably doesn't matter. if (os.str().empty()) os << ' '; - const PathDiagnosticLocation &Loc = ExecutionContinues(N); + const PathDiagnosticLocation &Loc = ExecutionContinues(C); if (Loc.asStmt()) os << "Execution continues on line " @@ -416,7 +595,7 @@ PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, << '.'; else { os << "Execution jumps to the end of the "; - const Decl *D = N->getLocationContext()->getDecl(); + const Decl *D = C.getCurrLocationContext()->getDecl(); if (isa<ObjCMethodDecl>(D)) os << "method"; else if (isa<FunctionDecl>(D)) @@ -454,12 +633,14 @@ static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) { } static PathDiagnosticLocation -getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, - const LocationContext *LC, bool allowNestedContexts) { +getEnclosingStmtLocation(const Stmt *S, const LocationContext *LC, + bool allowNestedContexts = false) { if (!S) return {}; - while (const Stmt *Parent = getEnclosingParent(S, P)) { + const SourceManager &SMgr = LC->getDecl()->getASTContext().getSourceManager(); + + while (const Stmt *Parent = getEnclosingParent(S, LC->getParentMap())) { switch (Parent->getStmtClass()) { case Stmt::BinaryOperatorClass: { const auto *B = cast<BinaryOperator>(Parent); @@ -520,59 +701,51 @@ getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, return PathDiagnosticLocation(S, SMgr, LC); } -PathDiagnosticLocation -PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { - assert(S && "Null Stmt passed to getEnclosingStmtLocation"); - return ::getEnclosingStmtLocation(S, getSourceManager(), getParentMap(), LC, - /*allowNestedContexts=*/false); -} - //===----------------------------------------------------------------------===// // "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// -using StackDiagPair = - std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>; -using StackDiagVector = SmallVector<StackDiagPair, 6>; - -static void updateStackPiecesWithMessage(PathDiagnosticPiece &P, - StackDiagVector &CallStack) { - // If the piece contains a special message, add it to all the call - // pieces on the active stack. - if (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) { - if (ep->hasCallStackHint()) - for (const auto &I : CallStack) { - PathDiagnosticCallPiece *CP = I.first; - const ExplodedNode *N = I.second; - std::string stackMsg = ep->getCallStackMessage(N); - - // The last message on the path to final bug is the most important - // one. Since we traverse the path backwards, do not add the message - // if one has been previously added. - if (!CP->hasCallStackMessage()) - CP->setCallStackMessage(stackMsg); - } - } + +/// If the piece contains a special message, add it to all the call pieces on +/// the active stack. For example, my_malloc allocated memory, so MallocChecker +/// will construct an event at the call to malloc(), and add a stack hint that +/// an allocated memory was returned. We'll use this hint to construct a message +/// when returning from the call to my_malloc +/// +/// void *my_malloc() { return malloc(sizeof(int)); } +/// void fishy() { +/// void *ptr = my_malloc(); // returned allocated memory +/// } // leak +void PathDiagnosticBuilder::updateStackPiecesWithMessage( + PathDiagnosticPieceRef P, const CallWithEntryStack &CallStack) const { + if (R->hasCallStackHint(P)) + for (const auto &I : CallStack) { + PathDiagnosticCallPiece *CP = I.first; + const ExplodedNode *N = I.second; + std::string stackMsg = R->getCallStackMessage(P, N); + + // The last message on the path to final bug is the most important + // one. Since we traverse the path backwards, do not add the message + // if one has been previously added. + if (!CP->hasCallStackMessage()) + CP->setCallStackMessage(stackMsg); + } } static void CompactMacroExpandedPieces(PathPieces &path, const SourceManager& SM); +PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForSwitchOP( + const PathDiagnosticConstruct &C, const CFGBlock *Dst, + PathDiagnosticLocation &Start) const { -std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( - const ExplodedNode *N, - const CFGBlock *Dst, - const SourceManager &SM, - const LocationContext *LC, - PathDiagnosticBuilder &PDB, - PathDiagnosticLocation &Start - ) { + const SourceManager &SM = getSourceManager(); // Figure out what case arm we took. std::string sbuf; llvm::raw_string_ostream os(sbuf); PathDiagnosticLocation End; if (const Stmt *S = Dst->getLabel()) { - End = PathDiagnosticLocation(S, SM, LC); + End = PathDiagnosticLocation(S, SM, C.getCurrLocationContext()); switch (S->getStmtClass()) { default: @@ -605,7 +778,7 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( } if (GetRawInt) - os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); + os << LHS->EvaluateKnownConstInt(getASTContext()); os << ":' at line " << End.asLocation().getExpansionLineNumber(); break; @@ -613,33 +786,29 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( } } else { os << "'Default' branch taken. "; - End = PDB.ExecutionContinues(os, N); + End = ExecutionContinues(os, C); } return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); } +PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForGotoOP( + const PathDiagnosticConstruct &C, const Stmt *S, + PathDiagnosticLocation &Start) const { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + const PathDiagnosticLocation &End = + getEnclosingStmtLocation(S, C.getCurrLocationContext()); + os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); +} -std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForGotoOP( - const Stmt *S, - PathDiagnosticBuilder &PDB, - PathDiagnosticLocation &Start) { - std::string sbuf; - llvm::raw_string_ostream os(sbuf); - const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); - os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); - return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); +PathDiagnosticPieceRef PathDiagnosticBuilder::generateDiagForBinaryOP( + const PathDiagnosticConstruct &C, const Stmt *T, const CFGBlock *Src, + const CFGBlock *Dst) const { -} + const SourceManager &SM = getSourceManager(); -std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( - const ExplodedNode *N, - const Stmt *T, - const CFGBlock *Src, - const CFGBlock *Dst, - const SourceManager &SM, - PathDiagnosticBuilder &PDB, - const LocationContext *LC) { const auto *B = cast<BinaryOperator>(T); std::string sbuf; llvm::raw_string_ostream os(sbuf); @@ -652,13 +821,14 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( if (*(Src->succ_begin() + 1) == Dst) { os << "false"; - End = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); Start = PathDiagnosticLocation::createOperatorLoc(B, SM); } else { os << "true"; - Start = PathDiagnosticLocation(B->getLHS(), SM, LC); - End = PDB.ExecutionContinues(N); + Start = + PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); + End = ExecutionContinues(C); } } else { assert(B->getOpcode() == BO_LOr); @@ -667,11 +837,12 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( if (*(Src->succ_begin() + 1) == Dst) { os << "false"; - Start = PathDiagnosticLocation(B->getLHS(), SM, LC); - End = PDB.ExecutionContinues(N); + Start = + PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); + End = ExecutionContinues(C); } else { os << "true"; - End = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PathDiagnosticLocation(B->getLHS(), SM, C.getCurrLocationContext()); Start = PathDiagnosticLocation::createOperatorLoc(B, SM); } @@ -680,11 +851,10 @@ std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( os.str()); } -void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, - const SourceManager &SM, - PathDiagnosticBuilder &PDB, - PathDiagnostic &PD) { - const LocationContext *LC = N->getLocationContext(); +void PathDiagnosticBuilder::generateMinimalDiagForBlockEdge( + PathDiagnosticConstruct &C, BlockEdge BE) const { + const SourceManager &SM = getSourceManager(); + const LocationContext *LC = C.getCurrLocationContext(); const CFGBlock *Src = BE.getSrc(); const CFGBlock *Dst = BE.getDst(); const Stmt *T = Src->getTerminatorStmt(); @@ -698,14 +868,13 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, case Stmt::GotoStmtClass: case Stmt::IndirectGotoStmtClass: { - if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) - PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start)); + if (const Stmt *S = C.getCurrentNode()->getNextStmtForDiagnostics()) + C.getActivePath().push_front(generateDiagForGotoOP(C, S, Start)); break; } case Stmt::SwitchStmtClass: { - PD.getActivePath().push_front( - generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start)); + C.getActivePath().push_front(generateDiagForSwitchOP(C, Dst, Start)); break; } @@ -713,8 +882,8 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, case Stmt::ContinueStmtClass: { std::string sbuf; llvm::raw_string_ostream os(sbuf); - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - PD.getActivePath().push_front( + PathDiagnosticLocation End = ExecutionContinues(os, C); + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); break; } @@ -731,24 +900,22 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, else os << "true"; - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); break; } // Determine control-flow for short-circuited '&&' and '||'. case Stmt::BinaryOperatorClass: { - if (!PDB.supportsLogicalOpControlFlow()) + if (!C.supportsLogicalOpControlFlow()) break; - std::shared_ptr<PathDiagnosticControlFlowPiece> Diag = - generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC); - PD.getActivePath().push_front(Diag); + C.getActivePath().push_front(generateDiagForBinaryOP(C, T, Src, Dst)); break; } @@ -758,21 +925,21 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, llvm::raw_string_ostream os(sbuf); os << "Loop condition is true. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PathDiagnosticLocation End = ExecutionContinues(os, C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); } else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Loop condition is false. Exiting loop")); } @@ -785,19 +952,19 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, llvm::raw_string_ostream os(sbuf); os << "Loop condition is false. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PathDiagnosticLocation End = ExecutionContinues(os, C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); } else { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Loop condition is true. Entering loop body")); } @@ -805,17 +972,17 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, break; case Stmt::IfStmtClass: { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PathDiagnosticLocation End = ExecutionContinues(C); if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + End = getEnclosingStmtLocation(S, C.getCurrLocationContext()); if (*(Src->succ_begin() + 1) == Dst) - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Taking false branch")); else - PD.getActivePath().push_front( + C.getActivePath().push_front( std::make_shared<PathDiagnosticControlFlowPiece>( Start, End, "Taking true branch")); @@ -824,75 +991,6 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, } } -// Cone-of-influence: support the reverse propagation of "interesting" symbols -// and values by tracing interesting calculations backwards through evaluated -// expressions along a path. This is probably overly complicated, but the idea -// is that if an expression computed an "interesting" value, the child -// expressions are also likely to be "interesting" as well (which then -// propagates to the values they in turn compute). This reverse propagation -// is needed to track interesting correlations across function call boundaries, -// where formal arguments bind to actual arguments, etc. This is also needed -// because the constraint solver sometimes simplifies certain symbolic values -// into constants when appropriate, and this complicates reasoning about -// interesting values. -using InterestingExprs = llvm::DenseSet<const Expr *>; - -static void reversePropagateIntererstingSymbols(BugReport &R, - InterestingExprs &IE, - const ProgramState *State, - const Expr *Ex, - const LocationContext *LCtx) { - SVal V = State->getSVal(Ex, LCtx); - if (!(R.isInteresting(V) || IE.count(Ex))) - return; - - switch (Ex->getStmtClass()) { - default: - if (!isa<CastExpr>(Ex)) - break; - LLVM_FALLTHROUGH; - case Stmt::BinaryOperatorClass: - case Stmt::UnaryOperatorClass: { - for (const Stmt *SubStmt : Ex->children()) { - if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) { - IE.insert(child); - SVal ChildV = State->getSVal(child, LCtx); - R.markInteresting(ChildV); - } - } - break; - } - } - - R.markInteresting(V); -} - -static void reversePropagateInterestingSymbols(BugReport &R, - InterestingExprs &IE, - const ProgramState *State, - const LocationContext *CalleeCtx) -{ - // FIXME: Handle non-CallExpr-based CallEvents. - const StackFrameContext *Callee = CalleeCtx->getStackFrame(); - const Stmt *CallSite = Callee->getCallSite(); - if (const auto *CE = dyn_cast_or_null<CallExpr>(CallSite)) { - if (const auto *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { - FunctionDecl::param_const_iterator PI = FD->param_begin(), - PE = FD->param_end(); - CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); - for (; AI != AE && PI != PE; ++AI, ++PI) { - if (const Expr *ArgE = *AI) { - if (const ParmVarDecl *PD = *PI) { - Loc LV = State->getLValue(PD, CalleeCtx); - if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV))) - IE.insert(ArgE); - } - } - } - } - } -} - //===----------------------------------------------------------------------===// // Functions for determining if a loop was executed 0 times. //===----------------------------------------------------------------------===// @@ -916,7 +1014,8 @@ static bool isJumpToFalseBranch(const BlockEdge *BE) { return (*(Src->succ_begin()+1) == BE->getDst()); } -static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { +static bool isContainedByStmt(const ParentMap &PM, const Stmt *S, + const Stmt *SubS) { while (SubS) { if (SubS == S) return true; @@ -925,7 +1024,7 @@ static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { return false; } -static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, +static const Stmt *getStmtBeforeCond(const ParentMap &PM, const Stmt *Term, const ExplodedNode *N) { while (N) { Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); @@ -939,7 +1038,7 @@ static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, return nullptr; } -static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { +static bool isInLoopBody(const ParentMap &PM, const Stmt *S, const Stmt *Term) { const Stmt *LoopBody = nullptr; switch (Term->getStmtClass()) { case Stmt::CXXForRangeStmtClass: { @@ -1007,30 +1106,20 @@ static const Stmt *getTerminatorCondition(const CFGBlock *B) { return S; } -static const char StrEnteringLoop[] = "Entering loop body"; -static const char StrLoopBodyZero[] = "Loop body executed 0 times"; -static const char StrLoopRangeEmpty[] = - "Loop body skipped when range is empty"; -static const char StrLoopCollectionEmpty[] = - "Loop body skipped when collection is empty"; +constexpr llvm::StringLiteral StrEnteringLoop = "Entering loop body"; +constexpr llvm::StringLiteral StrLoopBodyZero = "Loop body executed 0 times"; +constexpr llvm::StringLiteral StrLoopRangeEmpty = + "Loop body skipped when range is empty"; +constexpr llvm::StringLiteral StrLoopCollectionEmpty = + "Loop body skipped when collection is empty"; static std::unique_ptr<FilesToLineNumsMap> -findExecutedLines(SourceManager &SM, const ExplodedNode *N); - -/// Generate diagnostics for the node \p N, -/// and write it into \p PD. -/// \p AddPathEdges Whether diagnostic consumer can generate path arrows -/// showing both row and column. -static void generatePathDiagnosticsForNode(const ExplodedNode *N, - PathDiagnostic &PD, - PathDiagnosticLocation &PrevLoc, - PathDiagnosticBuilder &PDB, - LocationContextMap &LCM, - StackDiagVector &CallStack, - InterestingExprs &IE, - bool AddPathEdges) { - ProgramPoint P = N->getLocation(); - const SourceManager& SM = PDB.getSourceManager(); +findExecutedLines(const SourceManager &SM, const ExplodedNode *N); + +void PathDiagnosticBuilder::generatePathDiagnosticsForNode( + PathDiagnosticConstruct &C, PathDiagnosticLocation &PrevLoc) const { + ProgramPoint P = C.getCurrentNode()->getLocation(); + const SourceManager &SM = getSourceManager(); // Have we encountered an entrance to a call? It may be // the case that we have not encountered a matching @@ -1038,7 +1127,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, // terminated within the call itself. if (auto CE = P.getAs<CallEnter>()) { - if (AddPathEdges) { + if (C.shouldAddPathEdges()) { // Add an edge to the start of the function. const StackFrameContext *CalleeLC = CE->getCalleeContext(); const Decl *D = CalleeLC->getDecl(); @@ -1049,139 +1138,105 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, // because the exit edge comes from a statement (i.e. return), // not from declaration. if (D->hasBody()) - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createBegin(D, SM)); + addEdgeToPath(C.getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM)); } // Did we visit an entire call? - bool VisitedEntireCall = PD.isWithinCall(); - PD.popActivePath(); + bool VisitedEntireCall = C.PD->isWithinCall(); + C.PD->popActivePath(); - PathDiagnosticCallPiece *C; + PathDiagnosticCallPiece *Call; if (VisitedEntireCall) { - C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get()); + Call = cast<PathDiagnosticCallPiece>(C.getActivePath().front().get()); } else { + // The path terminated within a nested location context, create a new + // call piece to encapsulate the rest of the path pieces. const Decl *Caller = CE->getLocationContext()->getDecl(); - C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); - - if (AddPathEdges) { - // Since we just transferred the path over to the call piece, - // reset the mapping from active to location context. - assert(PD.getActivePath().size() == 1 && - PD.getActivePath().front().get() == C); - LCM[&PD.getActivePath()] = nullptr; - } - - // Record the location context mapping for the path within - // the call. - assert(LCM[&C->path] == nullptr || - LCM[&C->path] == CE->getCalleeContext()); - LCM[&C->path] = CE->getCalleeContext(); - - // If this is the first item in the active path, record - // the new mapping from active path to location context. - const LocationContext *&NewLC = LCM[&PD.getActivePath()]; - if (!NewLC) - NewLC = N->getLocationContext(); - - PDB.LC = NewLC; - } - C->setCallee(*CE, SM); + Call = PathDiagnosticCallPiece::construct(C.getActivePath(), Caller); + assert(C.getActivePath().size() == 1 && + C.getActivePath().front().get() == Call); + + // Since we just transferred the path over to the call piece, reset the + // mapping of the active path to the current location context. + assert(C.isInLocCtxMap(&C.getActivePath()) && + "When we ascend to a previously unvisited call, the active path's " + "address shouldn't change, but rather should be compacted into " + "a single CallEvent!"); + C.updateLocCtxMap(&C.getActivePath(), C.getCurrLocationContext()); + + // Record the location context mapping for the path within the call. + assert(!C.isInLocCtxMap(&Call->path) && + "When we ascend to a previously unvisited call, this must be the " + "first time we encounter the caller context!"); + C.updateLocCtxMap(&Call->path, CE->getCalleeContext()); + } + Call->setCallee(*CE, SM); // Update the previous location in the active path. - PrevLoc = C->getLocation(); + PrevLoc = Call->getLocation(); - if (!CallStack.empty()) { - assert(CallStack.back().first == C); - CallStack.pop_back(); + if (!C.CallStack.empty()) { + assert(C.CallStack.back().first == Call); + C.CallStack.pop_back(); } return; } - - if (AddPathEdges) { - // Query the location context here and the previous location - // as processing CallEnter may change the active path. - PDB.LC = N->getLocationContext(); - - // Record the mapping from the active path to the location - // context. - assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == PDB.LC); - LCM[&PD.getActivePath()] = PDB.LC; - } + assert(C.getCurrLocationContext() == C.getLocationContextForActivePath() && + "The current position in the bug path is out of sync with the " + "location context associated with the active path!"); // Have we encountered an exit from a function call? if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { // We are descending into a call (backwards). Construct // a new call piece to contain the path pieces for that call. - auto C = PathDiagnosticCallPiece::construct(*CE, SM); + auto Call = PathDiagnosticCallPiece::construct(*CE, SM); // Record the mapping from call piece to LocationContext. - LCM[&C->path] = CE->getCalleeContext(); - - if (AddPathEdges) { - const Stmt *S = CE->getCalleeContext()->getCallSite(); - // Propagate the interesting symbols accordingly. - if (const auto *Ex = dyn_cast_or_null<Expr>(S)) { - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - } + assert(!C.isInLocCtxMap(&Call->path) && + "We just entered a call, this must've been the first time we " + "encounter its context!"); + C.updateLocCtxMap(&Call->path, CE->getCalleeContext()); + + if (C.shouldAddPathEdges()) { // Add the edge to the return site. - addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn); + addEdgeToPath(C.getActivePath(), PrevLoc, Call->callReturn); PrevLoc.invalidate(); } - auto *P = C.get(); - PD.getActivePath().push_front(std::move(C)); + auto *P = Call.get(); + C.getActivePath().push_front(std::move(Call)); // Make the contents of the call the active path for now. - PD.pushActivePath(&P->path); - CallStack.push_back(StackDiagPair(P, N)); + C.PD->pushActivePath(&P->path); + C.CallStack.push_back(CallWithEntry(P, C.getCurrentNode())); return; } if (auto PS = P.getAs<PostStmt>()) { - if (!AddPathEdges) + if (!C.shouldAddPathEdges()) return; - // For expressions, make sure we propagate the - // interesting symbols correctly. - if (const Expr *Ex = PS->getStmtAs<Expr>()) - reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), Ex, - N->getLocationContext()); - // Add an edge. If this is an ObjCForCollectionStmt do // not add an edge here as it appears in the CFG both // as a terminator and as a terminator condition. if (!isa<ObjCForCollectionStmt>(PS->getStmt())) { PathDiagnosticLocation L = - PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC); - addEdgeToPath(PD.getActivePath(), PrevLoc, L); + PathDiagnosticLocation(PS->getStmt(), SM, C.getCurrLocationContext()); + addEdgeToPath(C.getActivePath(), PrevLoc, L); } } else if (auto BE = P.getAs<BlockEdge>()) { - if (!AddPathEdges) { - generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD); + if (!C.shouldAddPathEdges()) { + generateMinimalDiagForBlockEdge(C, *BE); return; } - // Does this represent entering a call? If so, look at propagating - // interesting symbols across call boundaries. - if (const ExplodedNode *NextNode = N->getFirstPred()) { - const LocationContext *CallerCtx = NextNode->getLocationContext(); - const LocationContext *CalleeCtx = PDB.LC; - if (CallerCtx != CalleeCtx && AddPathEdges) { - reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, - N->getState().get(), CalleeCtx); - } - } - // Are we jumping to the head of a loop? Add a special diagnostic. if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { - PathDiagnosticLocation L(Loop, SM, PDB.LC); + PathDiagnosticLocation L(Loop, SM, C.getCurrLocationContext()); const Stmt *Body = nullptr; if (const auto *FS = dyn_cast<ForStmt>(Loop)) @@ -1200,27 +1255,27 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, "of the loop"); p->setPrunable(true); - addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation()); - PD.getActivePath().push_front(std::move(p)); + addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation()); + C.getActivePath().push_front(std::move(p)); if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) { - addEdgeToPath(PD.getActivePath(), PrevLoc, - PathDiagnosticLocation::createEndBrace(CS, SM)); + addEdgeToPath(C.getActivePath(), PrevLoc, + PathDiagnosticLocation::createEndBrace(CS, SM)); } } const CFGBlock *BSrc = BE->getSrc(); - ParentMap &PM = PDB.getParentMap(); + const ParentMap &PM = C.getParentMap(); if (const Stmt *Term = BSrc->getTerminatorStmt()) { // Are we jumping past the loop body without ever executing the // loop (because the condition was false)? if (isLoop(Term)) { const Stmt *TermCond = getTerminatorCondition(BSrc); - bool IsInLoopBody = - isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term); + bool IsInLoopBody = isInLoopBody( + PM, getStmtBeforeCond(PM, TermCond, C.getCurrentNode()), Term); - const char *str = nullptr; + StringRef str; if (isJumpToFalseBranch(&*BE)) { if (!IsInLoopBody) { @@ -1236,31 +1291,41 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N, str = StrEnteringLoop; } - if (str) { - PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC); + if (!str.empty()) { + PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, + C.getCurrLocationContext()); auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str); PE->setPrunable(true); - addEdgeToPath(PD.getActivePath(), PrevLoc, - PE->getLocation()); - PD.getActivePath().push_front(std::move(PE)); + addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation()); + C.getActivePath().push_front(std::move(PE)); } } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) || isa<GotoStmt>(Term)) { - PathDiagnosticLocation L(Term, SM, PDB.LC); - addEdgeToPath(PD.getActivePath(), PrevLoc, L); + PathDiagnosticLocation L(Term, SM, C.getCurrLocationContext()); + addEdgeToPath(C.getActivePath(), PrevLoc, L); } } } } static std::unique_ptr<PathDiagnostic> -generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { +generateDiagnosticForBasicReport(const BasicBugReport *R) { const BugType &BT = R->getBugType(); - return llvm::make_unique<PathDiagnostic>( - R->getBugType().getCheckName(), R->getDeclWithIssue(), - R->getBugType().getName(), R->getDescription(), - R->getShortDescription(/*UseFallback=*/false), BT.getCategory(), - R->getUniqueingLocation(), R->getUniqueingDecl(), + return std::make_unique<PathDiagnostic>( + BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(), + R->getDescription(), R->getShortDescription(/*UseFallback=*/false), + BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(), + std::make_unique<FilesToLineNumsMap>()); +} + +static std::unique_ptr<PathDiagnostic> +generateEmptyDiagnosticForReport(const PathSensitiveBugReport *R, + const SourceManager &SM) { + const BugType &BT = R->getBugType(); + return std::make_unique<PathDiagnostic>( + BT.getCheckerName(), R->getDeclWithIssue(), BT.getDescription(), + R->getDescription(), R->getShortDescription(/*UseFallback=*/false), + BT.getCategory(), R->getUniqueingLocation(), R->getUniqueingDecl(), findExecutedLines(SM, R->getErrorNode())); } @@ -1342,8 +1407,8 @@ using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>; /// This avoids a "swoosh" effect, where an edge from a top-level statement A /// points to a sub-expression B.1 that's not at the start of B. In these cases, /// we'd like to see an edge from A to B, then another one from B to B.1. -static void addContextEdges(PathPieces &pieces, SourceManager &SM, - const ParentMap &PM, const LocationContext *LCtx) { +static void addContextEdges(PathPieces &pieces, const LocationContext *LC) { + const ParentMap &PM = LC->getParentMap(); PathPieces::iterator Prev = pieces.end(); for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E; Prev = I, ++I) { @@ -1360,7 +1425,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) { SrcContexts.push_back(NextSrcContext); InnerStmt = NextSrcContext.asStmt(); - NextSrcContext = getEnclosingStmtLocation(InnerStmt, SM, PM, LCtx, + NextSrcContext = getEnclosingStmtLocation(InnerStmt, LC, /*allowNested=*/true); } @@ -1373,7 +1438,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM, // We are looking at an edge. Is the destination within a larger // expression? PathDiagnosticLocation DstContext = - getEnclosingStmtLocation(Dst, SM, PM, LCtx, /*allowNested=*/true); + getEnclosingStmtLocation(Dst, LC, /*allowNested=*/true); if (!DstContext.isValid() || DstContext.asStmt() == Dst) break; @@ -1493,7 +1558,7 @@ static void simplifySimpleBranches(PathPieces &pieces) { /// If the locations in the range are not on the same line, returns None. /// /// Note that this does not do a precise user-visible character or column count. -static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, +static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, SourceRange Range) { SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()), SM.getExpansionRange(Range.getEnd()).getEnd()); @@ -1523,7 +1588,7 @@ static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, } /// \sa getLengthOnSingleLine(SourceManager, SourceRange) -static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, +static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM, const Stmt *S) { return getLengthOnSingleLine(SM, S->getSourceRange()); } @@ -1544,7 +1609,7 @@ static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, /// - if there is an inlined call between the edges instead of a single event. /// - if the whole statement is large enough that having subexpression arrows /// might be helpful. -static void removeContextCycles(PathPieces &Path, SourceManager &SM) { +static void removeContextCycles(PathPieces &Path, const SourceManager &SM) { for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) { // Pattern match the current piece and its successor. const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); @@ -1599,7 +1664,7 @@ static void removeContextCycles(PathPieces &Path, SourceManager &SM) { } /// Return true if X is contained by Y. -static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) { +static bool lexicalContains(const ParentMap &PM, const Stmt *X, const Stmt *Y) { while (X) { if (X == Y) return true; @@ -1609,8 +1674,8 @@ static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) { } // Remove short edges on the same line less than 3 columns in difference. -static void removePunyEdges(PathPieces &path, SourceManager &SM, - ParentMap &PM) { +static void removePunyEdges(PathPieces &path, const SourceManager &SM, + const ParentMap &PM) { bool erased = false; for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; @@ -1685,13 +1750,13 @@ static void removeIdenticalEvents(PathPieces &path) { } } -static bool optimizeEdges(PathPieces &path, SourceManager &SM, - OptimizedCallsSet &OCS, - LocationContextMap &LCM) { +static bool optimizeEdges(const PathDiagnosticConstruct &C, PathPieces &path, + OptimizedCallsSet &OCS) { bool hasChanges = false; - const LocationContext *LC = LCM[&path]; + const LocationContext *LC = C.getLocationContextFor(&path); assert(LC); - ParentMap &PM = LC->getParentMap(); + const ParentMap &PM = LC->getParentMap(); + const SourceManager &SM = C.getSourceManager(); for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) { // Optimize subpaths. @@ -1699,7 +1764,8 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, // Record the fact that a call has been optimized so we only do the // effort once. if (!OCS.count(CallI)) { - while (optimizeEdges(CallI->path, SM, OCS, LCM)) {} + while (optimizeEdges(C, CallI->path, OCS)) { + } OCS.insert(CallI); } ++I; @@ -1845,7 +1911,7 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, if (!hasChanges) { // Adjust edges into subexpressions to make them more uniform // and aesthetically pleasing. - addContextEdges(path, SM, PM, LC); + addContextEdges(path, LC); // Remove "cyclical" edges that include one or more context edges. removeContextCycles(path, SM); // Hoist edges originating from branch conditions to branches @@ -1866,27 +1932,24 @@ static bool optimizeEdges(PathPieces &path, SourceManager &SM, /// statement had an invalid source location), this function does nothing. // FIXME: We should just generate invalid edges anyway and have the optimizer // deal with them. -static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM, - SourceManager &SM) { +static void dropFunctionEntryEdge(const PathDiagnosticConstruct &C, + PathPieces &Path) { const auto *FirstEdge = dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get()); if (!FirstEdge) return; - const Decl *D = LCM[&Path]->getDecl(); - PathDiagnosticLocation EntryLoc = PathDiagnosticLocation::createBegin(D, SM); + const Decl *D = C.getLocationContextFor(&Path)->getDecl(); + PathDiagnosticLocation EntryLoc = + PathDiagnosticLocation::createBegin(D, C.getSourceManager()); if (FirstEdge->getStartLocation() != EntryLoc) return; Path.pop_front(); } -using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *, - std::vector<std::shared_ptr<PathDiagnosticPiece>>>; - /// Populate executes lines with lines containing at least one diagnostics. -static void updateExecutedLinesWithDiagnosticPieces( - PathDiagnostic &PD) { +static void updateExecutedLinesWithDiagnosticPieces(PathDiagnostic &PD) { PathPieces path = PD.path.flatten(/*ShouldFlattenMacros=*/true); FilesToLineNumsMap &ExecutedLines = PD.getExecutedLines(); @@ -1900,56 +1963,61 @@ static void updateExecutedLinesWithDiagnosticPieces( } } -/// This function is responsible for generating diagnostic pieces that are -/// *not* provided by bug report visitors. -/// These diagnostics may differ depending on the consumer's settings, -/// and are therefore constructed separately for each consumer. -/// -/// There are two path diagnostics generation modes: with adding edges (used -/// for plists) and without (used for HTML and text). -/// When edges are added (\p ActiveScheme is Extensive), -/// the path is modified to insert artificially generated -/// edges. -/// Otherwise, more detailed diagnostics is emitted for block edges, explaining -/// the transitions in words. -static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( - PathDiagnosticConsumer::PathGenerationScheme ActiveScheme, - PathDiagnosticBuilder &PDB, - const ExplodedNode *ErrorNode, - const VisitorsDiagnosticsTy &VisitorsDiagnostics) { - - bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None); - bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive); - SourceManager &SM = PDB.getSourceManager(); - BugReport *R = PDB.getBugReport(); - AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions(); - StackDiagVector CallStack; - InterestingExprs IE; - LocationContextMap LCM; - std::unique_ptr<PathDiagnostic> PD = generateEmptyDiagnosticForReport(R, SM); - - if (GenerateDiagnostics) { - auto EndNotes = VisitorsDiagnostics.find(ErrorNode); - std::shared_ptr<PathDiagnosticPiece> LastPiece; - if (EndNotes != VisitorsDiagnostics.end()) { - assert(!EndNotes->second.empty()); - LastPiece = EndNotes->second[0]; - } else { - LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, ErrorNode, *R); - } - PD->setEndOfPath(LastPiece); +PathDiagnosticConstruct::PathDiagnosticConstruct( + const PathDiagnosticConsumer *PDC, const ExplodedNode *ErrorNode, + const PathSensitiveBugReport *R) + : Consumer(PDC), CurrentNode(ErrorNode), + SM(CurrentNode->getCodeDecl().getASTContext().getSourceManager()), + PD(generateEmptyDiagnosticForReport(R, getSourceManager())) { + LCM[&PD->getActivePath()] = ErrorNode->getLocationContext(); +} + +PathDiagnosticBuilder::PathDiagnosticBuilder( + BugReporterContext BRC, std::unique_ptr<ExplodedGraph> BugPath, + PathSensitiveBugReport *r, const ExplodedNode *ErrorNode, + std::unique_ptr<VisitorsDiagnosticsTy> VisitorsDiagnostics) + : BugReporterContext(BRC), BugPath(std::move(BugPath)), R(r), + ErrorNode(ErrorNode), + VisitorsDiagnostics(std::move(VisitorsDiagnostics)) {} + +std::unique_ptr<PathDiagnostic> +PathDiagnosticBuilder::generate(const PathDiagnosticConsumer *PDC) const { + PathDiagnosticConstruct Construct(PDC, ErrorNode, R); + + const SourceManager &SM = getSourceManager(); + const AnalyzerOptions &Opts = getAnalyzerOptions(); + StringRef ErrorTag = ErrorNode->getLocation().getTag()->getTagDescription(); + + // See whether we need to silence the checker/package. + // FIXME: This will not work if the report was emitted with an incorrect tag. + for (const std::string &CheckerOrPackage : Opts.SilencedCheckersAndPackages) { + if (ErrorTag.startswith(CheckerOrPackage)) + return nullptr; } - PathDiagnosticLocation PrevLoc = PD->getLocation(); - const ExplodedNode *NextNode = ErrorNode->getFirstPred(); - while (NextNode) { - if (GenerateDiagnostics) - generatePathDiagnosticsForNode( - NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges); + if (!PDC->shouldGenerateDiagnostics()) + return generateEmptyDiagnosticForReport(R, getSourceManager()); + + // Construct the final (warning) event for the bug report. + auto EndNotes = VisitorsDiagnostics->find(ErrorNode); + PathDiagnosticPieceRef LastPiece; + if (EndNotes != VisitorsDiagnostics->end()) { + assert(!EndNotes->second.empty()); + LastPiece = EndNotes->second[0]; + } else { + LastPiece = BugReporterVisitor::getDefaultEndPath(*this, ErrorNode, + *getBugReport()); + } + Construct.PD->setEndOfPath(LastPiece); + + PathDiagnosticLocation PrevLoc = Construct.PD->getLocation(); + // From the error node to the root, ascend the bug path and construct the bug + // report. + while (Construct.ascendToPrevNode()) { + generatePathDiagnosticsForNode(Construct, PrevLoc); - auto VisitorNotes = VisitorsDiagnostics.find(NextNode); - NextNode = NextNode->getFirstPred(); - if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end()) + auto VisitorNotes = VisitorsDiagnostics->find(Construct.getCurrentNode()); + if (VisitorNotes == VisitorsDiagnostics->end()) continue; // This is a workaround due to inability to put shared PathDiagnosticPiece @@ -1957,74 +2025,74 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( std::set<llvm::FoldingSetNodeID> DeduplicationSet; // Add pieces from custom visitors. - for (const auto &Note : VisitorNotes->second) { + for (const PathDiagnosticPieceRef &Note : VisitorNotes->second) { llvm::FoldingSetNodeID ID; Note->Profile(ID); - auto P = DeduplicationSet.insert(ID); - if (!P.second) + if (!DeduplicationSet.insert(ID).second) continue; - if (AddPathEdges) - addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation()); - updateStackPiecesWithMessage(*Note, CallStack); - PD->getActivePath().push_front(Note); + if (PDC->shouldAddPathEdges()) + addEdgeToPath(Construct.getActivePath(), PrevLoc, Note->getLocation()); + updateStackPiecesWithMessage(Note, Construct.CallStack); + Construct.getActivePath().push_front(Note); } } - if (AddPathEdges) { + if (PDC->shouldAddPathEdges()) { // Add an edge to the start of the function. // We'll prune it out later, but it helps make diagnostics more uniform. - const StackFrameContext *CalleeLC = PDB.LC->getStackFrame(); + const StackFrameContext *CalleeLC = + Construct.getLocationContextForActivePath()->getStackFrame(); const Decl *D = CalleeLC->getDecl(); - addEdgeToPath(PD->getActivePath(), PrevLoc, + addEdgeToPath(Construct.getActivePath(), PrevLoc, PathDiagnosticLocation::createBegin(D, SM)); } // Finally, prune the diagnostic path of uninteresting stuff. - if (!PD->path.empty()) { + if (!Construct.PD->path.empty()) { if (R->shouldPrunePath() && Opts.ShouldPrunePaths) { bool stillHasNotes = - removeUnneededCalls(PD->getMutablePieces(), R, LCM); + removeUnneededCalls(Construct, Construct.getMutablePieces(), R); assert(stillHasNotes); (void)stillHasNotes; } // Remove pop-up notes if needed. if (!Opts.ShouldAddPopUpNotes) - removePopUpNotes(PD->getMutablePieces()); + removePopUpNotes(Construct.getMutablePieces()); // Redirect all call pieces to have valid locations. - adjustCallLocations(PD->getMutablePieces()); - removePiecesWithInvalidLocations(PD->getMutablePieces()); + adjustCallLocations(Construct.getMutablePieces()); + removePiecesWithInvalidLocations(Construct.getMutablePieces()); - if (AddPathEdges) { + if (PDC->shouldAddPathEdges()) { // Reduce the number of edges from a very conservative set // to an aesthetically pleasing subset that conveys the // necessary information. OptimizedCallsSet OCS; - while (optimizeEdges(PD->getMutablePieces(), SM, OCS, LCM)) {} + while (optimizeEdges(Construct, Construct.getMutablePieces(), OCS)) { + } // Drop the very first function-entry edge. It's not really necessary // for top-level functions. - dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM); + dropFunctionEntryEdge(Construct, Construct.getMutablePieces()); } // Remove messages that are basically the same, and edges that may not // make sense. // We have to do this after edge optimization in the Extensive mode. - removeRedundantMsgs(PD->getMutablePieces()); - removeEdgesToDefaultInitializers(PD->getMutablePieces()); + removeRedundantMsgs(Construct.getMutablePieces()); + removeEdgesToDefaultInitializers(Construct.getMutablePieces()); } - if (GenerateDiagnostics && Opts.ShouldDisplayMacroExpansions) - CompactMacroExpandedPieces(PD->getMutablePieces(), SM); + if (Opts.ShouldDisplayMacroExpansions) + CompactMacroExpandedPieces(Construct.getMutablePieces(), SM); - return PD; + return std::move(Construct.PD); } - //===----------------------------------------------------------------------===// // Methods for BugType and subclasses. //===----------------------------------------------------------------------===// @@ -2037,9 +2105,8 @@ void BuiltinBug::anchor() {} // Methods for BugReport and subclasses. //===----------------------------------------------------------------------===// -void BugReport::NodeResolver::anchor() {} - -void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { +void PathSensitiveBugReport::addVisitor( + std::unique_ptr<BugReporterVisitor> visitor) { if (!visitor) return; @@ -2054,20 +2121,11 @@ void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { Callbacks.push_back(std::move(visitor)); } -void BugReport::clearVisitors() { +void PathSensitiveBugReport::clearVisitors() { Callbacks.clear(); } -BugReport::~BugReport() { - while (!interestingSymbols.empty()) { - popInterestingSymbolsAndRegions(); - } -} - -const Decl *BugReport::getDeclWithIssue() const { - if (DeclWithIssue) - return DeclWithIssue; - +const Decl *PathSensitiveBugReport::getDeclWithIssue() const { const ExplodedNode *N = getErrorNode(); if (!N) return nullptr; @@ -2076,17 +2134,34 @@ const Decl *BugReport::getDeclWithIssue() const { return LC->getStackFrame()->getDecl(); } -void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { +void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddInteger(static_cast<int>(getKind())); + hash.AddPointer(&BT); + hash.AddString(Description); + assert(Location.isValid()); + Location.Profile(hash); + + for (SourceRange range : Ranges) { + if (!range.isValid()) + continue; + hash.AddInteger(range.getBegin().getRawEncoding()); + hash.AddInteger(range.getEnd().getRawEncoding()); + } +} + +void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const { + hash.AddInteger(static_cast<int>(getKind())); hash.AddPointer(&BT); hash.AddString(Description); PathDiagnosticLocation UL = getUniqueingLocation(); if (UL.isValid()) { UL.Profile(hash); - } else if (Location.isValid()) { - Location.Profile(hash); } else { - assert(ErrorNode); - hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode)); + // TODO: The statement may be null if the report was emitted before any + // statements were executed. In particular, some checkers by design + // occasionally emit their reports in empty functions (that have no + // statements in their body). Do we profile correctly in this case? + hash.AddPointer(ErrorNode->getCurrentOrPreviousStmtForDiagnostics()); } for (SourceRange range : Ranges) { @@ -2097,96 +2172,141 @@ void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { } } -void BugReport::markInteresting(SymbolRef sym) { +template <class T> +static void insertToInterestingnessMap( + llvm::DenseMap<T, bugreporter::TrackingKind> &InterestingnessMap, T Val, + bugreporter::TrackingKind TKind) { + auto Result = InterestingnessMap.insert({Val, TKind}); + + if (Result.second) + return; + + // Even if this symbol/region was already marked as interesting as a + // condition, if we later mark it as interesting again but with + // thorough tracking, overwrite it. Entities marked with thorough + // interestiness are the most important (or most interesting, if you will), + // and we wouldn't like to downplay their importance. + + switch (TKind) { + case bugreporter::TrackingKind::Thorough: + Result.first->getSecond() = bugreporter::TrackingKind::Thorough; + return; + case bugreporter::TrackingKind::Condition: + return; + } + + llvm_unreachable( + "BugReport::markInteresting currently can only handle 2 different " + "tracking kinds! Please define what tracking kind should this entitiy" + "have, if it was already marked as interesting with a different kind!"); +} + +void PathSensitiveBugReport::markInteresting(SymbolRef sym, + bugreporter::TrackingKind TKind) { if (!sym) return; - getInterestingSymbols().insert(sym); + insertToInterestingnessMap(InterestingSymbols, sym, TKind); if (const auto *meta = dyn_cast<SymbolMetadata>(sym)) - getInterestingRegions().insert(meta->getRegion()); + markInteresting(meta->getRegion(), TKind); } -void BugReport::markInteresting(const MemRegion *R) { +void PathSensitiveBugReport::markInteresting(const MemRegion *R, + bugreporter::TrackingKind TKind) { if (!R) return; R = R->getBaseRegion(); - getInterestingRegions().insert(R); + insertToInterestingnessMap(InterestingRegions, R, TKind); if (const auto *SR = dyn_cast<SymbolicRegion>(R)) - getInterestingSymbols().insert(SR->getSymbol()); + markInteresting(SR->getSymbol(), TKind); } -void BugReport::markInteresting(SVal V) { - markInteresting(V.getAsRegion()); - markInteresting(V.getAsSymbol()); +void PathSensitiveBugReport::markInteresting(SVal V, + bugreporter::TrackingKind TKind) { + markInteresting(V.getAsRegion(), TKind); + markInteresting(V.getAsSymbol(), TKind); } -void BugReport::markInteresting(const LocationContext *LC) { +void PathSensitiveBugReport::markInteresting(const LocationContext *LC) { if (!LC) return; InterestingLocationContexts.insert(LC); } -bool BugReport::isInteresting(SVal V) { - return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); +Optional<bugreporter::TrackingKind> +PathSensitiveBugReport::getInterestingnessKind(SVal V) const { + auto RKind = getInterestingnessKind(V.getAsRegion()); + auto SKind = getInterestingnessKind(V.getAsSymbol()); + if (!RKind) + return SKind; + if (!SKind) + return RKind; + + // If either is marked with throrough tracking, return that, we wouldn't like + // to downplay a note's importance by 'only' mentioning it as a condition. + switch(*RKind) { + case bugreporter::TrackingKind::Thorough: + return RKind; + case bugreporter::TrackingKind::Condition: + return SKind; + } + + llvm_unreachable( + "BugReport::getInterestingnessKind currently can only handle 2 different " + "tracking kinds! Please define what tracking kind should we return here " + "when the kind of getAsRegion() and getAsSymbol() is different!"); + return None; } -bool BugReport::isInteresting(SymbolRef sym) { +Optional<bugreporter::TrackingKind> +PathSensitiveBugReport::getInterestingnessKind(SymbolRef sym) const { if (!sym) - return false; + return None; // We don't currently consider metadata symbols to be interesting // even if we know their region is interesting. Is that correct behavior? - return getInterestingSymbols().count(sym); + auto It = InterestingSymbols.find(sym); + if (It == InterestingSymbols.end()) + return None; + return It->getSecond(); } -bool BugReport::isInteresting(const MemRegion *R) { +Optional<bugreporter::TrackingKind> +PathSensitiveBugReport::getInterestingnessKind(const MemRegion *R) const { if (!R) - return false; - R = R->getBaseRegion(); - bool b = getInterestingRegions().count(R); - if (b) - return true; - if (const auto *SR = dyn_cast<SymbolicRegion>(R)) - return getInterestingSymbols().count(SR->getSymbol()); - return false; -} + return None; -bool BugReport::isInteresting(const LocationContext *LC) { - if (!LC) - return false; - return InterestingLocationContexts.count(LC); -} + R = R->getBaseRegion(); + auto It = InterestingRegions.find(R); + if (It != InterestingRegions.end()) + return It->getSecond(); -void BugReport::lazyInitializeInterestingSets() { - if (interestingSymbols.empty()) { - interestingSymbols.push_back(new Symbols()); - interestingRegions.push_back(new Regions()); - } + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) + return getInterestingnessKind(SR->getSymbol()); + return None; } -BugReport::Symbols &BugReport::getInterestingSymbols() { - lazyInitializeInterestingSets(); - return *interestingSymbols.back(); +bool PathSensitiveBugReport::isInteresting(SVal V) const { + return getInterestingnessKind(V).hasValue(); } -BugReport::Regions &BugReport::getInterestingRegions() { - lazyInitializeInterestingSets(); - return *interestingRegions.back(); +bool PathSensitiveBugReport::isInteresting(SymbolRef sym) const { + return getInterestingnessKind(sym).hasValue(); } -void BugReport::pushInterestingSymbolsAndRegions() { - interestingSymbols.push_back(new Symbols(getInterestingSymbols())); - interestingRegions.push_back(new Regions(getInterestingRegions())); +bool PathSensitiveBugReport::isInteresting(const MemRegion *R) const { + return getInterestingnessKind(R).hasValue(); } -void BugReport::popInterestingSymbolsAndRegions() { - delete interestingSymbols.pop_back_val(); - delete interestingRegions.pop_back_val(); +bool PathSensitiveBugReport::isInteresting(const LocationContext *LC) const { + if (!LC) + return false; + return InterestingLocationContexts.count(LC); } -const Stmt *BugReport::getStmt() const { +const Stmt *PathSensitiveBugReport::getStmt() const { if (!ErrorNode) return nullptr; @@ -2196,59 +2316,83 @@ const Stmt *BugReport::getStmt() const { if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); if (BE->getBlock() == &Exit) - S = GetPreviousStmt(ErrorNode); + S = ErrorNode->getPreviousStmtForDiagnostics(); } if (!S) - S = PathDiagnosticLocation::getStmt(ErrorNode); + S = ErrorNode->getStmtForDiagnostics(); return S; } -llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() { +ArrayRef<SourceRange> +PathSensitiveBugReport::getRanges() const { // If no custom ranges, add the range of the statement corresponding to // the error node. - if (Ranges.empty()) { - if (const auto *E = dyn_cast_or_null<Expr>(getStmt())) - addRange(E->getSourceRange()); - else - return llvm::make_range(ranges_iterator(), ranges_iterator()); + if (Ranges.empty() && isa_and_nonnull<Expr>(getStmt())) + return ErrorNodeRange; + + return Ranges; +} + +PathDiagnosticLocation +PathSensitiveBugReport::getLocation() const { + assert(ErrorNode && "Cannot create a location with a null node."); + const Stmt *S = ErrorNode->getStmtForDiagnostics(); + ProgramPoint P = ErrorNode->getLocation(); + const LocationContext *LC = P.getLocationContext(); + SourceManager &SM = + ErrorNode->getState()->getStateManager().getContext().getSourceManager(); + + if (!S) { + // If this is an implicit call, return the implicit call point location. + if (Optional<PreImplicitCall> PIE = P.getAs<PreImplicitCall>()) + return PathDiagnosticLocation(PIE->getLocation(), SM); + if (auto FE = P.getAs<FunctionExitPoint>()) { + if (const ReturnStmt *RS = FE->getStmt()) + return PathDiagnosticLocation::createBegin(RS, SM, LC); + } + S = ErrorNode->getNextStmtForDiagnostics(); } - // User-specified absence of range info. - if (Ranges.size() == 1 && !Ranges.begin()->isValid()) - return llvm::make_range(ranges_iterator(), ranges_iterator()); + if (S) { + // For member expressions, return the location of the '.' or '->'. + if (const auto *ME = dyn_cast<MemberExpr>(S)) + return PathDiagnosticLocation::createMemberLoc(ME, SM); - return llvm::make_range(Ranges.begin(), Ranges.end()); -} + // For binary operators, return the location of the operator. + if (const auto *B = dyn_cast<BinaryOperator>(S)) + return PathDiagnosticLocation::createOperatorLoc(B, SM); + + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, LC); + + if (S->getBeginLoc().isValid()) + return PathDiagnosticLocation(S, SM, LC); -PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { - if (ErrorNode) { - assert(!Location.isValid() && - "Either Location or ErrorNode should be specified but not both."); - return PathDiagnosticLocation::createEndOfPath(ErrorNode, SM); + return PathDiagnosticLocation( + PathDiagnosticLocation::getValidSourceLocation(S, LC), SM); } - assert(Location.isValid()); - return Location; + return PathDiagnosticLocation::createDeclEnd(ErrorNode->getLocationContext(), + SM); } //===----------------------------------------------------------------------===// // Methods for BugReporter and subclasses. //===----------------------------------------------------------------------===// -BugReportEquivClass::~BugReportEquivClass() = default; - -GRBugReporter::~GRBugReporter() = default; - -BugReporterData::~BugReporterData() = default; - -ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } +const ExplodedGraph &PathSensitiveBugReporter::getGraph() const { + return Eng.getGraph(); +} -ProgramStateManager& -GRBugReporter::getStateManager() { return Eng.getStateManager(); } +ProgramStateManager &PathSensitiveBugReporter::getStateManager() const { + return Eng.getStateManager(); +} BugReporter::~BugReporter() { - FlushReports(); + // Make sure reports are flushed. + assert(StrBugTypes.empty() && + "Destroying BugReporter before diagnostics are emitted!"); // Free the bug reports we are tracking. for (const auto I : EQClassesVector) @@ -2256,9 +2400,6 @@ BugReporter::~BugReporter() { } void BugReporter::FlushReports() { - if (BugTypes.isEmpty()) - return; - // We need to flush reports in deterministic order to ensure the order // of the reports is consistent between runs. for (const auto EQ : EQClassesVector) @@ -2269,9 +2410,6 @@ void BugReporter::FlushReports() { // FIXME: There are leaks from checkers that assume that the BugTypes they // create will be destroyed by the BugReporter. llvm::DeleteContainerSeconds(StrBugTypes); - - // Remove all references to the BugType objects. - BugTypes = F.getEmptySet(); } //===----------------------------------------------------------------------===// @@ -2280,29 +2418,32 @@ void BugReporter::FlushReports() { namespace { -/// A wrapper around a report graph, which contains only a single path, and its -/// node maps. -class ReportGraph { +/// A wrapper around an ExplodedGraph that contains a single path from the root +/// to the error node. +class BugPathInfo { public: - InterExplodedGraphMap BackMap; - std::unique_ptr<ExplodedGraph> Graph; + std::unique_ptr<ExplodedGraph> BugPath; + PathSensitiveBugReport *Report; const ExplodedNode *ErrorNode; - size_t Index; }; -/// A wrapper around a trimmed graph and its node maps. -class TrimmedGraph { - InterExplodedGraphMap InverseMap; +/// A wrapper around an ExplodedGraph whose leafs are all error nodes. Can +/// conveniently retrieve bug paths from a single error node to the root. +class BugPathGetter { + std::unique_ptr<ExplodedGraph> TrimmedGraph; using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>; + /// Assign each node with its distance from the root. PriorityMapTy PriorityMap; - using NodeIndexPair = std::pair<const ExplodedNode *, size_t>; - - SmallVector<NodeIndexPair, 32> ReportNodes; + /// Since the getErrorNode() or BugReport refers to the original ExplodedGraph, + /// we need to pair it to the error node of the constructed trimmed graph. + using ReportNewNodePair = + std::pair<PathSensitiveBugReport *, const ExplodedNode *>; + SmallVector<ReportNewNodePair, 32> ReportNodes; - std::unique_ptr<ExplodedGraph> G; + BugPathInfo CurrentBugPath; /// A helper class for sorting ExplodedNodes by priority. template <bool Descending> @@ -2326,37 +2467,48 @@ class TrimmedGraph { : LI->second < RI->second; } - bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const { - return (*this)(LHS.first, RHS.first); + bool operator()(const ReportNewNodePair &LHS, + const ReportNewNodePair &RHS) const { + return (*this)(LHS.second, RHS.second); } }; public: - TrimmedGraph(const ExplodedGraph *OriginalGraph, - ArrayRef<const ExplodedNode *> Nodes); + BugPathGetter(const ExplodedGraph *OriginalGraph, + ArrayRef<PathSensitiveBugReport *> &bugReports); - bool popNextReportGraph(ReportGraph &GraphWrapper); + BugPathInfo *getNextBugPath(); }; } // namespace -TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, - ArrayRef<const ExplodedNode *> Nodes) { +BugPathGetter::BugPathGetter(const ExplodedGraph *OriginalGraph, + ArrayRef<PathSensitiveBugReport *> &bugReports) { + SmallVector<const ExplodedNode *, 32> Nodes; + for (const auto I : bugReports) { + assert(I->isValid() && + "We only allow BugReporterVisitors and BugReporter itself to " + "invalidate reports!"); + Nodes.emplace_back(I->getErrorNode()); + } + // The trimmed graph is created in the body of the constructor to ensure // that the DenseMaps have been initialized already. InterExplodedGraphMap ForwardMap; - G = OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap); + TrimmedGraph = OriginalGraph->trim(Nodes, &ForwardMap); // Find the (first) error node in the trimmed graph. We just need to consult // the node map which maps from nodes in the original graph to nodes // in the new graph. llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes; - for (unsigned i = 0, count = Nodes.size(); i < count; ++i) { - if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) { - ReportNodes.push_back(std::make_pair(NewNode, i)); - RemainingNodes.insert(NewNode); - } + for (PathSensitiveBugReport *Report : bugReports) { + const ExplodedNode *NewNode = ForwardMap.lookup(Report->getErrorNode()); + assert(NewNode && + "Failed to construct a trimmed graph that contains this error " + "node!"); + ReportNodes.emplace_back(Report, NewNode); + RemainingNodes.insert(NewNode); } assert(!RemainingNodes.empty() && "No error node found in the trimmed graph"); @@ -2364,8 +2516,8 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, // Perform a forward BFS to find all the shortest paths. std::queue<const ExplodedNode *> WS; - assert(G->num_roots() == 1); - WS.push(*G->roots_begin()); + assert(TrimmedGraph->num_roots() == 1); + WS.push(*TrimmedGraph->roots_begin()); unsigned Priority = 0; while (!WS.empty()) { @@ -2374,8 +2526,7 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, PriorityMapTy::iterator PriorityEntry; bool IsNew; - std::tie(PriorityEntry, IsNew) = - PriorityMap.insert(std::make_pair(Node, Priority)); + std::tie(PriorityEntry, IsNew) = PriorityMap.insert({Node, Priority}); ++Priority; if (!IsNew) { @@ -2387,29 +2538,26 @@ TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, if (RemainingNodes.empty()) break; - for (ExplodedNode::const_pred_iterator I = Node->succ_begin(), - E = Node->succ_end(); - I != E; ++I) - WS.push(*I); + for (const ExplodedNode *Succ : Node->succs()) + WS.push(Succ); } // Sort the error paths from longest to shortest. llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap)); } -bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { +BugPathInfo *BugPathGetter::getNextBugPath() { if (ReportNodes.empty()) - return false; + return nullptr; const ExplodedNode *OrigN; - std::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val(); + std::tie(CurrentBugPath.Report, OrigN) = ReportNodes.pop_back_val(); assert(PriorityMap.find(OrigN) != PriorityMap.end() && "error node not accessible from root"); - // Create a new graph with a single path. This is the graph - // that will be returned to the caller. - auto GNew = llvm::make_unique<ExplodedGraph>(); - GraphWrapper.BackMap.clear(); + // Create a new graph with a single path. This is the graph that will be + // returned to the caller. + auto GNew = std::make_unique<ExplodedGraph>(); // Now walk from the error node up the BFS path, always taking the // predeccessor with the lowest number. @@ -2417,19 +2565,15 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { while (true) { // Create the equivalent node in the new graph with the same state // and location. - ExplodedNode *NewN = GNew->createUncachedNode(OrigN->getLocation(), OrigN->getState(), - OrigN->isSink()); - - // Store the mapping to the original node. - InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN); - assert(IMitr != InverseMap.end() && "No mapping to original node."); - GraphWrapper.BackMap[NewN] = IMitr->second; + ExplodedNode *NewN = GNew->createUncachedNode( + OrigN->getLocation(), OrigN->getState(), + OrigN->getID(), OrigN->isSink()); // Link up the new node with the previous node. if (Succ) Succ->addPredecessor(NewN, *GNew); else - GraphWrapper.ErrorNode = NewN; + CurrentBugPath.ErrorNode = NewN; Succ = NewN; @@ -2442,23 +2586,22 @@ bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { // Find the next predeccessor node. We choose the node that is marked // with the lowest BFS number. OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(), - PriorityCompare<false>(PriorityMap)); + PriorityCompare<false>(PriorityMap)); } - GraphWrapper.Graph = std::move(GNew); + CurrentBugPath.BugPath = std::move(GNew); - return true; + return &CurrentBugPath; } /// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic /// object and collapses PathDiagosticPieces that are expanded by macros. static void CompactMacroExpandedPieces(PathPieces &path, const SourceManager& SM) { - using MacroStackTy = - std::vector< - std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; + using MacroStackTy = std::vector< + std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; - using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>; + using PiecesTy = std::vector<PathDiagnosticPieceRef>; MacroStackTy MacroStack; PiecesTy Pieces; @@ -2548,33 +2691,39 @@ static void CompactMacroExpandedPieces(PathPieces &path, /// Notes associated with {@code ErrorNode} are generated using /// {@code getEndPath}, and the rest are generated with {@code VisitNode}. static std::unique_ptr<VisitorsDiagnosticsTy> -generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode, +generateVisitorsDiagnostics(PathSensitiveBugReport *R, + const ExplodedNode *ErrorNode, BugReporterContext &BRC) { - auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>(); - BugReport::VisitorList visitors; + std::unique_ptr<VisitorsDiagnosticsTy> Notes = + std::make_unique<VisitorsDiagnosticsTy>(); + PathSensitiveBugReport::VisitorList visitors; // Run visitors on all nodes starting from the node *before* the last one. // The last node is reserved for notes generated with {@code getEndPath}. const ExplodedNode *NextNode = ErrorNode->getFirstPred(); while (NextNode) { - // At each iteration, move all visitors from report to visitor list. - for (BugReport::visitor_iterator I = R->visitor_begin(), - E = R->visitor_end(); - I != E; ++I) { - visitors.push_back(std::move(*I)); - } + // At each iteration, move all visitors from report to visitor list. This is + // important, because the Profile() functions of the visitors make sure that + // a visitor isn't added multiple times for the same node, but it's fine + // to add the a visitor with Profile() for different nodes (e.g. tracking + // a region at different points of the symbolic execution). + for (std::unique_ptr<BugReporterVisitor> &Visitor : R->visitors()) + visitors.push_back(std::move(Visitor)); + R->clearVisitors(); const ExplodedNode *Pred = NextNode->getFirstPred(); if (!Pred) { - std::shared_ptr<PathDiagnosticPiece> LastPiece; + PathDiagnosticPieceRef LastPiece; for (auto &V : visitors) { V->finalizeVisitor(BRC, ErrorNode, *R); if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) { assert(!LastPiece && "There can only be one final piece in a diagnostic."); + assert(Piece->getKind() == PathDiagnosticPiece::Kind::Event && + "The final piece must contain a message!"); LastPiece = std::move(Piece); (*Notes)[ErrorNode].push_back(LastPiece); } @@ -2597,127 +2746,81 @@ generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode, return Notes; } -/// Find a non-invalidated report for a given equivalence class, -/// and return together with a cache of visitors notes. -/// If none found, return a nullptr paired with an empty cache. -static -std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( - TrimmedGraph &TrimG, - ReportGraph &ErrorGraph, - ArrayRef<BugReport *> &bugReports, - AnalyzerOptions &Opts, - GRBugReporter &Reporter) { +Optional<PathDiagnosticBuilder> PathDiagnosticBuilder::findValidReport( + ArrayRef<PathSensitiveBugReport *> &bugReports, + PathSensitiveBugReporter &Reporter) { - while (TrimG.popNextReportGraph(ErrorGraph)) { + BugPathGetter BugGraph(&Reporter.getGraph(), bugReports); + + while (BugPathInfo *BugPath = BugGraph.getNextBugPath()) { // Find the BugReport with the original location. - assert(ErrorGraph.Index < bugReports.size()); - BugReport *R = bugReports[ErrorGraph.Index]; + PathSensitiveBugReport *R = BugPath->Report; assert(R && "No original report found for sliced graph."); assert(R->isValid() && "Report selected by trimmed graph marked invalid."); - const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + const ExplodedNode *ErrorNode = BugPath->ErrorNode; // Register refutation visitors first, if they mark the bug invalid no // further analysis is required - R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); + R->addVisitor(std::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); // Register additional node visitors. - R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); - R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); - R->addVisitor(llvm::make_unique<TagVisitor>()); + R->addVisitor(std::make_unique<NilReceiverBRVisitor>()); + R->addVisitor(std::make_unique<ConditionBRVisitor>()); + R->addVisitor(std::make_unique<TagVisitor>()); - BugReporterContext BRC(Reporter, ErrorGraph.BackMap); + BugReporterContext BRC(Reporter); // Run all visitors on a given graph, once. std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes = generateVisitorsDiagnostics(R, ErrorNode, BRC); if (R->isValid()) { - if (Opts.ShouldCrosscheckWithZ3) { + if (Reporter.getAnalyzerOptions().ShouldCrosscheckWithZ3) { // If crosscheck is enabled, remove all visitors, add the refutation // visitor and check again R->clearVisitors(); - R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>()); + R->addVisitor(std::make_unique<FalsePositiveRefutationBRVisitor>()); // We don't overrite the notes inserted by other visitors because the // refutation manager does not add any new note to the path - generateVisitorsDiagnostics(R, ErrorGraph.ErrorNode, BRC); + generateVisitorsDiagnostics(R, BugPath->ErrorNode, BRC); } // Check if the bug is still valid if (R->isValid()) - return std::make_pair(R, std::move(visitorNotes)); + return PathDiagnosticBuilder( + std::move(BRC), std::move(BugPath->BugPath), BugPath->Report, + BugPath->ErrorNode, std::move(visitorNotes)); } } - return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>()); + return {}; } std::unique_ptr<DiagnosticForConsumerMapTy> -GRBugReporter::generatePathDiagnostics( +PathSensitiveBugReporter::generatePathDiagnostics( ArrayRef<PathDiagnosticConsumer *> consumers, - ArrayRef<BugReport *> &bugReports) { + ArrayRef<PathSensitiveBugReport *> &bugReports) { assert(!bugReports.empty()); - auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); - bool HasValid = false; - SmallVector<const ExplodedNode *, 32> errorNodes; - for (const auto I : bugReports) { - if (I->isValid()) { - HasValid = true; - errorNodes.push_back(I->getErrorNode()); - } else { - // Keep the errorNodes list in sync with the bugReports list. - errorNodes.push_back(nullptr); - } - } - - // If all the reports have been marked invalid by a previous path generation, - // we're done. - if (!HasValid) - return Out; + auto Out = std::make_unique<DiagnosticForConsumerMapTy>(); - TrimmedGraph TrimG(&getGraph(), errorNodes); - ReportGraph ErrorGraph; - auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports, - getAnalyzerOptions(), *this); - BugReport *R = ReportInfo.first; + Optional<PathDiagnosticBuilder> PDB = + PathDiagnosticBuilder::findValidReport(bugReports, *this); - if (R && R->isValid()) { - const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + if (PDB) { for (PathDiagnosticConsumer *PC : consumers) { - PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC); - std::unique_ptr<PathDiagnostic> PD = generatePathDiagnosticForConsumer( - PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second); - (*Out)[PC] = std::move(PD); + if (std::unique_ptr<PathDiagnostic> PD = PDB->generate(PC)) { + (*Out)[PC] = std::move(PD); + } } } return Out; } -void BugReporter::Register(const BugType *BT) { - BugTypes = F.add(BugTypes, BT); -} - void BugReporter::emitReport(std::unique_ptr<BugReport> 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 - // file. The locations from handcrafted ASTs have no valid source locations - // and have to be discarded. Locations from model files should be preserved - // for processing and reporting. - if (DeclCtx->isBodyAutosynthesized() && - !DeclCtx->isBodyAutosynthesizedFromModelFile()) - return; - } - - bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid(); + bool ValidSourceLoc = R->getLocation().isValid(); assert(ValidSourceLoc); // If we mess up in a release build, we'd still prefer to just drop the bug // instead of trying to go on. @@ -2729,8 +2832,6 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { R->Profile(ID); // Lookup the equivance class. If there isn't one, create it. - const BugType& BT = R->getBugType(); - Register(&BT); void *InsertPos; BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); @@ -2742,6 +2843,28 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) { EQ->AddReport(std::move(R)); } +void PathSensitiveBugReporter::emitReport(std::unique_ptr<BugReport> R) { + if (auto PR = dyn_cast<PathSensitiveBugReport>(R.get())) + if (const ExplodedNode *E = PR->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 + // file. The locations from handcrafted ASTs have no valid source + // locations and have to be discarded. Locations from model files should + // be preserved for processing and reporting. + if (DeclCtx->isBodyAutosynthesized() && + !DeclCtx->isBodyAutosynthesizedFromModelFile()) + return; + } + + BugReporter::emitReport(std::move(R)); +} + //===----------------------------------------------------------------------===// // Emitting reports in equivalence classes. //===----------------------------------------------------------------------===// @@ -2758,107 +2881,19 @@ struct FRIEC_WLItem { } // namespace -static const CFGBlock *findBlockForNode(const ExplodedNode *N) { - ProgramPoint P = N->getLocation(); - if (auto BEP = P.getAs<BlockEntrance>()) - return BEP->getBlock(); - - // Find the node's current statement in the CFG. - if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) - return N->getLocationContext()->getAnalysisDeclContext() - ->getCFGStmtMap()->getBlock(S); - - return nullptr; -} - -// Returns true if by simply looking at the block, we can be sure that it -// results in a sink during analysis. This is useful to know when the analysis -// was interrupted, and we try to figure out if it would sink eventually. -// There may be many more reasons why a sink would appear during analysis -// (eg. checkers may generate sinks arbitrarily), but here we only consider -// sinks that would be obvious by looking at the CFG. -static bool isImmediateSinkBlock(const CFGBlock *Blk) { - if (Blk->hasNoReturnElement()) - return true; - - // FIXME: Throw-expressions are currently generating sinks during analysis: - // they're not supported yet, and also often used for actually terminating - // the program. So we should treat them as sinks in this analysis as well, - // at least for now, but once we have better support for exceptions, - // we'd need to carefully handle the case when the throw is being - // immediately caught. - if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) { - if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>()) - if (isa<CXXThrowExpr>(StmtElm->getStmt())) - return true; - return false; - })) - return true; - - return false; -} - -// Returns true if by looking at the CFG surrounding the node's program -// point, we can be sure that any analysis starting from this point would -// eventually end with a sink. We scan the child CFG blocks in a depth-first -// manner and see if all paths eventually end up in an immediate sink block. -static bool isInevitablySinking(const ExplodedNode *N) { - const CFG &Cfg = N->getCFG(); - - const CFGBlock *StartBlk = findBlockForNode(N); - if (!StartBlk) - return false; - if (isImmediateSinkBlock(StartBlk)) - return true; - - llvm::SmallVector<const CFGBlock *, 32> DFSWorkList; - llvm::SmallPtrSet<const CFGBlock *, 32> Visited; - - DFSWorkList.push_back(StartBlk); - while (!DFSWorkList.empty()) { - const CFGBlock *Blk = DFSWorkList.back(); - DFSWorkList.pop_back(); - Visited.insert(Blk); - - // If at least one path reaches the CFG exit, it means that control is - // returned to the caller. For now, say that we are not sure what - // happens next. If necessary, this can be improved to analyze - // the parent StackFrameContext's call site in a similar manner. - if (Blk == &Cfg.getExit()) - return false; - - for (const auto &Succ : Blk->succs()) { - if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) { - if (!isImmediateSinkBlock(SuccBlk) && !Visited.count(SuccBlk)) { - // If the block has reachable child blocks that aren't no-return, - // add them to the worklist. - DFSWorkList.push_back(SuccBlk); - } - } - } - } - - // Nothing reached the exit. It can only mean one thing: there's no return. - return true; -} - -static BugReport * -FindReportInEquivalenceClass(BugReportEquivClass& EQ, - SmallVectorImpl<BugReport*> &bugReports) { - BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); - assert(I != E); - const BugType& BT = I->getBugType(); - +BugReport *PathSensitiveBugReporter::findReportInEquivalenceClass( + BugReportEquivClass &EQ, SmallVectorImpl<BugReport *> &bugReports) { // If we don't need to suppress any of the nodes because they are // post-dominated by a sink, simply add all the nodes in the equivalence class // to 'Nodes'. Any of the reports will serve as a "representative" report. + assert(EQ.getReports().size() > 0); + const BugType& BT = EQ.getReports()[0]->getBugType(); if (!BT.isSuppressOnSink()) { - BugReport *R = &*I; - for (auto &I : EQ) { - const ExplodedNode *N = I.getErrorNode(); - if (N) { - R = &I; - bugReports.push_back(R); + BugReport *R = EQ.getReports()[0].get(); + for (auto &J : EQ.getReports()) { + if (auto *PR = dyn_cast<PathSensitiveBugReport>(J.get())) { + R = PR; + bugReports.push_back(PR); } } return R; @@ -2872,20 +2907,21 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // stack for very long paths. BugReport *exampleReport = nullptr; - for (; I != E; ++I) { - const ExplodedNode *errorNode = I->getErrorNode(); - - if (!errorNode) + for (const auto &I: EQ.getReports()) { + auto *R = dyn_cast<PathSensitiveBugReport>(I.get()); + if (!R) continue; + + const ExplodedNode *errorNode = R->getErrorNode(); if (errorNode->isSink()) { llvm_unreachable( "BugType::isSuppressSink() should not be 'true' for sink end nodes"); } // No successors? By definition this nodes isn't post-dominated by a sink. if (errorNode->succ_empty()) { - bugReports.push_back(&*I); + bugReports.push_back(R); if (!exampleReport) - exampleReport = &*I; + exampleReport = R; continue; } @@ -2893,8 +2929,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, // to being post-dominated by a sink. This works better when the analysis // is incomplete and we have never reached the no-return function call(s) // that we'd inevitably bump into on this path. - if (isInevitablySinking(errorNode)) - continue; + if (const CFGBlock *ErrorB = errorNode->getCFGBlock()) + if (ErrorB->isInevitablySinking()) + continue; // At this point we know that 'N' is not a sink and it has at least one // successor. Use a DFS worklist to find a non-sink end-of-path node. @@ -2917,9 +2954,9 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, if (Succ->succ_empty()) { // If we found an end-of-path node that is not a sink. if (!Succ->isSink()) { - bugReports.push_back(&*I); + bugReports.push_back(R); if (!exampleReport) - exampleReport = &*I; + exampleReport = R; WL.clear(); break; } @@ -2950,7 +2987,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ, void BugReporter::FlushReport(BugReportEquivClass& EQ) { SmallVector<BugReport*, 10> bugReports; - BugReport *report = FindReportInEquivalenceClass(EQ, bugReports); + BugReport *report = findReportInEquivalenceClass(EQ, bugReports); if (!report) return; @@ -2965,8 +3002,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { // If the path is empty, generate a single step path with the location // of the issue. if (PD->path.empty()) { - PathDiagnosticLocation L = report->getLocation(getSourceManager()); - auto piece = llvm::make_unique<PathDiagnosticEventPiece>( + PathDiagnosticLocation L = report->getLocation(); + auto piece = std::make_unique<PathDiagnosticEventPiece>( L, report->getDescription()); for (SourceRange Range : report->getRanges()) piece->addRange(Range); @@ -2993,10 +3030,8 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { Pieces.push_front(*I); } - // Get the meta data. - const BugReport::ExtraTextList &Meta = report->getExtraText(); - for (const auto &i : Meta) - PD->addMeta(i); + for (const auto &I : report->getFixits()) + Pieces.back()->addFixit(I); updateExecutedLinesWithDiagnosticPieces(*PD); Consumer->HandlePathDiagnostic(std::move(PD)); @@ -3006,7 +3041,7 @@ void BugReporter::FlushReport(BugReportEquivClass& EQ) { /// Insert all lines participating in the function signature \p Signature /// into \p ExecutedLines. static void populateExecutedLinesWithFunctionSignature( - const Decl *Signature, SourceManager &SM, + const Decl *Signature, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines) { SourceRange SignatureSourceRange; const Stmt* Body = Signature->getBody(); @@ -3031,7 +3066,7 @@ static void populateExecutedLinesWithFunctionSignature( } static void populateExecutedLinesWithStmt( - const Stmt *S, SourceManager &SM, + const Stmt *S, const SourceManager &SM, FilesToLineNumsMap &ExecutedLines) { SourceLocation Loc = S->getSourceRange().getBegin(); if (!Loc.isValid()) @@ -3045,8 +3080,8 @@ static void populateExecutedLinesWithStmt( /// \return all executed lines including function signatures on the path /// starting from \p N. static std::unique_ptr<FilesToLineNumsMap> -findExecutedLines(SourceManager &SM, const ExplodedNode *N) { - auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>(); +findExecutedLines(const SourceManager &SM, const ExplodedNode *N) { + auto ExecutedLines = std::make_unique<FilesToLineNumsMap>(); while (N) { if (N->getFirstPred() == nullptr) { @@ -3057,7 +3092,7 @@ findExecutedLines(SourceManager &SM, const ExplodedNode *N) { // Inlined function: show signature. const Decl* D = CE->getCalleeContext()->getDecl(); populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines); - } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) { + } else if (const Stmt *S = N->getStmtForDiagnostics()) { populateExecutedLinesWithStmt(S, SM, *ExecutedLines); // Show extra context for some parent kinds. @@ -3082,16 +3117,89 @@ findExecutedLines(SourceManager &SM, const ExplodedNode *N) { std::unique_ptr<DiagnosticForConsumerMapTy> BugReporter::generateDiagnosticForConsumerMap( - BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers, + BugReport *exampleReport, ArrayRef<PathDiagnosticConsumer *> consumers, ArrayRef<BugReport *> bugReports) { + auto *basicReport = cast<BasicBugReport>(exampleReport); + auto Out = std::make_unique<DiagnosticForConsumerMapTy>(); + for (auto *Consumer : consumers) + (*Out)[Consumer] = generateDiagnosticForBasicReport(basicReport); + return Out; +} - if (!report->isPathSensitive()) { - auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); - for (auto *Consumer : consumers) - (*Out)[Consumer] = generateEmptyDiagnosticForReport(report, - getSourceManager()); - return Out; +static PathDiagnosticCallPiece * +getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, + const SourceManager &SMgr) { + SourceLocation CallLoc = CP->callEnter.asLocation(); + + // If the call is within a macro, don't do anything (for now). + if (CallLoc.isMacroID()) + return nullptr; + + assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) && + "The call piece should not be in a header file."); + + // Check if CP represents a path through a function outside of the main file. + if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr)) + return CP; + + const PathPieces &Path = CP->path; + if (Path.empty()) + return nullptr; + + // Check if the last piece in the callee path is a call to a function outside + // of the main file. + if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) + return getFirstStackedCallToHeaderFile(CPInner, SMgr); + + // Otherwise, the last piece is in the main file. + return nullptr; +} + +static void resetDiagnosticLocationToMainFile(PathDiagnostic &PD) { + if (PD.path.empty()) + return; + + PathDiagnosticPiece *LastP = PD.path.back().get(); + assert(LastP); + const SourceManager &SMgr = LastP->getLocation().getManager(); + + // We only need to check if the report ends inside headers, if the last piece + // is a call piece. + if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { + CP = getFirstStackedCallToHeaderFile(CP, SMgr); + if (CP) { + // Mark the piece. + CP->setAsLastInMainSourceFile(); + + // Update the path diagnostic message. + const auto *ND = dyn_cast<NamedDecl>(CP->getCallee()); + if (ND) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << " (within a call to '" << ND->getDeclName() << "')"; + PD.appendToDesc(os.str()); + } + + // Reset the report containing declaration and location. + PD.setDeclWithIssue(CP->getCaller()); + PD.setLocation(CP->getLocation()); + + return; + } } +} + + + +std::unique_ptr<DiagnosticForConsumerMapTy> +PathSensitiveBugReporter::generateDiagnosticForConsumerMap( + BugReport *exampleReport, ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports) { + std::vector<BasicBugReport *> BasicBugReports; + std::vector<PathSensitiveBugReport *> PathSensitiveBugReports; + if (isa<BasicBugReport>(exampleReport)) + return BugReporter::generateDiagnosticForConsumerMap(exampleReport, + consumers, bugReports); // Generate the full path sensitive diagnostic, using the generation scheme // specified by the PathDiagnosticConsumer. Note that we have to generate @@ -3099,8 +3207,13 @@ BugReporter::generateDiagnosticForConsumerMap( // the BugReporterVisitors may mark this bug as a false positive. assert(!bugReports.empty()); MaxBugClassSize.updateMax(bugReports.size()); - std::unique_ptr<DiagnosticForConsumerMapTy> Out = - generatePathDiagnostics(consumers, bugReports); + + // Avoid copying the whole array because there may be a lot of reports. + ArrayRef<PathSensitiveBugReport *> convertedArrayOfReports( + reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.begin()), + reinterpret_cast<PathSensitiveBugReport *const *>(&*bugReports.end())); + std::unique_ptr<DiagnosticForConsumerMapTy> Out = generatePathDiagnostics( + consumers, convertedArrayOfReports); if (Out->empty()) return Out; @@ -3109,40 +3222,43 @@ BugReporter::generateDiagnosticForConsumerMap( // Examine the report and see if the last piece is in a header. Reset the // report location to the last piece in the main source file. - AnalyzerOptions &Opts = getAnalyzerOptions(); + const AnalyzerOptions &Opts = getAnalyzerOptions(); for (auto const &P : *Out) if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll) - P.second->resetDiagnosticLocationToMainFile(); + resetDiagnosticLocationToMainFile(*P.second); return Out; } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, - const CheckerBase *Checker, - StringRef Name, StringRef Category, - StringRef Str, PathDiagnosticLocation Loc, - ArrayRef<SourceRange> Ranges) { - EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str, - Loc, Ranges); + const CheckerBase *Checker, StringRef Name, + StringRef Category, StringRef Str, + PathDiagnosticLocation Loc, + ArrayRef<SourceRange> Ranges, + ArrayRef<FixItHint> Fixits) { + EmitBasicReport(DeclWithIssue, Checker->getCheckerName(), Name, Category, Str, + Loc, Ranges, Fixits); } void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, - CheckName CheckName, + CheckerNameRef CheckName, StringRef name, StringRef category, StringRef str, PathDiagnosticLocation Loc, - ArrayRef<SourceRange> Ranges) { + ArrayRef<SourceRange> Ranges, + ArrayRef<FixItHint> Fixits) { // 'BT' is owned by BugReporter. BugType *BT = getBugTypeForName(CheckName, name, category); - auto R = llvm::make_unique<BugReport>(*BT, str, Loc); + auto R = std::make_unique<BasicBugReport>(*BT, str, Loc); R->setDeclWithIssue(DeclWithIssue); - for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); - I != E; ++I) - R->addRange(*I); + for (const auto &SR : Ranges) + R->addRange(SR); + for (const auto &FH : Fixits) + R->addFixItHint(FH); emitReport(std::move(R)); } -BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name, - StringRef category) { +BugType *BugReporter::getBugTypeForName(CheckerNameRef CheckName, + StringRef name, StringRef category) { SmallString<136> fullDesc; llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name << ":" << category; diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp index 250793c4baff0..7ba93b858baf5 100644 --- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -26,6 +26,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LLVM.h" @@ -34,7 +35,6 @@ #include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" @@ -180,21 +180,60 @@ static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal, RLCV->getStore() == RightNode->getState()->getStore(); } -static Optional<const llvm::APSInt *> -getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { +static Optional<SVal> getSValForVar(const Expr *CondVarExpr, + const ExplodedNode *N) { ProgramStateRef State = N->getState(); const LocationContext *LCtx = N->getLocationContext(); + assert(CondVarExpr); + CondVarExpr = CondVarExpr->IgnoreImpCasts(); + // The declaration of the value may rely on a pointer so take its l-value. - if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(CondVarExpr)) { - if (const auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) { - SVal DeclSVal = State->getSVal(State->getLValue(VD, LCtx)); - if (auto DeclCI = DeclSVal.getAs<nonloc::ConcreteInt>()) - return &DeclCI->getValue(); - } - } + // FIXME: As seen in VisitCommonDeclRefExpr, sometimes DeclRefExpr may + // evaluate to a FieldRegion when it refers to a declaration of a lambda + // capture variable. We most likely need to duplicate that logic here. + if (const auto *DRE = dyn_cast<DeclRefExpr>(CondVarExpr)) + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) + return State->getSVal(State->getLValue(VD, LCtx)); + + if (const auto *ME = dyn_cast<MemberExpr>(CondVarExpr)) + if (const auto *FD = dyn_cast<FieldDecl>(ME->getMemberDecl())) + if (auto FieldL = State->getSVal(ME, LCtx).getAs<Loc>()) + return State->getRawSVal(*FieldL, FD->getType()); + + return None; +} + +static Optional<const llvm::APSInt *> +getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) { - return {}; + if (Optional<SVal> V = getSValForVar(CondVarExpr, N)) + if (auto CI = V->getAs<nonloc::ConcreteInt>()) + return &CI->getValue(); + return None; +} + +static bool isVarAnInterestingCondition(const Expr *CondVarExpr, + const ExplodedNode *N, + const PathSensitiveBugReport *B) { + // Even if this condition is marked as interesting, it isn't *that* + // interesting if it didn't happen in a nested stackframe, the user could just + // follow the arrows. + if (!B->getErrorNode()->getStackFrame()->isParentOf(N->getStackFrame())) + return false; + + if (Optional<SVal> V = getSValForVar(CondVarExpr, N)) + if (Optional<bugreporter::TrackingKind> K = B->getInterestingnessKind(*V)) + return *K == bugreporter::TrackingKind::Condition; + + return false; +} + +static bool isInterestingExpr(const Expr *E, const ExplodedNode *N, + const PathSensitiveBugReport *B) { + if (Optional<SVal> V = getSValForVar(E, N)) + return B->getInterestingnessKind(*V).hasValue(); + return false; } /// \return name of the macro inside the location \p Loc. @@ -255,21 +294,21 @@ static bool wasRegionOfInterestModifiedAt(const SubRegion *RegionOfInterest, // Implementation of BugReporterVisitor. //===----------------------------------------------------------------------===// -std::shared_ptr<PathDiagnosticPiece> -BugReporterVisitor::getEndPath(BugReporterContext &, - const ExplodedNode *, BugReport &) { +PathDiagnosticPieceRef BugReporterVisitor::getEndPath(BugReporterContext &, + const ExplodedNode *, + PathSensitiveBugReport &) { return nullptr; } -void -BugReporterVisitor::finalizeVisitor(BugReporterContext &, - const ExplodedNode *, BugReport &) {} - -std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( - BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { - PathDiagnosticLocation L = - PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); +void BugReporterVisitor::finalizeVisitor(BugReporterContext &, + const ExplodedNode *, + PathSensitiveBugReport &) {} +PathDiagnosticPieceRef +BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC, + const ExplodedNode *EndPathNode, + const PathSensitiveBugReport &BR) { + PathDiagnosticLocation L = BR.getLocation(); const auto &Ranges = BR.getRanges(); // Only add the statement itself as a range if we didn't specify any @@ -297,6 +336,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { MemRegionManager &MmrMgr; const SourceManager &SM; const PrintingPolicy &PP; + bugreporter::TrackingKind TKind; /// Recursion limit for dereferencing fields when looking for the /// region of interest. @@ -317,10 +357,10 @@ class NoStoreFuncVisitor final : public BugReporterVisitor { using RegionVector = SmallVector<const MemRegion *, 5>; public: - NoStoreFuncVisitor(const SubRegion *R) + NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind) : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()), SM(MmrMgr.getContext().getSourceManager()), - PP(MmrMgr.getContext().getPrintingPolicy()) {} + PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {} void Profile(llvm::FoldingSetNodeID &ID) const override { static int Tag = 0; @@ -333,9 +373,9 @@ public: return static_cast<void *>(&Tag); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BR, - BugReport &R) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BR, + PathSensitiveBugReport &R) override; private: /// Attempts to find the region of interest in a given record decl, @@ -368,11 +408,11 @@ private: /// either emit a note or suppress the report enirely. /// \return Diagnostics piece for region not modified in the current function, /// if it decides to emit one. - std::shared_ptr<PathDiagnosticPiece> - maybeEmitNote(BugReport &R, const CallEvent &Call, const ExplodedNode *N, - const RegionVector &FieldChain, const MemRegion *MatchedRegion, - StringRef FirstElement, bool FirstIsReferenceType, - unsigned IndirectionLevel); + PathDiagnosticPieceRef + maybeEmitNote(PathSensitiveBugReport &R, const CallEvent &Call, + const ExplodedNode *N, const RegionVector &FieldChain, + const MemRegion *MatchedRegion, StringRef FirstElement, + bool FirstIsReferenceType, unsigned IndirectionLevel); /// Pretty-print region \p MatchedRegion to \p os. /// \return Whether printing succeeded. @@ -501,9 +541,9 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord( return None; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR, - BugReport &R) { + PathSensitiveBugReport &R) { const LocationContext *Ctx = N->getLocationContext(); const StackFrameContext *SCtx = Ctx->getStackFrame(); @@ -611,8 +651,11 @@ void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) { } while (N); } -std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote( - BugReport &R, const CallEvent &Call, const ExplodedNode *N, +static llvm::StringLiteral WillBeUsedForACondition = + ", which participates in a condition later"; + +PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote( + PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N, const RegionVector &FieldChain, const MemRegion *MatchedRegion, StringRef FirstElement, bool FirstIsReferenceType, unsigned IndirectionLevel) { @@ -657,6 +700,8 @@ std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote( return nullptr; os << "'"; + if (TKind == bugreporter::TrackingKind::Condition) + os << WillBeUsedForACondition; return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); } @@ -752,13 +797,12 @@ class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor { bool WasModified = false; public: - MacroNullReturnSuppressionVisitor(const SubRegion *R, - const SVal V) : RegionOfInterest(R), - ValueAtDereference(V) {} + MacroNullReturnSuppressionVisitor(const SubRegion *R, const SVal V) + : RegionOfInterest(R), ValueAtDereference(V) {} - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override { + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override { if (WasModified) return nullptr; @@ -784,12 +828,12 @@ public: static void addMacroVisitorIfNecessary( const ExplodedNode *N, const MemRegion *R, - bool EnableNullFPSuppression, BugReport &BR, + bool EnableNullFPSuppression, PathSensitiveBugReport &BR, const SVal V) { AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; if (EnableNullFPSuppression && Options.ShouldSuppressNullReturnPaths && V.getAs<Loc>()) - BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>( + BR.addVisitor(std::make_unique<MacroNullReturnSuppressionVisitor>( R->getAs<SubRegion>(), V)); } @@ -806,7 +850,7 @@ private: /// \return Source location of right hand side of an assignment /// into \c RegionOfInterest, empty optional if none found. Optional<SourceLocation> matchAssignment(const ExplodedNode *N) { - const Stmt *S = PathDiagnosticLocation::getStmt(N); + const Stmt *S = N->getStmtForDiagnostics(); ProgramStateRef State = N->getState(); auto *LCtx = N->getLocationContext(); if (!S) @@ -841,7 +885,7 @@ namespace { /// This visitor is intended to be used when another visitor discovers that an /// interesting value comes from an inlined function call. class ReturnVisitor : public BugReporterVisitor { - const StackFrameContext *StackFrame; + const StackFrameContext *CalleeSFC; enum { Initial, MaybeUnsuppress, @@ -851,13 +895,13 @@ class ReturnVisitor : public BugReporterVisitor { bool EnableNullFPSuppression; bool ShouldInvalidate = true; AnalyzerOptions& Options; + bugreporter::TrackingKind TKind; public: - ReturnVisitor(const StackFrameContext *Frame, - bool Suppressed, - AnalyzerOptions &Options) - : StackFrame(Frame), EnableNullFPSuppression(Suppressed), - Options(Options) {} + ReturnVisitor(const StackFrameContext *Frame, bool Suppressed, + AnalyzerOptions &Options, bugreporter::TrackingKind TKind) + : CalleeSFC(Frame), EnableNullFPSuppression(Suppressed), + Options(Options), TKind(TKind) {} static void *getTag() { static int Tag = 0; @@ -866,7 +910,7 @@ public: void Profile(llvm::FoldingSetNodeID &ID) const override { ID.AddPointer(ReturnVisitor::getTag()); - ID.AddPointer(StackFrame); + ID.AddPointer(CalleeSFC); ID.AddBoolean(EnableNullFPSuppression); } @@ -878,8 +922,9 @@ public: /// the statement is a call that was inlined, we add the visitor to the /// bug report, so it can print a note later. static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S, - BugReport &BR, - bool InEnableNullFPSuppression) { + PathSensitiveBugReport &BR, + bool InEnableNullFPSuppression, + bugreporter::TrackingKind TKind) { if (!CallEvent::isCallStmt(S)) return; @@ -950,17 +995,16 @@ public: if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); - BR.markInteresting(CalleeContext); - BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext, + BR.addVisitor(std::make_unique<ReturnVisitor>(CalleeContext, EnableNullFPSuppression, - Options)); + Options, TKind)); } - std::shared_ptr<PathDiagnosticPiece> - visitNodeInitial(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { + PathDiagnosticPieceRef visitNodeInitial(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { // Only print a message at the interesting return statement. - if (N->getLocationContext() != StackFrame) + if (N->getLocationContext() != CalleeSFC) return nullptr; Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); @@ -974,7 +1018,7 @@ public: // Okay, we're at the right return statement, but do we have the return // value available? ProgramStateRef State = N->getState(); - SVal V = State->getSVal(Ret, StackFrame); + SVal V = State->getSVal(Ret, CalleeSFC); if (V.isUnknownOrUndef()) return nullptr; @@ -1001,13 +1045,16 @@ public: RetE = RetE->IgnoreParenCasts(); - // If we're returning 0, we should track where that 0 came from. - bugreporter::trackExpressionValue(N, RetE, BR, EnableNullFPSuppression); + // Let's track the return value. + bugreporter::trackExpressionValue( + N, RetE, BR, TKind, EnableNullFPSuppression); // Build an appropriate message based on the return value. SmallString<64> Msg; llvm::raw_svector_ostream Out(Msg); + bool WouldEventBeMeaningless = false; + if (State->isNull(V).isConstrainedTrue()) { if (V.getAs<Loc>()) { @@ -1030,10 +1077,19 @@ public: } else { if (auto CI = V.getAs<nonloc::ConcreteInt>()) { Out << "Returning the value " << CI->getValue(); - } else if (V.getAs<Loc>()) { - Out << "Returning pointer"; } else { - Out << "Returning value"; + // There is nothing interesting about returning a value, when it is + // plain value without any constraints, and the function is guaranteed + // to return that every time. We could use CFG::isLinear() here, but + // constexpr branches are obvious to the compiler, not necesserily to + // the programmer. + if (N->getCFG().size() == 3) + WouldEventBeMeaningless = true; + + if (V.getAs<Loc>()) + Out << "Returning pointer"; + else + Out << "Returning value"; } } @@ -1052,26 +1108,36 @@ public: Out << " (loaded from '" << *DD << "')"; } - PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame); + PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSFC); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; - return std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); + if (TKind == bugreporter::TrackingKind::Condition) + Out << WillBeUsedForACondition; + + auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); + + // If we determined that the note is meaningless, make it prunable, and + // don't mark the stackframe interesting. + if (WouldEventBeMeaningless) + EventPiece->setPrunable(true); + else + BR.markInteresting(CalleeSFC); + + return EventPiece; } - std::shared_ptr<PathDiagnosticPiece> - visitNodeMaybeUnsuppress(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { -#ifndef NDEBUG + PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { assert(Options.ShouldAvoidSuppressingNullArgumentPaths); -#endif // Are we at the entry node for this call? Optional<CallEnter> CE = N->getLocationAs<CallEnter>(); if (!CE) return nullptr; - if (CE->getCalleeContext() != StackFrame) + if (CE->getCalleeContext() != CalleeSFC) return nullptr; Mode = Satisfied; @@ -1083,7 +1149,7 @@ public: CallEventManager &CallMgr = StateMgr.getCallEventManager(); ProgramStateRef State = N->getState(); - CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); + CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State); for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); if (!ArgV) @@ -1097,7 +1163,7 @@ public: if (!State->isNull(*ArgV).isConstrainedTrue()) continue; - if (bugreporter::trackExpressionValue(N, ArgE, BR, EnableNullFPSuppression)) + if (trackExpressionValue(N, ArgE, BR, TKind, EnableNullFPSuppression)) ShouldInvalidate = false; // If we /can't/ track the null pointer, we should err on the side of @@ -1108,9 +1174,9 @@ public: return nullptr; } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override { + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override { switch (Mode) { case Initial: return visitNodeInitial(N, BRC, BR); @@ -1124,9 +1190,9 @@ public: } void finalizeVisitor(BugReporterContext &, const ExplodedNode *, - BugReport &BR) override { + PathSensitiveBugReport &BR) override { if (EnableNullFPSuppression && ShouldInvalidate) - BR.markInvalid(ReturnVisitor::getTag(), StackFrame); + BR.markInvalid(ReturnVisitor::getTag(), CalleeSFC); } }; @@ -1141,6 +1207,7 @@ void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(&tag); ID.AddPointer(R); ID.Add(V); + ID.AddInteger(static_cast<int>(TKind)); ID.AddBoolean(EnableNullFPSuppression); } @@ -1246,9 +1313,8 @@ static void showBRParamDiagnostics(llvm::raw_svector_ostream& os, } /// Show default diagnostics for storing bad region. -static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os, - const MemRegion *R, - SVal V) { +static void showBRDefaultDiagnostics(llvm::raw_svector_ostream &os, + const MemRegion *R, SVal V) { if (V.getAs<loc::ConcreteInt>()) { bool b = false; if (R->isBoundable()) { @@ -1291,9 +1357,10 @@ static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os, } } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { if (Satisfied) return nullptr; @@ -1314,7 +1381,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, // should track the initializer expression. if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); - if (FieldReg && FieldReg == R) { + if (FieldReg == R) { StoreSite = Pred; InitE = PIP->getInitializer()->getInit(); } @@ -1351,14 +1418,19 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) { if (const auto *VR = dyn_cast<VarRegion>(R)) { - const auto *Param = cast<ParmVarDecl>(VR->getDecl()); + if (const auto *Param = dyn_cast<ParmVarDecl>(VR->getDecl())) { + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); - ProgramStateManager &StateMgr = BRC.getStateManager(); - CallEventManager &CallMgr = StateMgr.getCallEventManager(); - - CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), - Succ->getState()); - InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), + Succ->getState()); + InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + } else { + // Handle Objective-C 'self'. + assert(isa<ImplicitParamDecl>(VR->getDecl())); + InitE = cast<ObjCMessageExpr>(CE->getCalleeContext()->getCallSite()) + ->getInstanceReceiver()->IgnoreParenCasts(); + } IsParam = true; } } @@ -1371,22 +1443,23 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (!StoreSite) return nullptr; + Satisfied = true; // If we have an expression that provided the value, try to track where it // came from. if (InitE) { - if (V.isUndef() || - V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { - if (!IsParam) - InitE = InitE->IgnoreParenCasts(); - bugreporter::trackExpressionValue(StoreSite, InitE, BR, - EnableNullFPSuppression); - } - ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), - BR, EnableNullFPSuppression); + if (!IsParam) + InitE = InitE->IgnoreParenCasts(); + + bugreporter::trackExpressionValue( + StoreSite, InitE, BR, TKind, EnableNullFPSuppression); } + if (TKind == TrackingKind::Condition && + !OriginSFC->isParentOf(StoreSite->getStackFrame())) + return nullptr; + // Okay, we've found the binding. Emit an appropriate message. SmallString<256> sbuf; llvm::raw_svector_ostream os(sbuf); @@ -1411,8 +1484,8 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { if (auto KV = State->getSVal(OriginalR).getAs<KnownSVal>()) - BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, OriginalR, EnableNullFPSuppression)); + BR.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, OriginalR, EnableNullFPSuppression, TKind, OriginSFC)); } } } @@ -1428,6 +1501,9 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, if (os.str().empty()) showBRDefaultDiagnostics(os, R, V); + if (TKind == bugreporter::TrackingKind::Condition) + os << WillBeUsedForACondition; + // Construct a new PathDiagnosticPiece. ProgramPoint P = StoreSite->getLocation(); PathDiagnosticLocation L; @@ -1467,9 +1543,8 @@ bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const { return (bool)N->getState()->assume(Constraint, !Assumption); } -std::shared_ptr<PathDiagnosticPiece> -TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &) { +PathDiagnosticPieceRef TrackConstraintBRVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) { const ExplodedNode *PrevN = N->getFirstPred(); if (IsSatisfied) return nullptr; @@ -1547,10 +1622,10 @@ const char *SuppressInlineDefensiveChecksVisitor::getTag() { return "IDCVisitor"; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, BugReporterContext &BRC, - BugReport &BR) { + PathSensitiveBugReport &BR) { const ExplodedNode *Pred = Succ->getFirstPred(); if (IsSatisfied) return nullptr; @@ -1649,24 +1724,12 @@ public: ID.AddPointer(&x); } - std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, - BugReport &BR) override; + PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &BR) override; }; } // end of anonymous namespace -static CFGBlock *GetRelevantBlock(const ExplodedNode *Node) { - if (auto SP = Node->getLocationAs<StmtPoint>()) { - const Stmt *S = SP->getStmt(); - assert(S); - - return const_cast<CFGBlock *>(Node->getLocationContext() - ->getAnalysisDeclContext()->getCFGStmtMap()->getBlock(S)); - } - - return nullptr; -} - static std::shared_ptr<PathDiagnosticEventPiece> constructDebugPieceForTrackedCondition(const Expr *Cond, const ExplodedNode *N, @@ -1687,34 +1750,75 @@ constructDebugPieceForTrackedCondition(const Expr *Cond, (Twine() + "Tracking condition '" + ConditionText + "'").str()); } -std::shared_ptr<PathDiagnosticPiece> +static bool isAssertlikeBlock(const CFGBlock *B, ASTContext &Context) { + if (B->succ_size() != 2) + return false; + + const CFGBlock *Then = B->succ_begin()->getReachableBlock(); + const CFGBlock *Else = (B->succ_begin() + 1)->getReachableBlock(); + + if (!Then || !Else) + return false; + + if (Then->isInevitablySinking() != Else->isInevitablySinking()) + return true; + + // For the following condition the following CFG would be built: + // + // -------------> + // / \ + // [B1] -> [B2] -> [B3] -> [sink] + // assert(A && B || C); \ \ + // -----------> [go on with the execution] + // + // It so happens that CFGBlock::getTerminatorCondition returns 'A' for block + // B1, 'A && B' for B2, and 'A && B || C' for B3. Let's check whether we + // reached the end of the condition! + if (const Stmt *ElseCond = Else->getTerminatorCondition()) + if (const auto *BinOp = dyn_cast<BinaryOperator>(ElseCond)) + if (BinOp->isLogicalOp()) + return isAssertlikeBlock(Else, Context); + + return false; +} + +PathDiagnosticPieceRef TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - BugReport &BR) { + PathSensitiveBugReport &BR) { // We can only reason about control dependencies within the same stack frame. if (Origin->getStackFrame() != N->getStackFrame()) return nullptr; - CFGBlock *NB = GetRelevantBlock(N); + CFGBlock *NB = const_cast<CFGBlock *>(N->getCFGBlock()); // Skip if we already inspected this block. if (!VisitedBlocks.insert(NB).second) return nullptr; - CFGBlock *OriginB = GetRelevantBlock(Origin); + CFGBlock *OriginB = const_cast<CFGBlock *>(Origin->getCFGBlock()); // TODO: Cache CFGBlocks for each ExplodedNode. if (!OriginB || !NB) return nullptr; + if (isAssertlikeBlock(NB, BRC.getASTContext())) + return nullptr; + if (ControlDeps.isControlDependent(OriginB, NB)) { + // We don't really want to explain for range loops. Evidence suggests that + // the only thing that leads to is the addition of calls to operator!=. + if (llvm::isa_and_nonnull<CXXForRangeStmt>(NB->getTerminatorStmt())) + return nullptr; + if (const Expr *Condition = NB->getLastCondition()) { // Keeping track of the already tracked conditions on a visitor level // isn't sufficient, because a new visitor is created for each tracked // expression, hence the BugReport level set. if (BR.addTrackedCondition(N)) { bugreporter::trackExpressionValue( - N, Condition, BR, /*EnableNullFPSuppression=*/false); + N, Condition, BR, bugreporter::TrackingKind::Condition, + /*EnableNullFPSuppression=*/false); return constructDebugPieceForTrackedCondition(Condition, N, BRC); } } @@ -1819,7 +1923,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex, static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, const Expr *Inner) { while (N) { - if (PathDiagnosticLocation::getStmt(N) == Inner) + if (N->getStmtForDiagnostics() == Inner) return N; N = N->getFirstPred(); } @@ -1827,8 +1931,11 @@ static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, } bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, - const Expr *E, BugReport &report, + const Expr *E, + PathSensitiveBugReport &report, + bugreporter::TrackingKind TKind, bool EnableNullFPSuppression) { + if (!E || !InputNode) return false; @@ -1838,6 +1945,7 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, return false; ProgramStateRef LVState = LVNode->getState(); + const StackFrameContext *SFC = LVNode->getStackFrame(); // We only track expressions if we believe that they are important. Chances // are good that control dependencies to the tracking point are also improtant @@ -1845,19 +1953,20 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, // TODO: Shouldn't we track control dependencies of every bug location, rather // than only tracked expressions? if (LVState->getAnalysisManager().getAnalyzerOptions().ShouldTrackConditions) - report.addVisitor(llvm::make_unique<TrackControlDependencyCondBRVisitor>( + report.addVisitor(std::make_unique<TrackControlDependencyCondBRVisitor>( InputNode)); // The message send could be nil due to the receiver being nil. // At this point in the path, the receiver should be live since we are at the // message send expr. If it is nil, start tracking it. if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) - trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression); + trackExpressionValue( + LVNode, Receiver, report, TKind, EnableNullFPSuppression); // Track the index if this is an array subscript. if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner)) trackExpressionValue( - LVNode, Arr->getIdx(), report, /*EnableNullFPSuppression*/ false); + LVNode, Arr->getIdx(), report, TKind, /*EnableNullFPSuppression*/false); // See if the expression we're interested refers to a variable. // If so, we can track both its contents and constraints on its value. @@ -1872,8 +1981,8 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, // got initialized. if (RR && !LVIsNull) if (auto KV = LVal.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, RR, EnableNullFPSuppression)); + report.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, RR, EnableNullFPSuppression, TKind, SFC)); // In case of C++ references, we want to differentiate between a null // reference and reference to null pointer. @@ -1887,17 +1996,18 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, // Mark both the variable region and its contents as interesting. SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); report.addVisitor( - llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R))); + std::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R), TKind)); MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary( LVNode, R, EnableNullFPSuppression, report, V); - report.markInteresting(V); - report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(R)); + report.markInteresting(V, TKind); + report.addVisitor(std::make_unique<UndefOrNullArgVisitor>(R)); - // If the contents are symbolic, find out when they became null. - if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) - report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + // If the contents are symbolic and null, find out when they became null. + if (V.getAsLocSymbol(/*IncludeBaseRegions=*/true)) + if (LVState->isNull(V).isConstrainedTrue()) + report.addVisitor(std::make_unique<TrackConstraintBRVisitor>( V.castAs<DefinedSVal>(), false)); // Add visitor, which will suppress inline defensive checks. @@ -1905,12 +2015,12 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() && EnableNullFPSuppression) report.addVisitor( - llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, + std::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, LVNode)); if (auto KV = V.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, R, EnableNullFPSuppression)); + report.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, R, EnableNullFPSuppression, TKind, SFC)); return true; } } @@ -1920,40 +2030,43 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext()); ReturnVisitor::addVisitorIfNecessary( - LVNode, Inner, report, EnableNullFPSuppression); + LVNode, Inner, report, EnableNullFPSuppression, TKind); // Is it a symbolic value? if (auto L = V.getAs<loc::MemRegionVal>()) { - report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion())); - // FIXME: this is a hack for fixing a later crash when attempting to // dereference a void* pointer. // We should not try to dereference pointers at all when we don't care // what is written inside the pointer. bool CanDereference = true; - if (const auto *SR = dyn_cast<SymbolicRegion>(L->getRegion())) + if (const auto *SR = L->getRegionAs<SymbolicRegion>()) { if (SR->getSymbol()->getType()->getPointeeType()->isVoidType()) CanDereference = false; + } else if (L->getRegionAs<AllocaRegion>()) + CanDereference = false; // At this point we are dealing with the region's LValue. // However, if the rvalue is a symbolic region, we should track it as well. // Try to use the correct type when looking up the value. SVal RVal; - if (ExplodedGraph::isInterestingLValueExpr(Inner)) { + if (ExplodedGraph::isInterestingLValueExpr(Inner)) RVal = LVState->getRawSVal(L.getValue(), Inner->getType()); - } else if (CanDereference) { + else if (CanDereference) RVal = LVState->getSVal(L->getRegion()); - } - if (CanDereference) + if (CanDereference) { + report.addVisitor( + std::make_unique<UndefOrNullArgVisitor>(L->getRegion())); + if (auto KV = RVal.getAs<KnownSVal>()) - report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - *KV, L->getRegion(), EnableNullFPSuppression)); + report.addVisitor(std::make_unique<FindLastStoreBRVisitor>( + *KV, L->getRegion(), EnableNullFPSuppression, TKind, SFC)); + } const MemRegion *RegionRVal = RVal.getAsRegion(); if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { - report.markInteresting(RegionRVal); - report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + report.markInteresting(RegionRVal, TKind); + report.addVisitor(std::make_unique<TrackConstraintBRVisitor>( loc::MemRegionVal(RegionRVal), /*assumption=*/false)); } } @@ -1978,9 +2091,9 @@ const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, return nullptr; } -std::shared_ptr<PathDiagnosticPiece> -NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { Optional<PreStmt> P = N->getLocationAs<PreStmt>(); if (!P) return nullptr; @@ -2006,8 +2119,9 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, // The receiver was nil, and hence the method was skipped. // Register a BugReporterVisitor to issue a message telling us how // the receiver was null. - bugreporter::trackExpressionValue(N, Receiver, BR, - /*EnableNullFPSuppression*/ false); + bugreporter::trackExpressionValue( + N, Receiver, BR, bugreporter::TrackingKind::Thorough, + /*EnableNullFPSuppression*/ false); // Issue a message saying that the method was skipped. PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), N->getLocationContext()); @@ -2015,57 +2129,16 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, } //===----------------------------------------------------------------------===// -// Implementation of FindLastStoreBRVisitor. -//===----------------------------------------------------------------------===// - -// Registers every VarDecl inside a Stmt with a last store visitor. -void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, - const Stmt *S, - bool EnableNullFPSuppression) { - const ExplodedNode *N = BR.getErrorNode(); - std::deque<const Stmt *> WorkList; - WorkList.push_back(S); - - while (!WorkList.empty()) { - const Stmt *Head = WorkList.front(); - WorkList.pop_front(); - - ProgramStateManager &StateMgr = N->getState()->getStateManager(); - - if (const auto *DR = dyn_cast<DeclRefExpr>(Head)) { - if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { - const VarRegion *R = - StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); - - // What did we load? - SVal V = N->getSVal(S); - - if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { - // Register a new visitor with the BugReport. - BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( - V.castAs<KnownSVal>(), R, EnableNullFPSuppression)); - } - } - } - - for (const Stmt *SubStmt : Head->children()) - WorkList.push_back(SubStmt); - } -} - -//===----------------------------------------------------------------------===// // Visitor that tries to report interesting diagnostics from conditions. //===----------------------------------------------------------------------===// /// Return the tag associated with this visitor. This tag will be used /// to make all PathDiagnosticPieces created by this visitor. -const char *ConditionBRVisitor::getTag() { - return "ConditionBRVisitor"; -} +const char *ConditionBRVisitor::getTag() { return "ConditionBRVisitor"; } -std::shared_ptr<PathDiagnosticPiece> -ConditionBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +ConditionBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { auto piece = VisitNodeImpl(N, BRC, BR); if (piece) { piece->setTag(getTag()); @@ -2075,9 +2148,10 @@ ConditionBRVisitor::VisitNode(const ExplodedNode *N, return piece; } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { + BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramPoint ProgPoint = N->getLocation(); const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags = ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); @@ -2113,9 +2187,10 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, return nullptr; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTerminator( const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk, - const CFGBlock *dstBlk, BugReport &R, BugReporterContext &BRC) { + const CFGBlock *dstBlk, PathSensitiveBugReport &R, + BugReporterContext &BRC) { const Expr *Cond = nullptr; // In the code below, Term is a CFG terminator and Cond is a branch condition @@ -2170,10 +2245,10 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( return VisitTrueTest(Cond, BRC, R, N, TookTrue); } -std::shared_ptr<PathDiagnosticPiece> +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N, - bool TookTrue) { + PathSensitiveBugReport &R, + const ExplodedNode *N, bool TookTrue) { ProgramStateRef CurrentState = N->getState(); ProgramStateRef PrevState = N->getFirstPred()->getState(); const LocationContext *LCtx = N->getLocationContext(); @@ -2243,7 +2318,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, const Expr *ParentEx, raw_ostream &Out, BugReporterContext &BRC, - BugReport &report, + PathSensitiveBugReport &report, const ExplodedNode *N, Optional<bool> &prunable, bool IsSameFieldName) { @@ -2258,7 +2333,7 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, SourceLocation BeginLoc = OriginalExpr->getBeginLoc(); SourceLocation EndLoc = OriginalExpr->getEndLoc(); if (BeginLoc.isMacroID() && EndLoc.isMacroID()) { - SourceManager &SM = BRC.getSourceManager(); + const SourceManager &SM = BRC.getSourceManager(); const LangOptions &LO = BRC.getASTContext().getLangOpts(); if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) && Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) { @@ -2326,21 +2401,22 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex, return false; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC, - BugReport &R, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + PathSensitiveBugReport &R, const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { bool shouldInvert = false; Optional<bool> shouldPrune; // Check if the field name of the MemberExprs is ambiguous. Example: // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'. bool IsSameFieldName = false; - if (const auto *LhsME = - dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts())) - if (const auto *RhsME = - dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts())) - IsSameFieldName = LhsME->getMemberDecl()->getName() == - RhsME->getMemberDecl()->getName(); + const auto *LhsME = dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts()); + const auto *RhsME = dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts()); + + if (LhsME && RhsME) + IsSameFieldName = + LhsME->getMemberDecl()->getName() == RhsME->getMemberDecl()->getName(); SmallString<128> LhsString, RhsString; { @@ -2410,25 +2486,44 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( Out << (shouldInvert ? LhsString : RhsString); const LocationContext *LCtx = N->getLocationContext(); - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + const SourceManager &SM = BRC.getSourceManager(); + + if (isVarAnInterestingCondition(BExpr->getLHS(), N, &R) || + isVarAnInterestingCondition(BExpr->getRHS(), N, &R)) + Out << WillBeUsedForACondition; // Convert 'field ...' to 'Field ...' if it is a MemberExpr. std::string Message = Out.str(); Message[0] = toupper(Message[0]); - // If we know the value create a pop-up note. - if (!IsAssuming) + // If we know the value create a pop-up note to the value part of 'BExpr'. + if (!IsAssuming) { + PathDiagnosticLocation Loc; + if (!shouldInvert) { + if (LhsME && LhsME->getMemberLoc().isValid()) + Loc = PathDiagnosticLocation(LhsME->getMemberLoc(), SM); + else + Loc = PathDiagnosticLocation(BExpr->getLHS(), SM, LCtx); + } else { + if (RhsME && RhsME->getMemberLoc().isValid()) + Loc = PathDiagnosticLocation(RhsME->getMemberLoc(), SM); + else + Loc = PathDiagnosticLocation(BExpr->getRHS(), SM, LCtx); + } + return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message); + } + PathDiagnosticLocation Loc(Cond, SM, LCtx); auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message); if (shouldPrune.hasValue()) event->setPrunable(shouldPrune.getValue()); return event; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( +PathDiagnosticPieceRef ConditionBRVisitor::VisitConditionVariable( StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N, bool TookTrue) { + PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue) { // FIXME: If there's already a constraint tracker for this variable, // we shouldn't emit anything here (c.f. the double note in // test/Analysis/inlining/path-notes.c) @@ -2441,24 +2536,22 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); + + if (isVarAnInterestingCondition(CondVarExpr, N, &report)) + Out << WillBeUsedForACondition; + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); - if (const auto *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { - if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { - const ProgramState *state = N->getState().get(); - if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { - if (report.isInteresting(R)) - event->setPrunable(false); - } - } - } + if (isInterestingExpr(CondVarExpr, N, &report)) + event->setPrunable(false); return event; } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { const auto *VD = dyn_cast<VarDecl>(DRE->getDecl()); if (!VD) return nullptr; @@ -2472,29 +2565,29 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( return nullptr; const LocationContext *LCtx = N->getLocationContext(); - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); - // If we know the value create a pop-up note. - if (!IsAssuming) + if (isVarAnInterestingCondition(DRE, N, &report)) + Out << WillBeUsedForACondition; + + // If we know the value create a pop-up note to the 'DRE'. + if (!IsAssuming) { + PathDiagnosticLocation Loc(DRE, BRC.getSourceManager(), LCtx); return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); + } + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); - const ProgramState *state = N->getState().get(); - if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { - if (report.isInteresting(R)) - event->setPrunable(false); - else { - SVal V = state->getSVal(R); - if (report.isInteresting(V)) - event->setPrunable(false); - } - } + + if (isInterestingExpr(DRE, N, &report)) + event->setPrunable(false); + return std::move(event); } -std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( +PathDiagnosticPieceRef ConditionBRVisitor::VisitTrueTest( const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC, - BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) { + PathSensitiveBugReport &report, const ExplodedNode *N, bool TookTrue, + bool IsAssuming) { SmallString<256> Buf; llvm::raw_svector_ostream Out(Buf); @@ -2505,15 +2598,28 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest( return nullptr; const LocationContext *LCtx = N->getLocationContext(); - PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + PathDiagnosticLocation Loc; + + // If we know the value create a pop-up note to the member of the MemberExpr. + if (!IsAssuming && ME->getMemberLoc().isValid()) + Loc = PathDiagnosticLocation(ME->getMemberLoc(), BRC.getSourceManager()); + else + Loc = PathDiagnosticLocation(Cond, BRC.getSourceManager(), LCtx); + if (!Loc.isValid() || !Loc.asLocation().isValid()) return nullptr; + if (isVarAnInterestingCondition(ME, N, &report)) + Out << WillBeUsedForACondition; + // If we know the value create a pop-up note. if (!IsAssuming) return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str()); - return std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + if (isInterestingExpr(ME, N, &report)) + event->setPrunable(false); + return event; } bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, @@ -2553,10 +2659,8 @@ bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out, return true; } -const char *const ConditionBRVisitor::GenericTrueMessage = - "Assuming the condition is true"; -const char *const ConditionBRVisitor::GenericFalseMessage = - "Assuming the condition is false"; +constexpr llvm::StringLiteral ConditionBRVisitor::GenericTrueMessage; +constexpr llvm::StringLiteral ConditionBRVisitor::GenericFalseMessage; bool ConditionBRVisitor::isPieceMessageGeneric( const PathDiagnosticPiece *Piece) { @@ -2569,10 +2673,11 @@ bool ConditionBRVisitor::isPieceMessageGeneric( //===----------------------------------------------------------------------===// void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( - BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { + BugReporterContext &BRC, const ExplodedNode *N, + PathSensitiveBugReport &BR) { // Here we suppress false positives coming from system headers. This list is // based on known issues. - AnalyzerOptions &Options = BRC.getAnalyzerOptions(); + const AnalyzerOptions &Options = BRC.getAnalyzerOptions(); const Decl *D = N->getLocationContext()->getDecl(); if (AnalysisDeclContext::isInStdNamespace(D)) { @@ -2639,8 +2744,8 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( // Skip reports within the sys/queue.h macros as we do not have the ability to // reason about data structure shapes. - SourceManager &SM = BRC.getSourceManager(); - FullSourceLoc Loc = BR.getLocation(SM).asLocation(); + const SourceManager &SM = BRC.getSourceManager(); + FullSourceLoc Loc = BR.getLocation().asLocation(); while (Loc.isMacroID()) { Loc = Loc.getSpellingLoc(); if (SM.getFilename(Loc).endswith("sys/queue.h")) { @@ -2654,9 +2759,9 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( // Implementation of UndefOrNullArgVisitor. //===----------------------------------------------------------------------===// -std::shared_ptr<PathDiagnosticPiece> -UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &BRC, BugReport &BR) { +PathDiagnosticPieceRef +UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, + PathSensitiveBugReport &BR) { ProgramStateRef State = N->getState(); ProgramPoint ProgLoc = N->getLocation(); @@ -2712,7 +2817,8 @@ FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor() : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {} void FalsePositiveRefutationBRVisitor::finalizeVisitor( - BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { + BugReporterContext &BRC, const ExplodedNode *EndPathNode, + PathSensitiveBugReport &BR) { // Collect new constraints VisitNode(EndPathNode, BRC, BR); @@ -2747,10 +2853,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor( BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext()); } -std::shared_ptr<PathDiagnosticPiece> -FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N, - BugReporterContext &, - BugReport &) { +PathDiagnosticPieceRef FalsePositiveRefutationBRVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &, PathSensitiveBugReport &) { // Collect new constraints const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>(); ConstraintRangeTy::Factory &CF = @@ -2784,9 +2888,9 @@ void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(&Tag); } -std::shared_ptr<PathDiagnosticPiece> -TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, - BugReport &R) { +PathDiagnosticPieceRef TagVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + PathSensitiveBugReport &R) { ProgramPoint PP = N->getLocation(); const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag()); if (!T) diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp index a5f7500e63074..5f04a59ba0551 100644 --- a/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -27,6 +27,7 @@ #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/CrossTU/CrossTranslationUnit.h" #include "clang/Basic/IdentifierTable.h" @@ -34,10 +35,9 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Specifiers.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" @@ -191,7 +191,8 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const { return ADC; } -const StackFrameContext *CallEvent::getCalleeStackFrame() const { +const StackFrameContext * +CallEvent::getCalleeStackFrame(unsigned BlockCount) const { AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext(); if (!ADC) return nullptr; @@ -217,11 +218,12 @@ const StackFrameContext *CallEvent::getCalleeStackFrame() const { break; assert(Idx < Sz); - return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx); + return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx); } -const VarRegion *CallEvent::getParameterLocation(unsigned Index) const { - const StackFrameContext *SFC = getCalleeStackFrame(); +const VarRegion *CallEvent::getParameterLocation(unsigned Index, + unsigned BlockCount) const { + const StackFrameContext *SFC = getCalleeStackFrame(BlockCount); // We cannot construct a VarRegion without a stack frame. if (!SFC) return nullptr; @@ -322,7 +324,7 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, if (getKind() != CE_CXXAllocator) if (isArgumentConstructedDirectly(Idx)) if (auto AdjIdx = getAdjustedParameterIndex(Idx)) - if (const VarRegion *VR = getParameterLocation(*AdjIdx)) + if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount)) ValuesToInvalidate.push_back(loc::MemRegionVal(VR)); } @@ -366,7 +368,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const { if (CD.Flags & CDF_MaybeBuiltin) { return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) && - (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()); + (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams <= parameters().size()); } if (!CD.IsLookupDone) { @@ -405,7 +408,8 @@ bool CallEvent::isCalled(const CallDescription &CD) const { return false; } - return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()); + return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) && + (!CD.RequiredParams || CD.RequiredParams == parameters().size()); } SVal CallEvent::getArgSVal(unsigned Index) const { @@ -1033,7 +1037,7 @@ getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) { ObjCMessageKind ObjCMethodCall::getMessageKind() const { if (!Data) { // Find the parent, ignoring implicit casts. - ParentMap &PM = getLocationContext()->getParentMap(); + const ParentMap &PM = getLocationContext()->getParentMap(); const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr()); // Check if parent is a PseudoObjectExpr. diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp index f4e6f909d7644..bc1c8964b3ee4 100644 --- a/lib/StaticAnalyzer/Core/Checker.cpp +++ b/lib/StaticAnalyzer/Core/Checker.cpp @@ -19,10 +19,10 @@ using namespace ento; int ImplicitNullDerefEvent::Tag; StringRef CheckerBase::getTagDescription() const { - return getCheckName().getName(); + return getCheckerName().getName(); } -CheckName CheckerBase::getCheckName() const { return Name; } +CheckerNameRef CheckerBase::getCheckerName() const { return Name; } CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, StringRef Msg) @@ -30,10 +30,10 @@ CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, CheckerProgramPointTag::CheckerProgramPointTag(const CheckerBase *Checker, StringRef Msg) - : SimpleProgramPointTag(Checker->getCheckName().getName(), Msg) {} + : SimpleProgramPointTag(Checker->getCheckerName().getName(), Msg) {} raw_ostream& clang::ento::operator<<(raw_ostream &Out, const CheckerBase &Checker) { - Out << Checker.getCheckName().getName(); + Out << Checker.getCheckerName().getName(); return Out; } diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp index 34cdc9db699db..11693132de687 100644 --- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -91,7 +91,7 @@ parseAssignment(const Stmt *S) { } else if (auto PD = dyn_cast_or_null<DeclStmt>(S)) { // Initialization assert(PD->isSingleDecl() && "We process decls one by one"); - VD = dyn_cast_or_null<VarDecl>(PD->getSingleDecl()); + VD = cast<VarDecl>(PD->getSingleDecl()); RHS = VD->getAnyInitializer(); } diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp index 27d5797b4cbc9..f676bd8952836 100644 --- a/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -748,7 +748,7 @@ void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, continue; Indent(Out, Space, IsDot) - << "{ \"checker\": \"" << CT.second->getCheckName().getName() + << "{ \"checker\": \"" << CT.second->getCheckerName().getName() << "\", \"messages\": [" << NL; Indent(Out, InnerSpace, IsDot) << '\"' << TempBuf.str().trim() << '\"' << NL; diff --git a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp index 54501314386a9..bdae3e605efff 100644 --- a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp +++ b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp @@ -17,4 +17,5 @@ const char * const MemoryRefCount = "Memory (Core Foundation/Objective-C/OSObject)"; const char * const MemoryError = "Memory error"; const char * const UnixAPI = "Unix API"; +const char * const CXXObjectLifecycle = "C++ object lifecycle"; }}} diff --git a/lib/StaticAnalyzer/Core/DynamicType.cpp b/lib/StaticAnalyzer/Core/DynamicType.cpp new file mode 100644 index 0000000000000..a78e0e05e903b --- /dev/null +++ b/lib/StaticAnalyzer/Core/DynamicType.cpp @@ -0,0 +1,229 @@ +//===- DynamicType.cpp - Dynamic type related APIs --------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs that track and query dynamic type information. This +// information can be used to devirtualize calls during the symbolic execution +// or do type checking. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" +#include "clang/Basic/JsonSupport.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +/// The GDM component containing the dynamic type info. This is a map from a +/// symbol to its most likely type. +REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *, + clang::ento::DynamicTypeInfo) + +/// A set factory of dynamic cast informations. +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo) + +/// A map from symbols to cast informations. +REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *, + CastSet) + +namespace clang { +namespace ento { + +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) { + MR = MR->StripCasts(); + + // Look up the dynamic type in the GDM. + if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR)) + return *DTI; + + // Otherwise, fall back to what we know about the region. + if (const auto *TR = dyn_cast<TypedRegion>(MR)) + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); + + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) { + SymbolRef Sym = SR->getSymbol(); + return DynamicTypeInfo(Sym->getType()); + } + + return {}; +} + +const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State, + const MemRegion *MR) { + return State->get<DynamicTypeMap>(MR); +} + +const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy) { + const auto *Lookup = State->get<DynamicCastMap>().lookup(MR); + if (!Lookup) + return nullptr; + + for (const DynamicCastInfo &Cast : *Lookup) + if (Cast.equals(CastFromTy, CastToTy)) + return &Cast; + + return nullptr; +} + +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + DynamicTypeInfo NewTy) { + State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy); + assert(State); + return State; +} + +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR, + QualType NewTy, bool CanBeSubClassed) { + return setDynamicTypeInfo(State, MR, DynamicTypeInfo(NewTy, CanBeSubClassed)); +} + +ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, + const MemRegion *MR, + QualType CastFromTy, + QualType CastToTy, + bool CastSucceeds) { + if (!MR) + return State; + + if (CastSucceeds) { + assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) && + "DynamicTypeInfo should always be a pointer."); + State = State->set<DynamicTypeMap>(MR, CastToTy); + } + + DynamicCastInfo::CastResult ResultKind = + CastSucceeds ? DynamicCastInfo::CastResult::Success + : DynamicCastInfo::CastResult::Failure; + + CastSet::Factory &F = State->get_context<CastSet>(); + + const CastSet *TempSet = State->get<DynamicCastMap>(MR); + CastSet Set = TempSet ? *TempSet : F.getEmptySet(); + + Set = F.add(Set, {CastFromTy, CastToTy, ResultKind}); + State = State->set<DynamicCastMap>(MR, Set); + + assert(State); + return State; +} + +template <typename MapTy> +ProgramStateRef removeDead(ProgramStateRef State, const MapTy &Map, + SymbolReaper &SR) { + for (const auto &Elem : Map) + if (!SR.isLiveRegion(Elem.first)) + State = State->remove<DynamicCastMap>(Elem.first); + + return State; +} + +ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR) { + return removeDead(State, State->get<DynamicTypeMap>(), SR); +} + +ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) { + return removeDead(State, State->get<DynamicCastMap>(), SR); +} + +static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, + bool IsDot) { + Indent(Out, Space, IsDot) << "\"dynamic_types\": "; + + const DynamicTypeMapTy &Map = State->get<DynamicTypeMap>(); + if (Map.isEmpty()) { + Out << "null," << NL; + return; + } + + ++Space; + Out << '[' << NL; + for (DynamicTypeMapTy::iterator I = Map.begin(); I != Map.end(); ++I) { + const MemRegion *MR = I->first; + const DynamicTypeInfo &DTI = I->second; + Indent(Out, Space, IsDot) + << "{ \"region\": \"" << MR << "\", \"dyn_type\": "; + if (!DTI.isValid()) { + Out << "null"; + } else { + Out << '\"' << DTI.getType()->getPointeeType().getAsString() + << "\", \"sub_classable\": " + << (DTI.canBeASubClass() ? "true" : "false"); + } + Out << " }"; + + if (std::next(I) != Map.end()) + Out << ','; + Out << NL; + } + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; +} + +static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, + bool IsDot) { + Indent(Out, Space, IsDot) << "\"dynamic_casts\": "; + + const DynamicCastMapTy &Map = State->get<DynamicCastMap>(); + if (Map.isEmpty()) { + Out << "null," << NL; + return; + } + + ++Space; + Out << '[' << NL; + for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) { + const MemRegion *MR = I->first; + const CastSet &Set = I->second; + + Indent(Out, Space, IsDot) << "{ \"region\": \"" << MR << "\", \"casts\": "; + if (Set.isEmpty()) { + Out << "null "; + } else { + ++Space; + Out << '[' << NL; + for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) { + Indent(Out, Space, IsDot) + << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \"" + << SI->to().getAsString() << "\", \"kind\": \"" + << (SI->succeeds() ? "success" : "fail") << "\" }"; + + if (std::next(SI) != Set.end()) + Out << ','; + Out << NL; + } + --Space; + Indent(Out, Space, IsDot) << ']'; + } + Out << '}'; + + if (std::next(I) != Map.end()) + Out << ','; + Out << NL; + } + + --Space; + Indent(Out, Space, IsDot) << "]," << NL; +} + +void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, + const char *NL, unsigned int Space, bool IsDot) { + printDynamicTypesJson(Out, State, NL, Space, IsDot); + printDynamicCastsJson(Out, State, NL, Space, IsDot); +} + +} // namespace ento +} // namespace clang diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp deleted file mode 100644 index 79424452240d7..0000000000000 --- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp +++ /dev/null @@ -1,97 +0,0 @@ -//===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines APIs that track and query dynamic type information. This -// information can be used to devirtualize calls during the symbolic execution -// or do type checking. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" -#include "clang/Basic/JsonSupport.h" -#include "clang/Basic/LLVM.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/raw_ostream.h" -#include <cassert> - -namespace clang { -namespace ento { - -DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, - const MemRegion *Reg) { - Reg = Reg->StripCasts(); - - // Look up the dynamic type in the GDM. - const DynamicTypeInfo *GDMType = State->get<DynamicTypeMap>(Reg); - if (GDMType) - return *GDMType; - - // Otherwise, fall back to what we know about the region. - if (const auto *TR = dyn_cast<TypedRegion>(Reg)) - return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false); - - if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) { - SymbolRef Sym = SR->getSymbol(); - return DynamicTypeInfo(Sym->getType()); - } - - return {}; -} - -ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, - DynamicTypeInfo NewTy) { - Reg = Reg->StripCasts(); - ProgramStateRef NewState = State->set<DynamicTypeMap>(Reg, NewTy); - assert(NewState); - return NewState; -} - -void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State, - const char *NL, unsigned int Space, bool IsDot) { - Indent(Out, Space, IsDot) << "\"dynamic_types\": "; - - const DynamicTypeMapTy &DTM = State->get<DynamicTypeMap>(); - if (DTM.isEmpty()) { - Out << "null," << NL; - return; - } - - ++Space; - Out << '[' << NL; - for (DynamicTypeMapTy::iterator I = DTM.begin(); I != DTM.end(); ++I) { - const MemRegion *MR = I->first; - const DynamicTypeInfo &DTI = I->second; - Out << "{ \"region\": \"" << MR << "\", \"dyn_type\": "; - if (DTI.isValid()) { - Out << '\"' << DTI.getType()->getPointeeType().getAsString() - << "\", \"sub_classable\": " - << (DTI.canBeASubClass() ? "true" : "false"); - } else { - Out << "null"; // Invalid type info - } - Out << "}"; - - if (std::next(I) != DTM.end()) - Out << ','; - Out << NL; - } - - --Space; - Indent(Out, Space, IsDot) << "]," << NL; -} - -void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() { - static int index = 0; - return &index; -} - -} // namespace ento -} // namespace clang diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 551c89b04db4b..1ccf4c6104a65 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -108,6 +108,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry, case Stmt::ObjCStringLiteralClass: case Stmt::StringLiteralClass: case Stmt::TypeTraitExprClass: + case Stmt::SizeOfPackExprClass: // Known constants; defer to SValBuilder. return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp index c86b1436baabb..c4838492271cd 100644 --- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -16,6 +16,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" #include "clang/AST/Stmt.h" +#include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/ProgramPoint.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/Basic/LLVM.h" @@ -134,7 +135,7 @@ bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { // Do not collect nodes for non-consumed Stmt or Expr to ensure precise // diagnostic generation; specifically, so that we could anchor arrows // pointing to the beginning of statements (as written in code). - ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + const ParentMap &PM = progPoint.getLocationContext()->getParentMap(); if (!PM.isConsumedExpr(Ex)) return false; @@ -282,16 +283,115 @@ ExplodedNode * const *ExplodedNode::NodeGroup::end() const { return Storage.getAddrOfPtr1() + 1; } -int64_t ExplodedNode::getID(ExplodedGraph *G) const { - return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this); -} - bool ExplodedNode::isTrivial() const { return pred_size() == 1 && succ_size() == 1 && getFirstPred()->getState()->getID() == getState()->getID() && getFirstPred()->succ_size() == 1; } +const CFGBlock *ExplodedNode::getCFGBlock() const { + ProgramPoint P = getLocation(); + if (auto BEP = P.getAs<BlockEntrance>()) + return BEP->getBlock(); + + // Find the node's current statement in the CFG. + // FIXME: getStmtForDiagnostics() does nasty things in order to provide + // a valid statement for body farms, do we need this behavior here? + if (const Stmt *S = getStmtForDiagnostics()) + return getLocationContext() + ->getAnalysisDeclContext() + ->getCFGStmtMap() + ->getBlock(S); + + return nullptr; +} + +static const LocationContext * +findTopAutosynthesizedParentContext(const LocationContext *LC) { + assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized()); + const LocationContext *ParentLC = LC->getParent(); + assert(ParentLC && "We don't start analysis from autosynthesized code"); + while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) { + LC = ParentLC; + ParentLC = LC->getParent(); + assert(ParentLC && "We don't start analysis from autosynthesized code"); + } + return LC; +} + +const Stmt *ExplodedNode::getStmtForDiagnostics() const { + // We cannot place diagnostics on autosynthesized code. + // Put them onto the call site through which we jumped into autosynthesized + // code for the first time. + const LocationContext *LC = getLocationContext(); + if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) { + // It must be a stack frame because we only autosynthesize functions. + return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC)) + ->getCallSite(); + } + // Otherwise, see if the node's program point directly points to a statement. + // FIXME: Refactor into a ProgramPoint method? + ProgramPoint P = getLocation(); + if (auto SP = P.getAs<StmtPoint>()) + return SP->getStmt(); + if (auto BE = P.getAs<BlockEdge>()) + return BE->getSrc()->getTerminatorStmt(); + if (auto CE = P.getAs<CallEnter>()) + return CE->getCallExpr(); + if (auto CEE = P.getAs<CallExitEnd>()) + return CEE->getCalleeContext()->getCallSite(); + if (auto PIPP = P.getAs<PostInitializer>()) + return PIPP->getInitializer()->getInit(); + if (auto CEB = P.getAs<CallExitBegin>()) + return CEB->getReturnStmt(); + if (auto FEP = P.getAs<FunctionExitPoint>()) + return FEP->getStmt(); + + return nullptr; +} + +const Stmt *ExplodedNode::getNextStmtForDiagnostics() const { + for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) { + if (const Stmt *S = N->getStmtForDiagnostics()) { + // Check if the statement is '?' or '&&'/'||'. These are "merges", + // not actual statement points. + switch (S->getStmtClass()) { + case Stmt::ChooseExprClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + continue; + case Stmt::BinaryOperatorClass: { + BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) + continue; + break; + } + default: + break; + } + // We found the statement, so return it. + return S; + } + } + + return nullptr; +} + +const Stmt *ExplodedNode::getPreviousStmtForDiagnostics() const { + for (const ExplodedNode *N = getFirstPred(); N; N = N->getFirstPred()) + if (const Stmt *S = N->getStmtForDiagnostics()) + return S; + + return nullptr; +} + +const Stmt *ExplodedNode::getCurrentOrPreviousStmtForDiagnostics() const { + if (const Stmt *S = getStmtForDiagnostics()) + return S; + + return getPreviousStmtForDiagnostics(); +} + ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, ProgramStateRef State, bool IsSink, @@ -313,14 +413,14 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, V = (NodeTy*) getAllocator().Allocate<NodeTy>(); } - new (V) NodeTy(L, State, IsSink); + ++NumNodes; + new (V) NodeTy(L, State, NumNodes, IsSink); if (ReclaimNodeInterval) ChangedNodes.push_back(V); // Insert the node into the node set and return it. Nodes.InsertNode(V, InsertPos); - ++NumNodes; if (IsNew) *IsNew = true; } @@ -332,9 +432,10 @@ ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L, ProgramStateRef State, + int64_t Id, bool IsSink) { NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>(); - new (V) NodeTy(L, State, IsSink); + new (V) NodeTy(L, State, Id, IsSink); return V; } @@ -394,7 +495,8 @@ ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, // Create the corresponding node in the new graph and record the mapping // from the old node to the new node. - ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink()); + ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, + N->getID(), N->isSink()); Pass2[N] = NewN; // Also record the reverse mapping from the new node to the old node. diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index 1fef5b3c1edd5..e92e95354f5f2 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -977,8 +977,8 @@ void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType, CallOpts.IsArrayCtorOrDtor).getAsRegion(); - VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, - Pred, Dst, CallOpts); + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), + /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, @@ -1036,8 +1036,9 @@ void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, Base->isVirtual()); - VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {}); + EvalCallOptions CallOpts; + VisitCXXDestructor(BaseTy, BaseVal.getAsRegion(), CurDtor->getBody(), + /*IsBase=*/true, Pred, Dst, CallOpts); } void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, @@ -1048,10 +1049,10 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, const LocationContext *LCtx = Pred->getLocationContext(); const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); - Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, - LCtx->getStackFrame()); - SVal FieldVal = - State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); + Loc ThisStorageLoc = + getSValBuilder().getCXXThis(CurDtor, LCtx->getStackFrame()); + Loc ThisLoc = State->getSVal(ThisStorageLoc).castAs<Loc>(); + SVal FieldVal = State->getLValue(Member, ThisLoc); // FIXME: We need to run the same destructor on every element of the array. // This workaround will just run the first destructor (which will still @@ -1060,8 +1061,8 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, FieldVal = makeZeroElementRegion(State, FieldVal, T, CallOpts.IsArrayCtorOrDtor); - VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(), - CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); + VisitCXXDestructor(T, FieldVal.getAsRegion(), CurDtor->getBody(), + /*IsBase=*/false, Pred, Dst, CallOpts); } void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, @@ -1109,8 +1110,6 @@ void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, EvalCallOptions CallOpts; CallOpts.IsTemporaryCtorOrDtor = true; if (!MR) { - CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; - // If we have no MR, we still need to unwrap the array to avoid destroying // the whole array at once. Regardless, we'd eventually need to model array // destructors properly, element-by-element. @@ -1266,6 +1265,9 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::OMPCancelDirectiveClass: case Stmt::OMPTaskLoopDirectiveClass: case Stmt::OMPTaskLoopSimdDirectiveClass: + case Stmt::OMPMasterTaskLoopDirectiveClass: + case Stmt::OMPMasterTaskLoopSimdDirectiveClass: + case Stmt::OMPParallelMasterTaskLoopDirectiveClass: case Stmt::OMPDistributeDirectiveClass: case Stmt::OMPDistributeParallelForDirectiveClass: case Stmt::OMPDistributeParallelForSimdDirectiveClass: @@ -1369,6 +1371,8 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, case Stmt::CUDAKernelCallExprClass: case Stmt::OpaqueValueExprClass: case Stmt::AsTypeExprClass: + case Stmt::ConceptSpecializationExprClass: + case Stmt::CXXRewrittenBinaryOperatorClass: // Fall through. // Cases we intentionally don't evaluate, since they don't need @@ -3005,9 +3009,13 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end()); for (const auto &EQ : EQClasses) { - for (const BugReport &Report : EQ) { - if (Report.getErrorNode()->getState() == N->getState() && - Report.getErrorNode()->getLocation() == N->getLocation()) + for (const auto &I : EQ.getReports()) { + const auto *PR = dyn_cast<PathSensitiveBugReport>(I.get()); + if (!PR) + continue; + const ExplodedNode *EN = PR->getErrorNode(); + if (EN->getState() == N->getState() && + EN->getLocation() == N->getLocation()) return true; } } @@ -3023,22 +3031,16 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { llvm::function_ref<void(const ExplodedNode *)> PreCallback, llvm::function_ref<void(const ExplodedNode *)> PostCallback, llvm::function_ref<bool(const ExplodedNode *)> Stop) { - const ExplodedNode *FirstHiddenNode = N; - while (FirstHiddenNode->pred_size() == 1 && - isNodeHidden(*FirstHiddenNode->pred_begin())) { - FirstHiddenNode = *FirstHiddenNode->pred_begin(); - } - const ExplodedNode *OtherNode = FirstHiddenNode; while (true) { - PreCallback(OtherNode); - if (Stop(OtherNode)) + PreCallback(N); + if (Stop(N)) return true; - if (OtherNode == N) + if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc())) break; - PostCallback(OtherNode); + PostCallback(N); - OtherNode = *OtherNode->succ_begin(); + N = N->getFirstSucc(); } return false; } @@ -3055,16 +3057,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { const unsigned int Space = 1; ProgramStateRef State = N->getState(); - auto Noop = [](const ExplodedNode*){}; - bool HasReport = traverseHiddenNodes( - N, Noop, Noop, &nodeHasBugReport); - bool IsSink = traverseHiddenNodes( - N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); }); - - Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \"" - << (const void *)N << "\", \"state_id\": " << State->getID() - << ", \"has_report\": " << (HasReport ? "true" : "false") - << ", \"is_sink\": " << (IsSink ? "true" : "false") + Out << "{ \"state_id\": " << State->getID() << ",\\l"; Indent(Out, Space, IsDot) << "\"program_points\": [\\l"; @@ -3077,9 +3070,12 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { OtherNode->getLocation().printJson(Out, /*NL=*/"\\l"); Out << ", \"tag\": "; if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) - Out << '\"' << Tag->getTagDescription() << "\" }"; + Out << '\"' << Tag->getTagDescription() << "\""; else - Out << "null }"; + Out << "null"; + Out << ", \"node_id\": " << OtherNode->getID() << + ", \"is_sink\": " << OtherNode->isSink() << + ", \"has_report\": " << nodeHasBugReport(OtherNode) << " }"; }, // Adds a comma and a new-line between each program point. [&](const ExplodedNode *) { Out << ",\\l"; }, @@ -3088,16 +3084,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { Out << "\\l"; // Adds a new-line to the last program point. Indent(Out, Space, IsDot) << "],\\l"; - bool SameAsAllPredecessors = - std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) { - return P->getState() == State; - }); - - if (!SameAsAllPredecessors) { - State->printDOT(Out, N->getLocationContext(), Space); - } else { - Indent(Out, Space, IsDot) << "\"program_state\": null"; - } + State->printDOT(Out, N->getLocationContext(), Space); Out << "\\l}\\l"; return Out.str(); @@ -3132,8 +3119,12 @@ std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { // Iterate through the reports and get their nodes. for (BugReporter::EQClasses_iterator EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { - const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode()); - if (N) Src.push_back(N); + const auto *R = + dyn_cast<PathSensitiveBugReport>(EI->getReports()[0].get()); + if (!R) + continue; + const auto *N = const_cast<ExplodedNode *>(R->getErrorNode()); + Src.push_back(N); } return DumpGraph(Src, Filename); } else { diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp index f436650fbdd9b..02a398c77ac88 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -850,8 +850,7 @@ VisitOffsetOfExpr(const OffsetOfExpr *OOE, if (OOE->EvaluateAsInt(Result, getContext())) { APSInt IV = Result.Val.getInt(); assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); - assert(OOE->getType()->isBuiltinType()); - assert(OOE->getType()->getAs<BuiltinType>()->isInteger()); + assert(OOE->getType()->castAs<BuiltinType>()->isInteger()); assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); SVal X = svalBuilder.makeIntVal(IV); B.generateNode(OOE, Pred, diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp index 1cbd09ea57932..058be985540d3 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -323,7 +323,8 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( CallEventManager &CEMgr = getStateManager().getCallEventManager(); SVal V = UnknownVal(); auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> { - const LocationContext *FutureSFC = Caller->getCalleeStackFrame(); + const LocationContext *FutureSFC = + Caller->getCalleeStackFrame(currBldrCtx->blockCount()); // Return early if we are unable to reliably foresee // the future stack frame. if (!FutureSFC) @@ -342,7 +343,7 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( // because this-argument is implemented as a normal argument in // operator call expressions but not in operator declarations. const VarRegion *VR = Caller->getParameterLocation( - *Caller->getAdjustedParameterIndex(Idx)); + *Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount()); if (!VR) return None; @@ -603,7 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, bool IsBaseDtor, ExplodedNode *Pred, ExplodedNodeSet &Dst, - const EvalCallOptions &CallOpts) { + EvalCallOptions &CallOpts) { assert(S && "A destructor without a trigger!"); const LocationContext *LCtx = Pred->getLocationContext(); ProgramStateRef State = Pred->getState(); @@ -611,7 +612,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); assert(RecordDecl && "Only CXXRecordDecls should have destructors"); const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); - // FIXME: There should always be a Decl, otherwise the destructor call // shouldn't have been added to the CFG in the first place. if (!DtorDecl) { @@ -625,9 +625,27 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType, return; } + if (!Dest) { + // We're trying to destroy something that is not a region. This may happen + // for a variety of reasons (unknown target region, concrete integer instead + // of target region, etc.). The current code makes an attempt to recover. + // FIXME: We probably don't really need to recover when we're dealing + // with concrete integers specifically. + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + if (const Expr *E = dyn_cast_or_null<Expr>(S)) { + Dest = MRMgr.getCXXTempObjectRegion(E, Pred->getLocationContext()); + } else { + static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor"); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateSink(Pred->getLocation().withTag(&T), + Pred->getState(), Pred); + return; + } + } + CallEventManager &CEMgr = getStateManager().getCallEventManager(); CallEventRef<CXXDestructorCall> Call = - CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); + CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), Call->getSourceRange().getBegin(), @@ -757,7 +775,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { // Invalidate placement args. // FIXME: Once we figure out how we want allocators to work, - // we should be using the usual pre-/(default-)eval-/post-call checks here. + // we should be using the usual pre-/(default-)eval-/post-call checkers + // here. State = Call->invalidateRegions(blockCount); if (!State) return; @@ -785,9 +804,8 @@ void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, if (CNE->isArray()) { // FIXME: allocating an array requires simulating the constructors. // For now, just return a symbolicated region. - if (const SubRegion *NewReg = - dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) { - QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + if (const auto *NewReg = cast_or_null<SubRegion>(symVal.getAsRegion())) { + QualType ObjTy = CNE->getType()->getPointeeType(); const ElementRegion *EleReg = getStoreManager().GetElementZeroRegion(NewReg, ObjTy); Result = loc::MemRegionVal(EleReg); diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp index b935e3afe34b2..345d4d817deae 100644 --- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -451,9 +451,8 @@ bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, // Construct a new stack frame for the callee. AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); const StackFrameContext *CalleeSFC = - CalleeADC->getStackFrame(ParentOfCallee, CallE, - currBldrCtx->getBlock(), - currStmtIdx); + CalleeADC->getStackFrame(ParentOfCallee, CallE, currBldrCtx->getBlock(), + currBldrCtx->blockCount(), currStmtIdx); CallEnter Loc(CallE, CalleeSFC, CurLC); diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp index 64c42699fcf3c..a4918d7179ff5 100644 --- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/Stmt.h" @@ -23,7 +24,6 @@ #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/Rewrite/Core/Rewriter.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/ArrayRef.h" @@ -134,17 +134,17 @@ private: } // namespace -void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP) { +void ento::createHTMLDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &) { C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true)); } -void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& prefix, - const Preprocessor &PP) { +void ento::createHTMLSingleFileDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &) { C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false)); } @@ -555,8 +555,9 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " - << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue, - PP.getLangOpts()) << " -->\n"; + << GetIssueHash(SMgr, L, D.getCheckerName(), D.getBugType(), + DeclWithIssue, PP.getLangOpts()) + << " -->\n"; os << "\n<!-- BUGLINE " << LineNumber @@ -612,7 +613,7 @@ HandlePopUpPieceStartTag(Rewriter &R, for (const auto &Range : PopUpRanges) { html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", "<table class='variable_popup'><tbody>", - /*IsTokenRange=*/false); + /*IsTokenRange=*/true); } } @@ -644,12 +645,11 @@ static void HandlePopUpPieceEndTag(Rewriter &R, Out << "</tbody></table></span>"; html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "<span class='variable'>", Buf.c_str(), - /*IsTokenRange=*/false); - - // Otherwise inject just the new row at the end of the range. + /*IsTokenRange=*/true); } else { + // Otherwise inject just the new row at the end of the range. html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(), - /*IsTokenRange=*/false); + /*IsTokenRange=*/true); } } @@ -658,16 +658,14 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R, // Process the path. // Maintain the counts of extra note pieces separately. unsigned TotalPieces = path.size(); - unsigned TotalNotePieces = - std::count_if(path.begin(), path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &p) { - return isa<PathDiagnosticNotePiece>(*p); - }); - unsigned PopUpPieceCount = - std::count_if(path.begin(), path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &p) { - return isa<PathDiagnosticPopUpPiece>(*p); - }); + unsigned TotalNotePieces = std::count_if( + path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) { + return isa<PathDiagnosticNotePiece>(*p); + }); + unsigned PopUpPieceCount = std::count_if( + path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) { + return isa<PathDiagnosticPopUpPiece>(*p); + }); unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount; unsigned NumRegularPieces = TotalRegularPieces; diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp index 9838249ae82ca..1a09a521f1167 100644 --- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp +++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -165,7 +165,9 @@ static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) { return true; while (!N->pred_empty()) { - const Stmt *S = PathDiagnosticLocation::getStmt(N); + // FIXME: getStmtForDiagnostics() does nasty things in order to provide + // a valid statement for body farms, do we need this behavior here? + const Stmt *S = N->getStmtForDiagnostics(); if (!S) { N = N->getFirstPred(); continue; diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp index f763701af7fb1..a10d7e69ad7e7 100644 --- a/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -506,7 +506,7 @@ void ElementRegion::dumpToStream(raw_ostream &os) const { } void FieldRegion::dumpToStream(raw_ostream &os) const { - os << superRegion << "->" << *getDecl(); + os << superRegion << "." << *getDecl(); } void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { @@ -1075,7 +1075,7 @@ MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, const SubRegion *Super, bool IsVirtual) { if (isa<TypedValueRegion>(Super)) { - assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual)); + assert(isValidBaseClass(RD, cast<TypedValueRegion>(Super), IsVirtual)); (void)&isValidBaseClass; if (IsVirtual) { @@ -1426,6 +1426,7 @@ static RegionOffset calculateOffset(const MemRegion *R) { case MemRegion::FieldRegionKind: { const auto *FR = cast<FieldRegion>(R); R = FR->getSuperRegion(); + assert(R); const RecordDecl *RD = FR->getDecl()->getParent(); if (RD->isUnion() || !RD->isCompleteDefinition()) { diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp deleted file mode 100644 index 54fbd6a5bc496..0000000000000 --- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp +++ /dev/null @@ -1,1466 +0,0 @@ -//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// This file defines the PathDiagnostic-related interfaces. -// -//===----------------------------------------------------------------------===// - -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclBase.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclObjC.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/Expr.h" -#include "clang/AST/ExprCXX.h" -#include "clang/AST/OperationKinds.h" -#include "clang/AST/ParentMap.h" -#include "clang/AST/Stmt.h" -#include "clang/AST/Type.h" -#include "clang/Analysis/AnalysisDeclContext.h" -#include "clang/Analysis/CFG.h" -#include "clang/Analysis/ProgramPoint.h" -#include "clang/Basic/FileManager.h" -#include "clang/Basic/LLVM.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Basic/SourceManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/FoldingSet.h" -#include "llvm/ADT/None.h" -#include "llvm/ADT/Optional.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/ADT/SmallString.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringExtras.h" -#include "llvm/ADT/StringRef.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/ErrorHandling.h" -#include "llvm/Support/raw_ostream.h" -#include <cassert> -#include <cstring> -#include <memory> -#include <utility> -#include <vector> - -using namespace clang; -using namespace ento; - -bool PathDiagnosticMacroPiece::containsEvent() const { - for (const auto &P : subPieces) { - if (isa<PathDiagnosticEventPiece>(*P)) - return true; - if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get())) - if (MP->containsEvent()) - return true; - } - return false; -} - -static StringRef StripTrailingDots(StringRef s) { - for (StringRef::size_type i = s.size(); i != 0; --i) - if (s[i - 1] != '.') - return s.substr(0, i); - return {}; -} - -PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, - Kind k, DisplayHint hint) - : str(StripTrailingDots(s)), kind(k), Hint(hint) {} - -PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) - : kind(k), Hint(hint) {} - -PathDiagnosticPiece::~PathDiagnosticPiece() = default; - -PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default; - -PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default; - -PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default; - -PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; - -PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; - -PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default; - -void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, - bool ShouldFlattenMacros) const { - for (auto &Piece : *this) { - switch (Piece->getKind()) { - case PathDiagnosticPiece::Call: { - auto &Call = cast<PathDiagnosticCallPiece>(*Piece); - if (auto CallEnter = Call.getCallEnterEvent()) - Current.push_back(std::move(CallEnter)); - Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros); - if (auto callExit = Call.getCallExitEvent()) - Current.push_back(std::move(callExit)); - break; - } - case PathDiagnosticPiece::Macro: { - auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece); - if (ShouldFlattenMacros) { - Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); - } else { - Current.push_back(Piece); - PathPieces NewPath; - Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); - // FIXME: This probably shouldn't mutate the original path piece. - Macro.subPieces = NewPath; - } - break; - } - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::ControlFlow: - case PathDiagnosticPiece::Note: - case PathDiagnosticPiece::PopUp: - Current.push_back(Piece); - break; - } - } -} - -PathDiagnostic::~PathDiagnostic() = default; - -PathDiagnostic::PathDiagnostic( - StringRef CheckName, const Decl *declWithIssue, StringRef bugtype, - StringRef verboseDesc, StringRef shortDesc, StringRef category, - PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, - std::unique_ptr<FilesToLineNumsMap> ExecutedLines) - : CheckName(CheckName), DeclWithIssue(declWithIssue), - BugType(StripTrailingDots(bugtype)), - VerboseDesc(StripTrailingDots(verboseDesc)), - ShortDesc(StripTrailingDots(shortDesc)), - Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), - UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), - path(pathImpl) {} - -static PathDiagnosticCallPiece * -getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, - const SourceManager &SMgr) { - SourceLocation CallLoc = CP->callEnter.asLocation(); - - // If the call is within a macro, don't do anything (for now). - if (CallLoc.isMacroID()) - return nullptr; - - assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) && - "The call piece should not be in a header file."); - - // Check if CP represents a path through a function outside of the main file. - if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr)) - return CP; - - const PathPieces &Path = CP->path; - if (Path.empty()) - return nullptr; - - // Check if the last piece in the callee path is a call to a function outside - // of the main file. - if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) - return getFirstStackedCallToHeaderFile(CPInner, SMgr); - - // Otherwise, the last piece is in the main file. - return nullptr; -} - -void PathDiagnostic::resetDiagnosticLocationToMainFile() { - if (path.empty()) - return; - - PathDiagnosticPiece *LastP = path.back().get(); - assert(LastP); - const SourceManager &SMgr = LastP->getLocation().getManager(); - - // We only need to check if the report ends inside headers, if the last piece - // is a call piece. - if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { - CP = getFirstStackedCallToHeaderFile(CP, SMgr); - if (CP) { - // Mark the piece. - CP->setAsLastInMainSourceFile(); - - // Update the path diagnostic message. - const auto *ND = dyn_cast<NamedDecl>(CP->getCallee()); - if (ND) { - SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - os << " (within a call to '" << ND->getDeclName() << "')"; - appendToDesc(os.str()); - } - - // Reset the report containing declaration and location. - DeclWithIssue = CP->getCaller(); - Loc = CP->getLocation(); - - return; - } - } -} - -void PathDiagnosticConsumer::anchor() {} - -PathDiagnosticConsumer::~PathDiagnosticConsumer() { - // Delete the contents of the FoldingSet if it isn't empty already. - for (auto &Diag : Diags) - delete &Diag; -} - -void PathDiagnosticConsumer::HandlePathDiagnostic( - std::unique_ptr<PathDiagnostic> D) { - if (!D || D->path.empty()) - return; - - // We need to flatten the locations (convert Stmt* to locations) because - // the referenced statements may be freed by the time the diagnostics - // are emitted. - D->flattenLocations(); - - // If the PathDiagnosticConsumer does not support diagnostics that - // cross file boundaries, prune out such diagnostics now. - if (!supportsCrossFileDiagnostics()) { - // Verify that the entire path is from the same FileID. - FileID FID; - const SourceManager &SMgr = D->path.front()->getLocation().getManager(); - SmallVector<const PathPieces *, 5> WorkList; - WorkList.push_back(&D->path); - SmallString<128> buf; - llvm::raw_svector_ostream warning(buf); - warning << "warning: Path diagnostic report is not generated. Current " - << "output format does not support diagnostics that cross file " - << "boundaries. Refer to --analyzer-output for valid output " - << "formats\n"; - - while (!WorkList.empty()) { - const PathPieces &path = *WorkList.pop_back_val(); - - for (const auto &I : path) { - const PathDiagnosticPiece *piece = I.get(); - FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); - - if (FID.isInvalid()) { - FID = SMgr.getFileID(L); - } else if (SMgr.getFileID(L) != FID) { - llvm::errs() << warning.str(); - return; - } - - // Check the source ranges. - ArrayRef<SourceRange> Ranges = piece->getRanges(); - for (const auto &I : Ranges) { - SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); - if (!L.isFileID() || SMgr.getFileID(L) != FID) { - llvm::errs() << warning.str(); - return; - } - L = SMgr.getExpansionLoc(I.getEnd()); - if (!L.isFileID() || SMgr.getFileID(L) != FID) { - llvm::errs() << warning.str(); - return; - } - } - - if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) - WorkList.push_back(&call->path); - else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) - WorkList.push_back(¯o->subPieces); - } - } - - if (FID.isInvalid()) - return; // FIXME: Emit a warning? - } - - // Profile the node to see if we already have something matching it - llvm::FoldingSetNodeID profile; - D->Profile(profile); - void *InsertPos = nullptr; - - if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { - // Keep the PathDiagnostic with the shorter path. - // Note, the enclosing routine is called in deterministic order, so the - // results will be consistent between runs (no reason to break ties if the - // size is the same). - const unsigned orig_size = orig->full_size(); - const unsigned new_size = D->full_size(); - if (orig_size <= new_size) - return; - - assert(orig != D.get()); - Diags.RemoveNode(orig); - delete orig; - } - - Diags.InsertNode(D.release()); -} - -static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); - -static Optional<bool> -compareControlFlow(const PathDiagnosticControlFlowPiece &X, - const PathDiagnosticControlFlowPiece &Y) { - FullSourceLoc XSL = X.getStartLocation().asLocation(); - FullSourceLoc YSL = Y.getStartLocation().asLocation(); - if (XSL != YSL) - return XSL.isBeforeInTranslationUnitThan(YSL); - FullSourceLoc XEL = X.getEndLocation().asLocation(); - FullSourceLoc YEL = Y.getEndLocation().asLocation(); - if (XEL != YEL) - return XEL.isBeforeInTranslationUnitThan(YEL); - return None; -} - -static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, - const PathDiagnosticMacroPiece &Y) { - return comparePath(X.subPieces, Y.subPieces); -} - -static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, - const PathDiagnosticCallPiece &Y) { - FullSourceLoc X_CEL = X.callEnter.asLocation(); - FullSourceLoc Y_CEL = Y.callEnter.asLocation(); - if (X_CEL != Y_CEL) - return X_CEL.isBeforeInTranslationUnitThan(Y_CEL); - FullSourceLoc X_CEWL = X.callEnterWithin.asLocation(); - FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation(); - if (X_CEWL != Y_CEWL) - return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL); - FullSourceLoc X_CRL = X.callReturn.asLocation(); - FullSourceLoc Y_CRL = Y.callReturn.asLocation(); - if (X_CRL != Y_CRL) - return X_CRL.isBeforeInTranslationUnitThan(Y_CRL); - return comparePath(X.path, Y.path); -} - -static Optional<bool> comparePiece(const PathDiagnosticPiece &X, - const PathDiagnosticPiece &Y) { - if (X.getKind() != Y.getKind()) - return X.getKind() < Y.getKind(); - - FullSourceLoc XL = X.getLocation().asLocation(); - FullSourceLoc YL = Y.getLocation().asLocation(); - if (XL != YL) - return XL.isBeforeInTranslationUnitThan(YL); - - if (X.getString() != Y.getString()) - return X.getString() < Y.getString(); - - if (X.getRanges().size() != Y.getRanges().size()) - return X.getRanges().size() < Y.getRanges().size(); - - const SourceManager &SM = XL.getManager(); - - for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) { - SourceRange XR = X.getRanges()[i]; - SourceRange YR = Y.getRanges()[i]; - if (XR != YR) { - if (XR.getBegin() != YR.getBegin()) - return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin()); - return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); - } - } - - switch (X.getKind()) { - case PathDiagnosticPiece::ControlFlow: - return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), - cast<PathDiagnosticControlFlowPiece>(Y)); - case PathDiagnosticPiece::Macro: - return compareMacro(cast<PathDiagnosticMacroPiece>(X), - cast<PathDiagnosticMacroPiece>(Y)); - case PathDiagnosticPiece::Call: - return compareCall(cast<PathDiagnosticCallPiece>(X), - cast<PathDiagnosticCallPiece>(Y)); - case PathDiagnosticPiece::Event: - case PathDiagnosticPiece::Note: - case PathDiagnosticPiece::PopUp: - return None; - } - llvm_unreachable("all cases handled"); -} - -static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { - if (X.size() != Y.size()) - return X.size() < Y.size(); - - PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); - PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); - - for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { - Optional<bool> b = comparePiece(**X_I, **Y_I); - if (b.hasValue()) - return b.getValue(); - } - - return None; -} - -static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) { - std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc(); - std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc(); - const SourceManager &SM = XL.getManager(); - std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs); - if (InSameTU.first) - return XL.isBeforeInTranslationUnitThan(YL); - const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID()); - const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID()); - if (!XFE || !YFE) - return XFE && !YFE; - int NameCmp = XFE->getName().compare(YFE->getName()); - if (NameCmp != 0) - return NameCmp == -1; - // Last resort: Compare raw file IDs that are possibly expansions. - return XL.getFileID() < YL.getFileID(); -} - -static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { - FullSourceLoc XL = X.getLocation().asLocation(); - FullSourceLoc YL = Y.getLocation().asLocation(); - if (XL != YL) - return compareCrossTUSourceLocs(XL, YL); - if (X.getBugType() != Y.getBugType()) - return X.getBugType() < Y.getBugType(); - if (X.getCategory() != Y.getCategory()) - return X.getCategory() < Y.getCategory(); - if (X.getVerboseDescription() != Y.getVerboseDescription()) - return X.getVerboseDescription() < Y.getVerboseDescription(); - if (X.getShortDescription() != Y.getShortDescription()) - return X.getShortDescription() < Y.getShortDescription(); - if (X.getDeclWithIssue() != Y.getDeclWithIssue()) { - const Decl *XD = X.getDeclWithIssue(); - if (!XD) - return true; - const Decl *YD = Y.getDeclWithIssue(); - if (!YD) - return false; - SourceLocation XDL = XD->getLocation(); - SourceLocation YDL = YD->getLocation(); - if (XDL != YDL) { - const SourceManager &SM = XL.getManager(); - return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM), - FullSourceLoc(YDL, SM)); - } - } - PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end(); - PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end(); - if (XE - XI != YE - YI) - return (XE - XI) < (YE - YI); - for ( ; XI != XE ; ++XI, ++YI) { - if (*XI != *YI) - return (*XI) < (*YI); - } - Optional<bool> b = comparePath(X.path, Y.path); - assert(b.hasValue()); - return b.getValue(); -} - -void PathDiagnosticConsumer::FlushDiagnostics( - PathDiagnosticConsumer::FilesMade *Files) { - if (flushed) - return; - - flushed = true; - - std::vector<const PathDiagnostic *> BatchDiags; - for (const auto &D : Diags) - BatchDiags.push_back(&D); - - // Sort the diagnostics so that they are always emitted in a deterministic - // order. - int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) = - [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) { - assert(*X != *Y && "PathDiagnostics not uniqued!"); - if (compare(**X, **Y)) - return -1; - assert(compare(**Y, **X) && "Not a total order!"); - return 1; - }; - array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp); - - FlushDiagnosticsImpl(BatchDiags, Files); - - // Delete the flushed diagnostics. - for (const auto D : BatchDiags) - delete D; - - // Clear out the FoldingSet. - Diags.clear(); -} - -PathDiagnosticConsumer::FilesMade::~FilesMade() { - for (PDFileEntry &Entry : Set) - Entry.~PDFileEntry(); -} - -void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD, - StringRef ConsumerName, - StringRef FileName) { - llvm::FoldingSetNodeID NodeID; - NodeID.Add(PD); - void *InsertPos; - PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); - if (!Entry) { - Entry = Alloc.Allocate<PDFileEntry>(); - Entry = new (Entry) PDFileEntry(NodeID); - Set.InsertNode(Entry, InsertPos); - } - - // Allocate persistent storage for the file name. - char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1); - memcpy(FileName_cstr, FileName.data(), FileName.size()); - - Entry->files.push_back(std::make_pair(ConsumerName, - StringRef(FileName_cstr, - FileName.size()))); -} - -PathDiagnosticConsumer::PDFileEntry::ConsumerFiles * -PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) { - llvm::FoldingSetNodeID NodeID; - NodeID.Add(PD); - void *InsertPos; - PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); - if (!Entry) - return nullptr; - return &Entry->files; -} - -//===----------------------------------------------------------------------===// -// PathDiagnosticLocation methods. -//===----------------------------------------------------------------------===// - -static SourceLocation getValidSourceLocation(const Stmt* S, - LocationOrAnalysisDeclContext LAC, - bool UseEnd = false) { - SourceLocation L = UseEnd ? S->getEndLoc() : S->getBeginLoc(); - assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " - "be passed to PathDiagnosticLocation upon creation."); - - // S might be a temporary statement that does not have a location in the - // source code, so find an enclosing statement and use its location. - if (!L.isValid()) { - AnalysisDeclContext *ADC; - if (LAC.is<const LocationContext*>()) - ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); - else - ADC = LAC.get<AnalysisDeclContext*>(); - - ParentMap &PM = ADC->getParentMap(); - - const Stmt *Parent = S; - do { - Parent = PM.getParent(Parent); - - // In rare cases, we have implicit top-level expressions, - // such as arguments for implicit member initializers. - // In this case, fall back to the start of the body (even if we were - // asked for the statement end location). - if (!Parent) { - const Stmt *Body = ADC->getBody(); - if (Body) - L = Body->getBeginLoc(); - else - L = ADC->getDecl()->getEndLoc(); - break; - } - - L = UseEnd ? Parent->getEndLoc() : Parent->getBeginLoc(); - } while (!L.isValid()); - } - - // FIXME: Ironically, this assert actually fails in some cases. - //assert(L.isValid()); - return L; -} - -static PathDiagnosticLocation -getLocationForCaller(const StackFrameContext *SFC, - const LocationContext *CallerCtx, - const SourceManager &SM) { - const CFGBlock &Block = *SFC->getCallSiteBlock(); - CFGElement Source = Block[SFC->getIndex()]; - - switch (Source.getKind()) { - case CFGElement::Statement: - case CFGElement::Constructor: - case CFGElement::CXXRecordTypedCall: - return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), - SM, CallerCtx); - case CFGElement::Initializer: { - const CFGInitializer &Init = Source.castAs<CFGInitializer>(); - return PathDiagnosticLocation(Init.getInitializer()->getInit(), - SM, CallerCtx); - } - case CFGElement::AutomaticObjectDtor: { - const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); - return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), - SM, CallerCtx); - } - case CFGElement::DeleteDtor: { - const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>(); - return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx); - } - case CFGElement::BaseDtor: - case CFGElement::MemberDtor: { - const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); - if (const Stmt *CallerBody = CallerInfo->getBody()) - return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); - return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); - } - case CFGElement::NewAllocator: { - const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>(); - return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx); - } - case CFGElement::TemporaryDtor: { - // Temporary destructors are for temporaries. They die immediately at around - // the location of CXXBindTemporaryExpr. If they are lifetime-extended, - // they'd be dealt with via an AutomaticObjectDtor instead. - const auto &Dtor = Source.castAs<CFGTemporaryDtor>(); - return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM, - CallerCtx); - } - case CFGElement::ScopeBegin: - case CFGElement::ScopeEnd: - llvm_unreachable("not yet implemented!"); - case CFGElement::LifetimeEnds: - case CFGElement::LoopExit: - llvm_unreachable("CFGElement kind should not be on callsite!"); - } - - llvm_unreachable("Unknown CFGElement kind"); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createBegin(const Decl *D, - const SourceManager &SM) { - return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createBegin(const Stmt *S, - const SourceManager &SM, - LocationOrAnalysisDeclContext LAC) { - return PathDiagnosticLocation(getValidSourceLocation(S, LAC), - SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createEnd(const Stmt *S, - const SourceManager &SM, - LocationOrAnalysisDeclContext LAC) { - if (const auto *CS = dyn_cast<CompoundStmt>(S)) - return createEndBrace(CS, SM); - return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), - SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, - const SourceManager &SM) { - return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createConditionalColonLoc( - const ConditionalOperator *CO, - const SourceManager &SM) { - return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, - const SourceManager &SM) { - - assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid()); - - // In some cases, getMemberLoc isn't valid -- in this case we'll return with - // some other related valid SourceLocation. - if (ME->getMemberLoc().isValid()) - return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); - - return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, - const SourceManager &SM) { - SourceLocation L = CS->getLBracLoc(); - return PathDiagnosticLocation(L, SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, - const SourceManager &SM) { - SourceLocation L = CS->getRBracLoc(); - return PathDiagnosticLocation(L, SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, - const SourceManager &SM) { - // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. - if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) - if (!CS->body_empty()) { - SourceLocation Loc = (*CS->body_begin())->getBeginLoc(); - return PathDiagnosticLocation(Loc, SM, SingleLocK); - } - - return PathDiagnosticLocation(); -} - -PathDiagnosticLocation -PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, - const SourceManager &SM) { - SourceLocation L = LC->getDecl()->getBodyRBrace(); - return PathDiagnosticLocation(L, SM, SingleLocK); -} - -PathDiagnosticLocation -PathDiagnosticLocation::create(const ProgramPoint& P, - const SourceManager &SMng) { - const Stmt* S = nullptr; - if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { - const CFGBlock *BSrc = BE->getSrc(); - if (BSrc->getTerminator().isVirtualBaseBranch()) { - // TODO: VirtualBaseBranches should also appear for destructors. - // In this case we should put the diagnostic at the end of decl. - return PathDiagnosticLocation::createBegin( - P.getLocationContext()->getDecl(), SMng); - - } else { - S = BSrc->getTerminatorCondition(); - if (!S) { - // If the BlockEdge has no terminator condition statement but its - // source is the entry of the CFG (e.g. a checker crated the branch at - // the beginning of a function), use the function's declaration instead. - assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no " - "TerminatorCondition and is not the enrty block of the CFG"); - return PathDiagnosticLocation::createBegin( - P.getLocationContext()->getDecl(), SMng); - } - } - } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { - S = SP->getStmt(); - if (P.getAs<PostStmtPurgeDeadSymbols>()) - return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); - } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { - return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), - SMng); - } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) { - return PathDiagnosticLocation(PIC->getLocation(), SMng); - } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { - return PathDiagnosticLocation(PIE->getLocation(), SMng); - } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { - return getLocationForCaller(CE->getCalleeContext(), - CE->getLocationContext(), - SMng); - } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { - return getLocationForCaller(CEE->getCalleeContext(), - CEE->getLocationContext(), - SMng); - } else if (auto CEB = P.getAs<CallExitBegin>()) { - if (const ReturnStmt *RS = CEB->getReturnStmt()) - return PathDiagnosticLocation::createBegin(RS, SMng, - CEB->getLocationContext()); - return PathDiagnosticLocation( - CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng); - } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { - CFGElement BlockFront = BE->getBlock()->front(); - if (auto StmtElt = BlockFront.getAs<CFGStmt>()) { - return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); - } else if (auto NewAllocElt = BlockFront.getAs<CFGNewAllocator>()) { - return PathDiagnosticLocation( - NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); - } - llvm_unreachable("Unexpected CFG element at front of block"); - } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) { - return PathDiagnosticLocation(FE->getStmt(), SMng, - FE->getLocationContext()); - } else { - llvm_unreachable("Unexpected ProgramPoint"); - } - - return PathDiagnosticLocation(S, SMng, P.getLocationContext()); -} - -static const LocationContext * -findTopAutosynthesizedParentContext(const LocationContext *LC) { - assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized()); - const LocationContext *ParentLC = LC->getParent(); - assert(ParentLC && "We don't start analysis from autosynthesized code"); - while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) { - LC = ParentLC; - ParentLC = LC->getParent(); - assert(ParentLC && "We don't start analysis from autosynthesized code"); - } - return LC; -} - -const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { - // We cannot place diagnostics on autosynthesized code. - // Put them onto the call site through which we jumped into autosynthesized - // code for the first time. - const LocationContext *LC = N->getLocationContext(); - if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) { - // It must be a stack frame because we only autosynthesize functions. - return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC)) - ->getCallSite(); - } - // Otherwise, see if the node's program point directly points to a statement. - ProgramPoint P = N->getLocation(); - if (auto SP = P.getAs<StmtPoint>()) - return SP->getStmt(); - if (auto BE = P.getAs<BlockEdge>()) - return BE->getSrc()->getTerminatorStmt(); - if (auto CE = P.getAs<CallEnter>()) - return CE->getCallExpr(); - if (auto CEE = P.getAs<CallExitEnd>()) - return CEE->getCalleeContext()->getCallSite(); - if (auto PIPP = P.getAs<PostInitializer>()) - return PIPP->getInitializer()->getInit(); - if (auto CEB = P.getAs<CallExitBegin>()) - return CEB->getReturnStmt(); - if (auto FEP = P.getAs<FunctionExitPoint>()) - return FEP->getStmt(); - - return nullptr; -} - -const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) { - for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) { - if (const Stmt *S = getStmt(N)) { - // Check if the statement is '?' or '&&'/'||'. These are "merges", - // not actual statement points. - switch (S->getStmtClass()) { - case Stmt::ChooseExprClass: - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: - continue; - case Stmt::BinaryOperatorClass: { - BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); - if (Op == BO_LAnd || Op == BO_LOr) - continue; - break; - } - default: - break; - } - // We found the statement, so return it. - return S; - } - } - - return nullptr; -} - -PathDiagnosticLocation - PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N, - const SourceManager &SM) { - assert(N && "Cannot create a location with a null node."); - const Stmt *S = getStmt(N); - const LocationContext *LC = N->getLocationContext(); - - if (!S) { - // If this is an implicit call, return the implicit call point location. - if (Optional<PreImplicitCall> PIE = N->getLocationAs<PreImplicitCall>()) - return PathDiagnosticLocation(PIE->getLocation(), SM); - if (auto FE = N->getLocationAs<FunctionExitPoint>()) { - if (const ReturnStmt *RS = FE->getStmt()) - return PathDiagnosticLocation::createBegin(RS, SM, LC); - } - S = getNextStmt(N); - } - - if (S) { - ProgramPoint P = N->getLocation(); - - // For member expressions, return the location of the '.' or '->'. - if (const auto *ME = dyn_cast<MemberExpr>(S)) - return PathDiagnosticLocation::createMemberLoc(ME, SM); - - // For binary operators, return the location of the operator. - if (const auto *B = dyn_cast<BinaryOperator>(S)) - return PathDiagnosticLocation::createOperatorLoc(B, SM); - - if (P.getAs<PostStmtPurgeDeadSymbols>()) - return PathDiagnosticLocation::createEnd(S, SM, LC); - - if (S->getBeginLoc().isValid()) - return PathDiagnosticLocation(S, SM, LC); - return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM); - } - - return createDeclEnd(N->getLocationContext(), SM); -} - -PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( - const PathDiagnosticLocation &PDL) { - FullSourceLoc L = PDL.asLocation(); - return PathDiagnosticLocation(L, L.getManager(), SingleLocK); -} - -FullSourceLoc - PathDiagnosticLocation::genLocation(SourceLocation L, - LocationOrAnalysisDeclContext LAC) const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - case RangeK: - break; - case StmtK: - // Defensive checking. - if (!S) - break; - return FullSourceLoc(getValidSourceLocation(S, LAC), - const_cast<SourceManager&>(*SM)); - case DeclK: - // Defensive checking. - if (!D) - break; - return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); - } - - return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); -} - -PathDiagnosticRange - PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { - assert(isValid()); - // Note that we want a 'switch' here so that the compiler can warn us in - // case we add more cases. - switch (K) { - case SingleLocK: - return PathDiagnosticRange(SourceRange(Loc,Loc), true); - case RangeK: - break; - case StmtK: { - const Stmt *S = asStmt(); - switch (S->getStmtClass()) { - default: - break; - case Stmt::DeclStmtClass: { - const auto *DS = cast<DeclStmt>(S); - if (DS->isSingleDecl()) { - // Should always be the case, but we'll be defensive. - return SourceRange(DS->getBeginLoc(), - DS->getSingleDecl()->getLocation()); - } - break; - } - // FIXME: Provide better range information for different - // terminators. - case Stmt::IfStmtClass: - case Stmt::WhileStmtClass: - case Stmt::DoStmtClass: - case Stmt::ForStmtClass: - case Stmt::ChooseExprClass: - case Stmt::IndirectGotoStmtClass: - case Stmt::SwitchStmtClass: - case Stmt::BinaryConditionalOperatorClass: - case Stmt::ConditionalOperatorClass: - case Stmt::ObjCForCollectionStmtClass: { - SourceLocation L = getValidSourceLocation(S, LAC); - return SourceRange(L, L); - } - } - SourceRange R = S->getSourceRange(); - if (R.isValid()) - return R; - break; - } - case DeclK: - if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) - return MD->getSourceRange(); - if (const auto *FD = dyn_cast<FunctionDecl>(D)) { - if (Stmt *Body = FD->getBody()) - return Body->getSourceRange(); - } - else { - SourceLocation L = D->getLocation(); - return PathDiagnosticRange(SourceRange(L, L), true); - } - } - - return SourceRange(Loc, Loc); -} - -void PathDiagnosticLocation::flatten() { - if (K == StmtK) { - K = RangeK; - S = nullptr; - D = nullptr; - } - else if (K == DeclK) { - K = SingleLocK; - S = nullptr; - D = nullptr; - } -} - -//===----------------------------------------------------------------------===// -// Manipulation of PathDiagnosticCallPieces. -//===----------------------------------------------------------------------===// - -std::shared_ptr<PathDiagnosticCallPiece> -PathDiagnosticCallPiece::construct(const CallExitEnd &CE, - const SourceManager &SM) { - const Decl *caller = CE.getLocationContext()->getDecl(); - PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), - CE.getLocationContext(), - SM); - return std::shared_ptr<PathDiagnosticCallPiece>( - new PathDiagnosticCallPiece(caller, pos)); -} - -PathDiagnosticCallPiece * -PathDiagnosticCallPiece::construct(PathPieces &path, - const Decl *caller) { - std::shared_ptr<PathDiagnosticCallPiece> C( - new PathDiagnosticCallPiece(path, caller)); - path.clear(); - auto *R = C.get(); - path.push_front(std::move(C)); - return R; -} - -void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, - const SourceManager &SM) { - const StackFrameContext *CalleeCtx = CE.getCalleeContext(); - Callee = CalleeCtx->getDecl(); - - callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); - callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); - - // Autosynthesized property accessors are special because we'd never - // pop back up to non-autosynthesized code until we leave them. - // This is not generally true for autosynthesized callees, which may call - // non-autosynthesized callbacks. - // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag - // defaults to false. - if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee)) - IsCalleeAnAutosynthesizedPropertyAccessor = ( - MD->isPropertyAccessor() && - CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized()); -} - -static void describeTemplateParameters(raw_ostream &Out, - const ArrayRef<TemplateArgument> TAList, - const LangOptions &LO, - StringRef Prefix = StringRef(), - StringRef Postfix = StringRef()); - -static void describeTemplateParameter(raw_ostream &Out, - const TemplateArgument &TArg, - const LangOptions &LO) { - - if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { - describeTemplateParameters(Out, TArg.getPackAsArray(), LO); - } else { - TArg.print(PrintingPolicy(LO), Out); - } -} - -static void describeTemplateParameters(raw_ostream &Out, - const ArrayRef<TemplateArgument> TAList, - const LangOptions &LO, - StringRef Prefix, StringRef Postfix) { - if (TAList.empty()) - return; - - Out << Prefix; - for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) { - describeTemplateParameter(Out, TAList[I], LO); - Out << ", "; - } - describeTemplateParameter(Out, TAList[TAList.size() - 1], LO); - Out << Postfix; -} - -static void describeClass(raw_ostream &Out, const CXXRecordDecl *D, - StringRef Prefix = StringRef()) { - if (!D->getIdentifier()) - return; - Out << Prefix << '\'' << *D; - if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D)) - describeTemplateParameters(Out, T->getTemplateArgs().asArray(), - D->getASTContext().getLangOpts(), "<", ">"); - - Out << '\''; -} - -static bool describeCodeDecl(raw_ostream &Out, const Decl *D, - bool ExtendedDescription, - StringRef Prefix = StringRef()) { - if (!D) - return false; - - if (isa<BlockDecl>(D)) { - if (ExtendedDescription) - Out << Prefix << "anonymous block"; - return ExtendedDescription; - } - - if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { - Out << Prefix; - if (ExtendedDescription && !MD->isUserProvided()) { - if (MD->isExplicitlyDefaulted()) - Out << "defaulted "; - else - Out << "implicit "; - } - - if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { - if (CD->isDefaultConstructor()) - Out << "default "; - else if (CD->isCopyConstructor()) - Out << "copy "; - else if (CD->isMoveConstructor()) - Out << "move "; - - Out << "constructor"; - describeClass(Out, MD->getParent(), " for "); - } else if (isa<CXXDestructorDecl>(MD)) { - if (!MD->isUserProvided()) { - Out << "destructor"; - describeClass(Out, MD->getParent(), " for "); - } else { - // Use ~Foo for explicitly-written destructors. - Out << "'" << *MD << "'"; - } - } else if (MD->isCopyAssignmentOperator()) { - Out << "copy assignment operator"; - describeClass(Out, MD->getParent(), " for "); - } else if (MD->isMoveAssignmentOperator()) { - Out << "move assignment operator"; - describeClass(Out, MD->getParent(), " for "); - } else { - if (MD->getParent()->getIdentifier()) - Out << "'" << *MD->getParent() << "::" << *MD << "'"; - else - Out << "'" << *MD << "'"; - } - - return true; - } - - Out << Prefix << '\'' << cast<NamedDecl>(*D); - - // Adding template parameters. - if (const auto FD = dyn_cast<FunctionDecl>(D)) - if (const TemplateArgumentList *TAList = - FD->getTemplateSpecializationArgs()) - describeTemplateParameters(Out, TAList->asArray(), - FD->getASTContext().getLangOpts(), "<", ">"); - - Out << '\''; - return true; -} - -std::shared_ptr<PathDiagnosticEventPiece> -PathDiagnosticCallPiece::getCallEnterEvent() const { - // We do not produce call enters and call exits for autosynthesized property - // accessors. We do generally produce them for other functions coming from - // the body farm because they may call callbacks that bring us back into - // visible code. - if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor) - return nullptr; - - SmallString<256> buf; - llvm::raw_svector_ostream Out(buf); - - Out << "Calling "; - describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); - - assert(callEnter.asLocation().isValid()); - return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str()); -} - -std::shared_ptr<PathDiagnosticEventPiece> -PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { - if (!callEnterWithin.asLocation().isValid()) - return nullptr; - if (Callee->isImplicit() || !Callee->hasBody()) - return nullptr; - if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee)) - if (MD->isDefaulted()) - return nullptr; - - SmallString<256> buf; - llvm::raw_svector_ostream Out(buf); - - Out << "Entered call"; - describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); - - return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str()); -} - -std::shared_ptr<PathDiagnosticEventPiece> -PathDiagnosticCallPiece::getCallExitEvent() const { - // We do not produce call enters and call exits for autosynthesized property - // accessors. We do generally produce them for other functions coming from - // the body farm because they may call callbacks that bring us back into - // visible code. - if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor) - return nullptr; - - SmallString<256> buf; - llvm::raw_svector_ostream Out(buf); - - if (!CallStackMessage.empty()) { - Out << CallStackMessage; - } else { - bool DidDescribe = describeCodeDecl(Out, Callee, - /*ExtendedDescription=*/false, - "Returning from "); - if (!DidDescribe) - Out << "Returning to caller"; - } - - assert(callReturn.asLocation().isValid()); - return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str()); -} - -static void compute_path_size(const PathPieces &pieces, unsigned &size) { - for (const auto &I : pieces) { - const PathDiagnosticPiece *piece = I.get(); - if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) - compute_path_size(cp->path, size); - else - ++size; - } -} - -unsigned PathDiagnostic::full_size() { - unsigned size = 0; - compute_path_size(path, size); - return size; -} - -//===----------------------------------------------------------------------===// -// FoldingSet profiling methods. -//===----------------------------------------------------------------------===// - -void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger(Range.getBegin().getRawEncoding()); - ID.AddInteger(Range.getEnd().getRawEncoding()); - ID.AddInteger(Loc.getRawEncoding()); -} - -void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { - ID.AddInteger((unsigned) getKind()); - ID.AddString(str); - // FIXME: Add profiling support for code hints. - ID.AddInteger((unsigned) getDisplayHint()); - ArrayRef<SourceRange> Ranges = getRanges(); - for (const auto &I : Ranges) { - ID.AddInteger(I.getBegin().getRawEncoding()); - ID.AddInteger(I.getEnd().getRawEncoding()); - } -} - -void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - for (const auto &I : path) - ID.Add(*I); -} - -void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - ID.Add(Pos); -} - -void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticPiece::Profile(ID); - for (const auto &I : *this) - ID.Add(I); -} - -void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); - for (const auto &I : subPieces) - ID.Add(*I); -} - -void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); -} - -void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const { - PathDiagnosticSpotPiece::Profile(ID); -} - -void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { - ID.Add(getLocation()); - ID.AddString(BugType); - ID.AddString(VerboseDesc); - ID.AddString(Category); -} - -void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { - Profile(ID); - for (const auto &I : path) - ID.Add(*I); - for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) - ID.AddString(*I); -} - -StackHintGenerator::~StackHintGenerator() = default; - -std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ - if (!N) - return getMessageForSymbolNotFound(); - - ProgramPoint P = N->getLocation(); - CallExitEnd CExit = P.castAs<CallExitEnd>(); - - // FIXME: Use CallEvent to abstract this over all calls. - const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); - const auto *CE = dyn_cast_or_null<CallExpr>(CallSite); - if (!CE) - return {}; - - // Check if one of the parameters are set to the interesting symbol. - unsigned ArgIndex = 0; - for (CallExpr::const_arg_iterator I = CE->arg_begin(), - E = CE->arg_end(); I != E; ++I, ++ArgIndex){ - SVal SV = N->getSVal(*I); - - // Check if the variable corresponding to the symbol is passed by value. - SymbolRef AS = SV.getAsLocSymbol(); - if (AS == Sym) { - return getMessageForArg(*I, ArgIndex); - } - - // Check if the parameter is a pointer to the symbol. - if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { - // Do not attempt to dereference void*. - if ((*I)->getType()->isVoidPointerType()) - continue; - SVal PSV = N->getState()->getSVal(Reg->getRegion()); - SymbolRef AS = PSV.getAsLocSymbol(); - if (AS == Sym) { - return getMessageForArg(*I, ArgIndex); - } - } - } - - // Check if we are returning the interesting symbol. - SVal SV = N->getSVal(CE); - SymbolRef RetSym = SV.getAsLocSymbol(); - if (RetSym == Sym) { - return getMessageForReturn(CE); - } - - return getMessageForSymbolNotFound(); -} - -std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, - unsigned ArgIndex) { - // Printed parameters start at 1, not 0. - ++ArgIndex; - - SmallString<200> buf; - llvm::raw_svector_ostream os(buf); - - os << Msg << " via " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) - << " parameter"; - - return os.str(); -} - -LLVM_DUMP_METHOD void PathPieces::dump() const { - unsigned index = 0; - for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { - llvm::errs() << "[" << index++ << "] "; - (*I)->dump(); - llvm::errs() << "\n"; - } -} - -LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { - llvm::errs() << "CALL\n--------------\n"; - - if (const Stmt *SLoc = getLocation().getStmtOrNull()) - SLoc->dump(); - else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee())) - llvm::errs() << *ND << "\n"; - else - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { - llvm::errs() << "EVENT\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { - llvm::errs() << "CONTROL\n--------------\n"; - getStartLocation().dump(); - llvm::errs() << " ---- to ----\n"; - getEndLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { - llvm::errs() << "MACRO\n--------------\n"; - // FIXME: Print which macro is being invoked. -} - -LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { - llvm::errs() << "NOTE\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const { - llvm::errs() << "POP-UP\n--------------\n"; - llvm::errs() << getString() << "\n"; - llvm::errs() << " ---- at ----\n"; - getLocation().dump(); -} - -LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { - if (!isValid()) { - llvm::errs() << "<INVALID>\n"; - return; - } - - switch (K) { - case RangeK: - // FIXME: actually print the range. - llvm::errs() << "<range>\n"; - break; - case SingleLocK: - asLocation().dump(); - llvm::errs() << "\n"; - break; - case StmtK: - if (S) - S->dump(); - else - llvm::errs() << "<NULL STMT>\n"; - break; - case DeclK: - if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) - llvm::errs() << *ND << "\n"; - else if (isa<BlockDecl>(D)) - // FIXME: Make this nicer. - llvm::errs() << "<block>\n"; - else if (D) - llvm::errs() << "<unknown decl>\n"; - else - llvm::errs() << "<NULL DECL>\n"; - break; - } -} diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp index 8387512792974..3a3942a8301bd 100644 --- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -10,20 +10,22 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PlistSupport.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/Version.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Frontend/ASTUnit.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/TokenConcatenation.h" #include "clang/Rewrite/Core/HTMLRewrite.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/IssueHash.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" -#include "llvm/ADT/Statistic.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" #include "llvm/Support/Casting.h" using namespace clang; @@ -39,12 +41,13 @@ namespace { class PlistDiagnostics : public PathDiagnosticConsumer { const std::string OutputFile; const Preprocessor &PP; + const cross_tu::CrossTranslationUnitContext &CTU; AnalyzerOptions &AnOpts; const bool SupportsCrossFileDiagnostics; public: - PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& prefix, + PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU, bool supportsMultipleFiles); ~PlistDiagnostics() override {} @@ -73,12 +76,14 @@ class PlistPrinter { const FIDMap& FM; AnalyzerOptions &AnOpts; const Preprocessor &PP; + const cross_tu::CrossTranslationUnitContext &CTU; llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces; public: PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, - const Preprocessor &PP) - : FM(FM), AnOpts(AnOpts), PP(PP) { + const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) + : FM(FM), AnOpts(AnOpts), PP(PP), CTU(CTU) { } void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { @@ -129,6 +134,7 @@ private: void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges, unsigned indent); void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent); + void EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, unsigned indent); void ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, @@ -160,8 +166,8 @@ struct ExpansionInfo { } // end of anonymous namespace static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, - const Preprocessor &PP, + AnalyzerOptions &AnOpts, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU, const PathPieces &Path); /// Print coverage information to output stream {@code o}. @@ -172,8 +178,9 @@ static void printCoverage(const PathDiagnostic *D, FIDMap &FM, llvm::raw_fd_ostream &o); -static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, - const Preprocessor &PP); +static ExpansionInfo +getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU); //===----------------------------------------------------------------------===// // Methods of PlistPrinter. @@ -216,6 +223,33 @@ void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message, EmitString(o, Message) << '\n'; } +void PlistPrinter::EmitFixits(raw_ostream &o, ArrayRef<FixItHint> fixits, + unsigned indent) { + if (fixits.size() == 0) + return; + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + Indent(o, indent) << "<key>fixits</key>\n"; + Indent(o, indent) << "<array>\n"; + for (const auto &fixit : fixits) { + assert(!fixit.isNull()); + // FIXME: Add support for InsertFromRange and BeforePreviousInsertion. + assert(!fixit.InsertFromRange.isValid() && "Not implemented yet!"); + assert(!fixit.BeforePreviousInsertions && "Not implemented yet!"); + Indent(o, indent) << " <dict>\n"; + Indent(o, indent) << " <key>remove_range</key>\n"; + EmitRange(o, SM, Lexer::getAsCharRange(fixit.RemoveRange, SM, LangOpts), + FM, indent + 2); + Indent(o, indent) << " <key>insert_string</key>"; + EmitString(o, fixit.CodeToInsert); + o << "\n"; + Indent(o, indent) << " </dict>\n"; + } + Indent(o, indent) << "</array>\n"; +} + void PlistPrinter::ReportControlFlow(raw_ostream &o, const PathDiagnosticControlFlowPiece& P, unsigned indent) { @@ -266,6 +300,9 @@ void PlistPrinter::ReportControlFlow(raw_ostream &o, EmitString(o, s) << '\n'; } + assert(P.getFixits().size() == 0 && + "Fixits on constrol flow pieces are not implemented yet!"); + --indent; Indent(o, indent) << "</dict>\n"; } @@ -302,6 +339,9 @@ void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P // Output the text. EmitMessage(o, P.getString(), indent); + // Output the fixits. + EmitFixits(o, P.getFixits(), indent); + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; @@ -329,6 +369,9 @@ void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, if (auto callExit = P.getCallExitEvent()) ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true); + + assert(P.getFixits().size() == 0 && + "Fixits on call pieces are not implemented yet!"); } void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, @@ -341,13 +384,16 @@ void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, I != E; ++I) { ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false); } + + assert(P.getFixits().size() == 0 && + "Fixits on constrol flow pieces are not implemented yet!"); } void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { for (const PathDiagnosticMacroPiece *P : MacroPieces) { const SourceManager &SM = PP.getSourceManager(); - ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP); + ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP, CTU); Indent(o, indent) << "<dict>\n"; ++indent; @@ -398,6 +444,9 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, // Output the text. EmitMessage(o, P.getString(), indent); + // Output the fixits. + EmitFixits(o, P.getFixits(), indent); + // Finish up. --indent; Indent(o, indent); o << "</dict>\n"; @@ -426,6 +475,9 @@ void PlistPrinter::ReportPopUp(raw_ostream &o, // Output the text. EmitMessage(o, P.getString(), indent); + assert(P.getFixits().size() == 0 && + "Fixits on pop-up pieces are not implemented yet!"); + // Finish up. --indent; Indent(o, indent) << "</dict>\n"; @@ -469,20 +521,20 @@ static void printCoverage(const PathDiagnostic *D, } static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, - AnalyzerOptions &AnOpts, - const Preprocessor &PP, + AnalyzerOptions &AnOpts, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU, const PathPieces &Path) { - PlistPrinter Printer(FM, AnOpts, PP); - assert(std::is_partitioned( - Path.begin(), Path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &E) - { return E->getKind() == PathDiagnosticPiece::Note; }) && + PlistPrinter Printer(FM, AnOpts, PP, CTU); + assert(std::is_partitioned(Path.begin(), Path.end(), + [](const PathDiagnosticPieceRef &E) { + return E->getKind() == PathDiagnosticPiece::Note; + }) && "PathDiagnostic is not partitioned so that notes precede the rest"); PathPieces::const_iterator FirstNonNote = std::partition_point( - Path.begin(), Path.end(), - [](const std::shared_ptr<PathDiagnosticPiece> &E) - { return E->getKind() == PathDiagnosticPiece::Note; }); + Path.begin(), Path.end(), [](const PathDiagnosticPieceRef &E) { + return E->getKind() == PathDiagnosticPiece::Note; + }); PathPieces::const_iterator I = Path.begin(); @@ -518,26 +570,29 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, // Methods of PlistDiagnostics. //===----------------------------------------------------------------------===// -PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, - const std::string& output, - const Preprocessor &PP, - bool supportsMultipleFiles) - : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), - SupportsCrossFileDiagnostics(supportsMultipleFiles) {} - -void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string& s, - const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, +PlistDiagnostics::PlistDiagnostics( + AnalyzerOptions &AnalyzerOpts, const std::string &output, + const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU, + bool supportsMultipleFiles) + : OutputFile(output), PP(PP), CTU(CTU), AnOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) { + // FIXME: Will be used by a later planned change. + (void)this->CTU; +} + +void ento::createPlistDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &s, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU, /*supportsMultipleFiles*/ false)); } -void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &s, - const Preprocessor &PP) { - C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, +void ento::createPlistMultiFileDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &s, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, CTU, /*supportsMultipleFiles*/ true)); } void PlistDiagnostics::FlushDiagnosticsImpl( @@ -590,7 +645,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( // Open the file. std::error_code EC; - llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); + llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::OF_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; @@ -614,7 +669,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <dict>\n"; const PathDiagnostic *D = *DI; - printBugPath(o, FM, AnOpts, PP, D->path); + printBugPath(o, FM, AnOpts, PP, CTU, D->path); // Output the bug type and bug category. o << " <key>description</key>"; @@ -624,7 +679,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( o << " <key>type</key>"; EmitString(o, D->getBugType()) << '\n'; o << " <key>check_name</key>"; - EmitString(o, D->getCheckName()) << '\n'; + EmitString(o, D->getCheckerName()) << '\n'; o << " <!-- This hash is experimental and going to change! -->\n"; o << " <key>issue_hash_content_of_line_in_context</key>"; @@ -634,7 +689,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl( : D->getLocation().asLocation()), SM); const Decl *DeclWithIssue = D->getDeclWithIssue(); - EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(), + EmitString(o, GetIssueHash(SM, L, D->getCheckerName(), D->getBugType(), DeclWithIssue, LangOpts)) << '\n'; @@ -867,17 +922,23 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, // Definitions of helper functions and methods for expanding macros. //===----------------------------------------------------------------------===// -static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, - const Preprocessor &PP) { +static ExpansionInfo +getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { + + const Preprocessor *PPToUse = &PP; + if (auto LocAndUnit = CTU.getImportedFromSourceLocation(MacroLoc)) { + MacroLoc = LocAndUnit->first; + PPToUse = &LocAndUnit->second->getPreprocessor(); + } llvm::SmallString<200> ExpansionBuf; llvm::raw_svector_ostream OS(ExpansionBuf); - TokenPrinter Printer(OS, PP); + TokenPrinter Printer(OS, *PPToUse); llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens; - std::string MacroName = - getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}, - AlreadyProcessedTokens); + std::string MacroName = getMacroNameAndPrintExpansion( + Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens); return { MacroName, OS.str() }; } diff --git a/lib/StaticAnalyzer/Core/ProgramState.cpp b/lib/StaticAnalyzer/Core/ProgramState.cpp index a1ca0b1b84bfa..f50d82de3b28d 100644 --- a/lib/StaticAnalyzer/Core/ProgramState.cpp +++ b/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -15,7 +15,7 @@ #include "clang/Basic/JsonSupport.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" -#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp index 64724227395d5..9752a0e22832c 100644 --- a/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -330,7 +330,7 @@ private: std::unique_ptr<ConstraintManager> ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); + return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); } bool RangeConstraintManager::canReasonAbout(SVal X) const { diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index d2aea1fd92dda..5d2ef59e2d66b 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -108,7 +108,7 @@ public: Data == X.Data; } - void dump() const; + LLVM_DUMP_METHOD void dump() const; }; } // end anonymous namespace @@ -135,7 +135,9 @@ static inline raw_ostream &operator<<(raw_ostream &Out, BindingKey K) { } // namespace llvm -LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; } +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) +void BindingKey::dump() const { llvm::errs() << *this; } +#endif //===----------------------------------------------------------------------===// // Actual Store type. @@ -153,28 +155,42 @@ class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> { ClusterBindings::Factory *CBFactory; + // This flag indicates whether the current bindings are within the analysis + // that has started from main(). It affects how we perform loads from + // global variables that have initializers: if we have observed the + // program execution from the start and we know that these variables + // have not been overwritten yet, we can be sure that their initializers + // are still relevant. This flag never gets changed when the bindings are + // updated, so it could potentially be moved into RegionStoreManager + // (as if it's the same bindings but a different loading procedure) + // however that would have made the manager needlessly stateful. + bool IsMainAnalysis; + public: typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> ParentTy; RegionBindingsRef(ClusterBindings::Factory &CBFactory, const RegionBindings::TreeTy *T, - RegionBindings::TreeTy::Factory *F) + RegionBindings::TreeTy::Factory *F, + bool IsMainAnalysis) : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), - CBFactory(&CBFactory) {} + CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {} - RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) + RegionBindingsRef(const ParentTy &P, + ClusterBindings::Factory &CBFactory, + bool IsMainAnalysis) : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), - CBFactory(&CBFactory) {} + CBFactory(&CBFactory), IsMainAnalysis(IsMainAnalysis) {} RegionBindingsRef add(key_type_ref K, data_type_ref D) const { return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D), - *CBFactory); + *CBFactory, IsMainAnalysis); } RegionBindingsRef remove(key_type_ref K) const { return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K), - *CBFactory); + *CBFactory, IsMainAnalysis); } RegionBindingsRef addBinding(BindingKey K, SVal V) const; @@ -204,7 +220,13 @@ public: /// Return the internal tree as a Store. Store asStore() const { - return asImmutableMap().getRootWithoutRetain(); + llvm::PointerIntPair<Store, 1, bool> Ptr = { + asImmutableMap().getRootWithoutRetain(), IsMainAnalysis}; + return reinterpret_cast<Store>(Ptr.getOpaqueValue()); + } + + bool isMainAnalysis() const { + return IsMainAnalysis; } void printJson(raw_ostream &Out, const char *NL = "\n", @@ -379,8 +401,15 @@ public: /// casts from arrays to pointers. SVal ArrayToPointer(Loc Array, QualType ElementTy) override; + /// Creates the Store that correctly represents memory contents before + /// the beginning of the analysis of the given top-level stack frame. StoreRef getInitialStore(const LocationContext *InitLoc) override { - return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this); + bool IsMainAnalysis = false; + if (const auto *FD = dyn_cast<FunctionDecl>(InitLoc->getDecl())) + IsMainAnalysis = FD->isMain() && !Ctx.getLangOpts().CPlusPlus; + return StoreRef(RegionBindingsRef( + RegionBindingsRef::ParentTy(RBFactory.getEmptyMap(), RBFactory), + CBFactory, IsMainAnalysis).asStore(), *this); } //===-------------------------------------------------------------------===// @@ -606,9 +635,13 @@ public: // Part of public interface to class. //===------------------------------------------------------------------===// RegionBindingsRef getRegionBindings(Store store) const { - return RegionBindingsRef(CBFactory, - static_cast<const RegionBindings::TreeTy*>(store), - RBFactory.getTreeFactory()); + llvm::PointerIntPair<Store, 1, bool> Ptr; + Ptr.setFromOpaqueValue(const_cast<void *>(store)); + return RegionBindingsRef( + CBFactory, + static_cast<const RegionBindings::TreeTy *>(Ptr.getPointer()), + RBFactory.getTreeFactory(), + Ptr.getInt()); } void printJson(raw_ostream &Out, Store S, const char *NL = "\n", @@ -642,14 +675,14 @@ public: // Part of public interface to class. std::unique_ptr<StoreManager> ento::CreateRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = maximal_features_tag(); - return llvm::make_unique<RegionStoreManager>(StMgr, F); + return std::make_unique<RegionStoreManager>(StMgr, F); } std::unique_ptr<StoreManager> ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { RegionStoreFeatures F = minimal_features_tag(); F.enableFields(true); - return llvm::make_unique<RegionStoreManager>(StMgr, F); + return std::make_unique<RegionStoreManager>(StMgr, F); } @@ -1669,10 +1702,12 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, return svalBuilder.makeIntVal(c, T); } } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) { - // Check if the containing array is const and has an initialized value. + // Check if the containing array has an initialized value that we can trust. + // We can trust a const value or a value of a global initializer in main(). const VarDecl *VD = VR->getDecl(); - // Either the array or the array element has to be const. - if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) { + if (VD->getType().isConstQualified() || + R->getElementType().isConstQualified() || + (B.isMainAnalysis() && VD->hasGlobalStorage())) { if (const Expr *Init = VD->getAnyInitializer()) { if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { // The array index has to be known. @@ -1761,8 +1796,11 @@ SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, const VarDecl *VD = VR->getDecl(); QualType RecordVarTy = VD->getType(); unsigned Index = FD->getFieldIndex(); - // Either the record variable or the field has to be const qualified. - if (RecordVarTy.isConstQualified() || Ty.isConstQualified()) + // Either the record variable or the field has an initializer that we can + // trust. We trust initializers of constants and, additionally, respect + // initializers of globals when analyzing main(). + if (RecordVarTy.isConstQualified() || Ty.isConstQualified() || + (B.isMainAnalysis() && VD->hasGlobalStorage())) if (const Expr *Init = VD->getAnyInitializer()) if (const auto *InitList = dyn_cast<InitListExpr>(Init)) { if (Index < InitList->getNumInits()) { @@ -1980,6 +2018,12 @@ SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, if (isa<GlobalsSpaceRegion>(MS)) { QualType T = VD->getType(); + // If we're in main(), then global initializers have not become stale yet. + if (B.isMainAnalysis()) + if (const Expr *Init = VD->getAnyInitializer()) + if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + return *V; + // Function-scoped static variables are default-initialized to 0; if they // have an initializer, it would have been processed by now. // FIXME: This is only true when we're starting analysis from main(). @@ -2247,8 +2291,7 @@ RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, const TypedValueRegion* R, SVal V) { QualType T = R->getValueType(); - assert(T->isVectorType()); - const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. + const VectorType *VT = T->castAs<VectorType>(); // Use castAs for typedefs. // Handle lazy compound values and symbolic values. if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) @@ -2333,7 +2376,7 @@ RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, QualType T = R->getValueType(); assert(T->isStructureOrClassType()); - const RecordType* RT = T->getAs<RecordType>(); + const RecordType* RT = T->castAs<RecordType>(); const RecordDecl *RD = RT->getDecl(); if (!RD->isCompleteDefinition()) diff --git a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp index d5c14351d3302..6ad12ca0a688f 100644 --- a/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp +++ b/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp @@ -14,5 +14,5 @@ using namespace ento; std::unique_ptr<ConstraintManager> ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { - return llvm::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); + return std::make_unique<SMTConstraintManager>(Eng, StMgr.getSValBuilder()); } diff --git a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp index d1faf3f4dea9d..190ab7e21dbcc 100644 --- a/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp +++ b/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -10,10 +10,10 @@ // //===----------------------------------------------------------------------===// +#include "clang/Analysis/PathDiagnostic.h" #include "clang/Basic/Version.h" #include "clang/Lex/Preprocessor.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" @@ -43,10 +43,10 @@ public: }; } // end anonymous namespace -void ento::createSarifDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &Output, - const Preprocessor &) { +void ento::createSarifDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &Output, const Preprocessor &, + const cross_tu::CrossTranslationUnitContext &) { C.push_back(new SarifDiagnostics(AnalyzerOpts, Output)); } @@ -106,26 +106,26 @@ static std::string fileNameToURI(StringRef Filename) { return Ret.str().str(); } -static json::Object createFileLocation(const FileEntry &FE) { +static json::Object createArtifactLocation(const FileEntry &FE) { return json::Object{{"uri", fileNameToURI(getFileName(FE))}}; } -static json::Object createFile(const FileEntry &FE) { - return json::Object{{"fileLocation", createFileLocation(FE)}, +static json::Object createArtifact(const FileEntry &FE) { + return json::Object{{"location", createArtifactLocation(FE)}, {"roles", json::Array{"resultFile"}}, {"length", FE.getSize()}, {"mimeType", "text/plain"}}; } -static json::Object createFileLocation(const FileEntry &FE, - json::Array &Files) { +static json::Object createArtifactLocation(const FileEntry &FE, + json::Array &Artifacts) { std::string FileURI = fileNameToURI(getFileName(FE)); - // See if the Files array contains this URI already. If it does not, create - // a new file object to add to the array. - auto I = llvm::find_if(Files, [&](const json::Value &File) { + // See if the Artifacts array contains this URI already. If it does not, + // create a new artifact object to add to the array. + auto I = llvm::find_if(Artifacts, [&](const json::Value &File) { if (const json::Object *Obj = File.getAsObject()) { - if (const json::Object *FileLoc = Obj->getObject("fileLocation")) { + if (const json::Object *FileLoc = Obj->getObject("location")) { Optional<StringRef> URI = FileLoc->getString("uri"); return URI && URI->equals(FileURI); } @@ -133,28 +133,35 @@ static json::Object createFileLocation(const FileEntry &FE, return false; }); - // Calculate the index within the file location array so it can be stored in + // Calculate the index within the artifact array so it can be stored in // the JSON object. - auto Index = static_cast<unsigned>(std::distance(Files.begin(), I)); - if (I == Files.end()) - Files.push_back(createFile(FE)); + auto Index = static_cast<unsigned>(std::distance(Artifacts.begin(), I)); + if (I == Artifacts.end()) + Artifacts.push_back(createArtifact(FE)); - return json::Object{{"uri", FileURI}, {"fileIndex", Index}}; + return json::Object{{"uri", FileURI}, {"index", Index}}; } static json::Object createTextRegion(SourceRange R, const SourceManager &SM) { - return json::Object{ + json::Object Region{ {"startLine", SM.getExpansionLineNumber(R.getBegin())}, - {"endLine", SM.getExpansionLineNumber(R.getEnd())}, {"startColumn", SM.getExpansionColumnNumber(R.getBegin())}, - {"endColumn", SM.getExpansionColumnNumber(R.getEnd())}}; + }; + if (R.getBegin() == R.getEnd()) { + Region["endColumn"] = SM.getExpansionColumnNumber(R.getBegin()); + } else { + Region["endLine"] = SM.getExpansionLineNumber(R.getEnd()); + Region["endColumn"] = SM.getExpansionColumnNumber(R.getEnd()) + 1; + } + return Region; } static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE, const SourceManager &SMgr, - json::Array &Files) { - return json::Object{{{"fileLocation", createFileLocation(FE, Files)}, - {"region", createTextRegion(R, SMgr)}}}; + json::Array &Artifacts) { + return json::Object{ + {{"artifactLocation", createArtifactLocation(FE, Artifacts)}, + {"region", createTextRegion(R, SMgr)}}}; } enum class Importance { Important, Essential, Unimportant }; @@ -207,15 +214,16 @@ static Importance calculateImportance(const PathDiagnosticPiece &Piece) { } static json::Object createThreadFlow(const PathPieces &Pieces, - json::Array &Files) { + json::Array &Artifacts) { const SourceManager &SMgr = Pieces.front()->getLocation().getManager(); json::Array Locations; for (const auto &Piece : Pieces) { const PathDiagnosticLocation &P = Piece->getLocation(); Locations.push_back(createThreadFlowLocation( - createLocation(createPhysicalLocation(P.asRange(), - *P.asLocation().getFileEntry(), - SMgr, Files), + createLocation(createPhysicalLocation( + P.asRange(), + *P.asLocation().getExpansionLoc().getFileEntry(), + SMgr, Artifacts), Piece->getString()), calculateImportance(*Piece))); } @@ -223,35 +231,30 @@ static json::Object createThreadFlow(const PathPieces &Pieces, } static json::Object createCodeFlow(const PathPieces &Pieces, - json::Array &Files) { + json::Array &Artifacts) { return json::Object{ - {"threadFlows", json::Array{createThreadFlow(Pieces, Files)}}}; + {"threadFlows", json::Array{createThreadFlow(Pieces, Artifacts)}}}; } -static json::Object createTool() { - return json::Object{{"name", "clang"}, - {"fullName", "clang static analyzer"}, - {"language", "en-US"}, - {"version", getClangFullVersion()}}; -} - -static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, +static json::Object createResult(const PathDiagnostic &Diag, + json::Array &Artifacts, const StringMap<unsigned> &RuleMapping) { const PathPieces &Path = Diag.path.flatten(false); const SourceManager &SMgr = Path.front()->getLocation().getManager(); - auto Iter = RuleMapping.find(Diag.getCheckName()); + auto Iter = RuleMapping.find(Diag.getCheckerName()); assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?"); return json::Object{ {"message", createMessage(Diag.getVerboseDescription())}, - {"codeFlows", json::Array{createCodeFlow(Path, Files)}}, + {"codeFlows", json::Array{createCodeFlow(Path, Artifacts)}}, {"locations", json::Array{createLocation(createPhysicalLocation( Diag.getLocation().asRange(), - *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}}, + *Diag.getLocation().asLocation().getExpansionLoc().getFileEntry(), + SMgr, Artifacts))}}, {"ruleIndex", Iter->getValue()}, - {"ruleId", Diag.getCheckName()}}; + {"ruleId", Diag.getCheckerName()}}; } static StringRef getRuleDescription(StringRef CheckName) { @@ -277,10 +280,10 @@ static StringRef getRuleHelpURIStr(StringRef CheckName) { } static json::Object createRule(const PathDiagnostic &Diag) { - StringRef CheckName = Diag.getCheckName(); + StringRef CheckName = Diag.getCheckerName(); json::Object Ret{ {"fullDescription", createMessage(getRuleDescription(CheckName))}, - {"name", createMessage(CheckName)}, + {"name", CheckName}, {"id", CheckName}}; std::string RuleURI = getRuleHelpURIStr(CheckName); @@ -296,7 +299,7 @@ static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, llvm::StringSet<> Seen; llvm::for_each(Diags, [&](const PathDiagnostic *D) { - StringRef RuleID = D->getCheckName(); + StringRef RuleID = D->getCheckerName(); std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID); if (P.second) { RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index. @@ -307,24 +310,28 @@ static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, return Rules; } -static json::Object createResources(std::vector<const PathDiagnostic *> &Diags, - StringMap<unsigned> &RuleMapping) { - return json::Object{{"rules", createRules(Diags, RuleMapping)}}; +static json::Object createTool(std::vector<const PathDiagnostic *> &Diags, + StringMap<unsigned> &RuleMapping) { + return json::Object{ + {"driver", json::Object{{"name", "clang"}, + {"fullName", "clang static analyzer"}, + {"language", "en-US"}, + {"version", getClangFullVersion()}, + {"rules", createRules(Diags, RuleMapping)}}}}; } static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) { - json::Array Results, Files; + json::Array Results, Artifacts; StringMap<unsigned> RuleMapping; - json::Object Resources = createResources(Diags, RuleMapping); + json::Object Tool = createTool(Diags, RuleMapping); llvm::for_each(Diags, [&](const PathDiagnostic *D) { - Results.push_back(createResult(*D, Files, RuleMapping)); + Results.push_back(createResult(*D, Artifacts, RuleMapping)); }); - return json::Object{{"tool", createTool()}, - {"resources", std::move(Resources)}, + return json::Object{{"tool", std::move(Tool)}, {"results", std::move(Results)}, - {"files", std::move(Files)}}; + {"artifacts", std::move(Artifacts)}}; } void SarifDiagnostics::FlushDiagnosticsImpl( @@ -335,15 +342,15 @@ void SarifDiagnostics::FlushDiagnosticsImpl( // file can become large very quickly, so decoding into JSON to append a run // may be an expensive operation. std::error_code EC; - llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_Text); if (EC) { llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; return; } json::Object Sarif{ {"$schema", - "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"}, - {"version", "2.0.0-csd.2.beta.2018-11-28"}, + "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json"}, + {"version", "2.1.0"}, {"runs", json::Array{createRun(Diags)}}}; OS << llvm::formatv("{0:2}\n", json::Value(std::move(Sarif))); } diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp index 3cf616161c669..b4ab6877726ce 100644 --- a/lib/StaticAnalyzer/Core/Store.cpp +++ b/lib/StaticAnalyzer/Core/Store.cpp @@ -52,7 +52,7 @@ StoreRef StoreManager::enterStackFrame(Store OldStore, Call.getInitialStackFrameContents(LCtx, InitialBindings); for (const auto &I : InitialBindings) - Store = Bind(Store.getStore(), I.first, I.second); + Store = Bind(Store.getStore(), I.first.castAs<Loc>(), I.second); return Store; } diff --git a/lib/StaticAnalyzer/Core/WorkList.cpp b/lib/StaticAnalyzer/Core/WorkList.cpp index 129d1720395e4..348552ba73a9b 100644 --- a/lib/StaticAnalyzer/Core/WorkList.cpp +++ b/lib/StaticAnalyzer/Core/WorkList.cpp @@ -79,11 +79,11 @@ public: WorkList::~WorkList() = default; std::unique_ptr<WorkList> WorkList::makeDFS() { - return llvm::make_unique<DFS>(); + return std::make_unique<DFS>(); } std::unique_ptr<WorkList> WorkList::makeBFS() { - return llvm::make_unique<BFS>(); + return std::make_unique<BFS>(); } namespace { @@ -124,7 +124,7 @@ namespace { } // namespace std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() { - return llvm::make_unique<BFSBlockDFSContents>(); + return std::make_unique<BFSBlockDFSContents>(); } namespace { @@ -186,7 +186,7 @@ public: } // namespace std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() { - return llvm::make_unique<UnexploredFirstStack>(); + return std::make_unique<UnexploredFirstStack>(); } namespace { @@ -249,7 +249,7 @@ public: } // namespace std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() { - return llvm::make_unique<UnexploredFirstPriorityQueue>(); + return std::make_unique<UnexploredFirstPriorityQueue>(); } namespace { @@ -309,5 +309,5 @@ public: } std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() { - return llvm::make_unique<UnexploredFirstPriorityLocationQueue>(); + return std::make_unique<UnexploredFirstPriorityLocationQueue>(); } diff --git a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp index 454b61fd51a14..8236907ea773b 100644 --- a/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -12,6 +12,7 @@ #include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" #include "ModelInjector.h" +#include "clang/Analysis/PathDiagnostic.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -27,7 +28,6 @@ #include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" -#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" @@ -64,30 +64,30 @@ STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); // Special PathDiagnosticConsumers. //===----------------------------------------------------------------------===// -void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &prefix, - const Preprocessor &PP) { +void ento::createPlistHTMLDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &prefix, const Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { createHTMLDiagnosticConsumer(AnalyzerOpts, C, - llvm::sys::path::parent_path(prefix), PP); - createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP); + llvm::sys::path::parent_path(prefix), PP, CTU); + createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU); } -void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, - PathDiagnosticConsumers &C, - const std::string &Prefix, - const clang::Preprocessor &PP) { +void ento::createTextPathDiagnosticConsumer( + AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C, + const std::string &Prefix, const clang::Preprocessor &PP, + const cross_tu::CrossTranslationUnitContext &CTU) { llvm_unreachable("'text' consumer should be enabled on ClangDiags"); } namespace { class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { DiagnosticsEngine &Diag; - bool IncludePath, ShouldEmitAsError; + bool IncludePath = false, ShouldEmitAsError = false, FixitsAsRemarks = false; public: ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) - : Diag(Diag), IncludePath(false), ShouldEmitAsError(false) {} + : Diag(Diag) {} ~ClangDiagPathDiagConsumer() override {} StringRef getName() const override { return "ClangDiags"; } @@ -98,11 +98,9 @@ public: return IncludePath ? Minimal : None; } - void enablePaths() { - IncludePath = true; - } - + void enablePaths() { IncludePath = true; } void enableWerror() { ShouldEmitAsError = true; } + void enableFixitsAsRemarks() { FixitsAsRemarks = true; } void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, FilesMade *filesMade) override { @@ -111,22 +109,46 @@ public: ? Diag.getCustomDiagID(DiagnosticsEngine::Error, "%0") : Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0"); - - for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(), - E = Diags.end(); I != E; ++I) { + unsigned RemarkID = Diag.getCustomDiagID(DiagnosticsEngine::Remark, "%0"); + + auto reportPiece = + [&](unsigned ID, SourceLocation Loc, StringRef String, + ArrayRef<SourceRange> Ranges, ArrayRef<FixItHint> Fixits) { + if (!FixitsAsRemarks) { + Diag.Report(Loc, ID) << String << Ranges << Fixits; + } else { + Diag.Report(Loc, ID) << String << Ranges; + for (const FixItHint &Hint : Fixits) { + SourceManager &SM = Diag.getSourceManager(); + llvm::SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + // FIXME: Add support for InsertFromRange and + // BeforePreviousInsertion. + assert(!Hint.InsertFromRange.isValid() && "Not implemented yet!"); + assert(!Hint.BeforePreviousInsertions && "Not implemented yet!"); + OS << SM.getSpellingColumnNumber(Hint.RemoveRange.getBegin()) + << "-" << SM.getSpellingColumnNumber(Hint.RemoveRange.getEnd()) + << ": '" << Hint.CodeToInsert << "'"; + Diag.Report(Loc, RemarkID) << OS.str(); + } + } + }; + + for (std::vector<const PathDiagnostic *>::iterator I = Diags.begin(), + E = Diags.end(); + I != E; ++I) { const PathDiagnostic *PD = *I; - SourceLocation WarnLoc = PD->getLocation().asLocation(); - Diag.Report(WarnLoc, WarnID) << PD->getShortDescription() - << PD->path.back()->getRanges(); + reportPiece(WarnID, PD->getLocation().asLocation(), + PD->getShortDescription(), PD->path.back()->getRanges(), + PD->path.back()->getFixits()); // First, add extra notes, even if paths should not be included. for (const auto &Piece : PD->path) { if (!isa<PathDiagnosticNotePiece>(Piece.get())) continue; - SourceLocation NoteLoc = Piece->getLocation().asLocation(); - Diag.Report(NoteLoc, NoteID) << Piece->getString() - << Piece->getRanges(); + reportPiece(NoteID, Piece->getLocation().asLocation(), + Piece->getString(), Piece->getRanges(), Piece->getFixits()); } if (!IncludePath) @@ -138,9 +160,8 @@ public: if (isa<PathDiagnosticNotePiece>(Piece.get())) continue; - SourceLocation NoteLoc = Piece->getLocation().asLocation(); - Diag.Report(NoteLoc, NoteID) << Piece->getString() - << Piece->getRanges(); + reportPiece(NoteID, Piece->getLocation().asLocation(), + Piece->getString(), Piece->getRanges(), Piece->getFixits()); } } } @@ -212,13 +233,13 @@ public: Plugins(plugins), Injector(injector), CTU(CI) { DigestAnalyzerOptions(); if (Opts->PrintStats || Opts->ShouldSerializeStats) { - AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>( + AnalyzerTimers = std::make_unique<llvm::TimerGroup>( "analyzer", "Analyzer timers"); - SyntaxCheckTimer = llvm::make_unique<llvm::Timer>( + SyntaxCheckTimer = std::make_unique<llvm::Timer>( "syntaxchecks", "Syntax-based analysis time", *AnalyzerTimers); - ExprEngineTimer = llvm::make_unique<llvm::Timer>( + ExprEngineTimer = std::make_unique<llvm::Timer>( "exprengine", "Path exploration time", *AnalyzerTimers); - BugReporterTimer = llvm::make_unique<llvm::Timer>( + BugReporterTimer = std::make_unique<llvm::Timer>( "bugreporter", "Path-sensitive report post-processing time", *AnalyzerTimers); llvm::EnableStatistics(/* PrintOnExit= */ false); @@ -241,6 +262,9 @@ public: if (Opts->AnalyzerWerror) clangDiags->enableWerror(); + if (Opts->ShouldEmitFixItHintsAsRemarks) + clangDiags->enableFixitsAsRemarks(); + if (Opts->AnalysisDiagOpt == PD_TEXT) { clangDiags->enablePaths(); @@ -249,7 +273,7 @@ public: default: #define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ case PD_##NAME: \ - CREATEFN(*Opts.get(), PathConsumers, OutDir, PP); \ + CREATEFN(*Opts.get(), PathConsumers, OutDir, PP, CTU); \ break; #include "clang/StaticAnalyzer/Core/Analyses.def" } @@ -311,9 +335,9 @@ public: checkerMgr = createCheckerManager( *Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics()); - Mgr = llvm::make_unique<AnalysisManager>( - *Ctx, PP.getDiagnostics(), PathConsumers, CreateStoreMgr, - CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); + Mgr = std::make_unique<AnalysisManager>(*Ctx, PathConsumers, CreateStoreMgr, + CreateConstraintMgr, + checkerMgr.get(), *Opts, Injector); } /// Store the top level decls in the set to be processed later on. @@ -609,6 +633,7 @@ void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) { // After all decls handled, run checkers on the entire TranslationUnit. checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); + BR.FlushReports(); RecVisitorBR = nullptr; } @@ -626,7 +651,7 @@ void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { if (isBisonFile(C)) { reportAnalyzerProgress("Skipping bison-generated file\n"); - } else if (Opts->DisableAllChecks) { + } else if (Opts->DisableAllCheckers) { // Don't analyze if the user explicitly asked for no checks to be performed // on this file. @@ -766,6 +791,9 @@ void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, if (SyntaxCheckTimer) SyntaxCheckTimer->stopTimer(); } + + BR.FlushReports(); + if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) { RunPathSensitiveChecks(D, IMode, VisitedCallees); if (IMode != ExprEngine::Inline_Minimal) @@ -826,7 +854,7 @@ ento::CreateAnalysisConsumer(CompilerInstance &CI) { AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); bool hasModelPath = analyzerOpts->Config.count("model-path") > 0; - return llvm::make_unique<AnalysisConsumer>( + return std::make_unique<AnalysisConsumer>( CI, CI.getFrontendOpts().OutputFile, analyzerOpts, CI.getFrontendOpts().Plugins, hasModelPath ? new ModelInjector(CI) : nullptr); diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp index 1e45ee96145ab..f4f06e32cd1d7 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -30,7 +30,7 @@ std::unique_ptr<CheckerManager> ento::createCheckerManager( ArrayRef<std::string> plugins, ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns, DiagnosticsEngine &diags) { - auto checkerMgr = llvm::make_unique<CheckerManager>(context, opts); + auto checkerMgr = std::make_unique<CheckerManager>(context, opts); CheckerRegistry allCheckers(plugins, diags, opts, context.getLangOpts(), checkerRegistrationFns); @@ -74,11 +74,22 @@ void ento::printCheckerConfigList(raw_ostream &OS, } void ento::printAnalyzerConfigList(raw_ostream &out) { - out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n"; - out << "USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; - out << " -analyzer-config OPTION1=VALUE, -analyzer-config " - "OPTION2=VALUE, ...\n\n"; - out << "OPTIONS:\n\n"; + // FIXME: This message sounds scary, should be scary, but incorrectly states + // that all configs are super dangerous. In reality, many of them should be + // accessible to the user. We should create a user-facing subset of config + // options under a different frontend flag. + out << R"( +OVERVIEW: Clang Static Analyzer -analyzer-config Option List + +The following list of configurations are meant for development purposes only, as +some of the variables they define are set to result in the most optimal +analysis. Setting them to other values may drastically change how the analyzer +behaves, and may even result in instabilities, crashes! + +USAGE: -analyzer-config <OPTION1=VALUE,OPTION2=VALUE,...> + -analyzer-config OPTION1=VALUE, -analyzer-config OPTION2=VALUE, ... +OPTIONS: +)"; using OptionAndDescriptionTy = std::pair<StringRef, std::string>; OptionAndDescriptionTy PrintableOptions[] = { diff --git a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp index 3fd4c36947cbb..e00fd976f6b80 100644 --- a/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp +++ b/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -200,12 +200,12 @@ CheckerRegistry::CheckerRegistry( // Parse '-analyzer-checker' and '-analyzer-disable-checker' options from the // command line. - for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersControlList) { + for (const std::pair<std::string, bool> &Opt : AnOpts.CheckersAndPackages) { CheckerInfoListRange CheckerForCmdLineArg = getMutableCheckersForCmdLineArg(Opt.first); if (CheckerForCmdLineArg.begin() == CheckerForCmdLineArg.end()) { - Diags.Report(diag::err_unknown_analyzer_checker) << Opt.first; + Diags.Report(diag::err_unknown_analyzer_checker_or_package) << Opt.first; Diags.Report(diag::note_suggest_disabling_all_checkers); } @@ -437,7 +437,7 @@ void CheckerRegistry::initializeManager(CheckerManager &CheckerMgr) const { // Initialize the CheckerManager with all enabled checkers. for (const auto *Checker : enabledCheckers) { - CheckerMgr.setCurrentCheckName(CheckName(Checker->FullName)); + CheckerMgr.setCurrentCheckerName(CheckerNameRef(Checker->FullName)); Checker->Initialize(CheckerMgr); } } @@ -468,9 +468,10 @@ isOptionContainedIn(const CheckerRegistry::CmdLineOptionList &OptionList, void CheckerRegistry::validateCheckerOptions() const { for (const auto &Config : AnOpts.Config) { - StringRef SuppliedChecker; + StringRef SuppliedCheckerOrPackage; StringRef SuppliedOption; - std::tie(SuppliedChecker, SuppliedOption) = Config.getKey().split(':'); + std::tie(SuppliedCheckerOrPackage, SuppliedOption) = + Config.getKey().split(':'); if (SuppliedOption.empty()) continue; @@ -483,21 +484,24 @@ void CheckerRegistry::validateCheckerOptions() const { // Since lower_bound would look for the first element *not less* than "cor", // it would return with an iterator to the first checker in the core, so we // we really have to use find here, which uses operator==. - auto CheckerIt = llvm::find(Checkers, CheckerInfo(SuppliedChecker)); + auto CheckerIt = + llvm::find(Checkers, CheckerInfo(SuppliedCheckerOrPackage)); if (CheckerIt != Checkers.end()) { - isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedChecker, + isOptionContainedIn(CheckerIt->CmdLineOptions, SuppliedCheckerOrPackage, SuppliedOption, AnOpts, Diags); continue; } - auto PackageIt = llvm::find(Packages, PackageInfo(SuppliedChecker)); + auto PackageIt = + llvm::find(Packages, PackageInfo(SuppliedCheckerOrPackage)); if (PackageIt != Packages.end()) { - isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedChecker, + isOptionContainedIn(PackageIt->CmdLineOptions, SuppliedCheckerOrPackage, SuppliedOption, AnOpts, Diags); continue; } - Diags.Report(diag::err_unknown_analyzer_checker) << SuppliedChecker; + Diags.Report(diag::err_unknown_analyzer_checker_or_package) + << SuppliedCheckerOrPackage; } } diff --git a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp index a8af6b3d801a8..04fbd0cea46b6 100644 --- a/lib/StaticAnalyzer/Frontend/FrontendActions.cpp +++ b/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -23,5 +23,5 @@ ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies) std::unique_ptr<ASTConsumer> ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { - return llvm::make_unique<ModelConsumer>(Bodies); + return std::make_unique<ModelConsumer>(Bodies); } diff --git a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp index fe5f59045cde0..687fda75db48e 100644 --- a/lib/StaticAnalyzer/Frontend/ModelInjector.cpp +++ b/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -9,6 +9,7 @@ #include "ModelInjector.h" #include "clang/AST/Decl.h" #include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LangStandard.h" #include "clang/Basic/Stack.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" @@ -65,7 +66,7 @@ void ModelInjector::onBodySynthesis(const NamedDecl *D) { auto Invocation = std::make_shared<CompilerInvocation>(CI.getInvocation()); FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); - InputKind IK = InputKind::CXX; // FIXME + InputKind IK = Language::CXX; // FIXME FrontendOpts.Inputs.clear(); FrontendOpts.Inputs.emplace_back(fileName, IK); FrontendOpts.DisableFree = true; |