std::shared_ptr<PathDiagnosticPiece> SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { if (Satisfied) return nullptr; ProgramStateRef State = Succ->getState(); bool CalledNow = Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); bool CalledBefore = Pred->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); // Is Succ the node on which the analyzer noted that [super dealloc] was // called on ReceiverSymbol? if (CalledNow && !CalledBefore) { Satisfied = true; ProgramPoint P = Succ->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( L, "[super dealloc] called here"); } return nullptr; }
std::shared_ptr<PathDiagnosticPiece> InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &) { if (!isSymbolTracked(N->getState(), PtrToBuf) || isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) return nullptr; const Stmt *S = PathDiagnosticLocation::getStmt(N); if (!S) return nullptr; const MemRegion *ObjRegion = allocation_state::getContainerObjRegion(N->getState(), PtrToBuf); const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); QualType ObjTy = TypedRegion->getValueType(); SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); OS << "Pointer to inner buffer of '" << ObjTy.getAsString() << "' obtained here"; PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, nullptr); }
std::shared_ptr<PathDiagnosticPiece> MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { // We need only the last move of the reported object's region. // The visitor walks the ExplodedGraph backwards. if (Found) return nullptr; ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = N->getFirstPred()->getState(); const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); const RegionState *TrackedObjectPrev = StatePrev->get<TrackedRegionMap>(Region); if (!TrackedObject) return nullptr; if (TrackedObjectPrev && TrackedObject) return nullptr; // Retrieve the associated statement. const Stmt *S = PathDiagnosticLocation::getStmt(N); if (!S) return nullptr; Found = true; SmallString<128> Str; llvm::raw_svector_ostream OS(Str); ObjectKind OK = Chk.classifyObject(Region, RD); switch (OK.StdKind) { case SK_SmartPtr: if (MK == MK_Dereference) { OS << "Smart pointer"; Chk.explainObject(OS, Region, RD, MK); OS << " is reset to null when moved from"; break; } // If it's not a dereference, we don't care if it was reset to null // or that it is even a smart pointer. LLVM_FALLTHROUGH; case SK_NonStd: case SK_Safe: OS << "Object"; Chk.explainObject(OS, Region, RD, MK); OS << " is moved"; break; case SK_Unsafe: OS << "Object"; Chk.explainObject(OS, Region, RD, MK); OS << " is left in a valid but unspecified state after move"; break; } // Generate the extra diagnostic. PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); }
void clang::bugreporter::registerFindLastStore(BugReporterContext& BRC, const void *data, const ExplodedNode* N) { const MemRegion *R = static_cast<const MemRegion*>(data); if (!R) return; const GRState *state = N->getState(); SVal V = state->getSVal(R); if (V.isUnknown()) return; BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); }
void clang::bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, const void *data, const ExplodedNode* N) { const Stmt *S = static_cast<const Stmt*>(data); if (!S) return; GRStateManager &StateMgr = BRC.getStateManager(); const GRState *state = N->getState(); if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) { if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { const VarRegion *R = StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); // What did we load? SVal V = state->getSVal(S); if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V) || V.isUndef()) { ::registerFindLastStore(BRC, R, V); } } } SVal V = state->getSValAsScalarOrLoc(S); // Uncomment this to find cases where we aren't properly getting the // base value that was dereferenced. // assert(!V.isUnknownOrUndef()); // Is it a symbolic value? if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) { const SubRegion *R = cast<SubRegion>(L->getRegion()); while (R && !isa<SymbolicRegion>(R)) { R = dyn_cast<SubRegion>(R->getSuperRegion()); } if (R) { assert(isa<SymbolicRegion>(R)); registerTrackConstraint(BRC, loc::MemRegionVal(R), false); } } }
std::shared_ptr<PathDiagnosticPiece> DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { // Stop traversal after the first conversion was found on a path. if (Satisfied) return nullptr; ProgramStateRef State = N->getState(); const LocationContext *LC = N->getLocationContext(); const Stmt *S = PathDiagnosticLocation::getStmt(N); if (!S) return nullptr; const auto *CastE = dyn_cast<CastExpr>(S); if (!CastE) return nullptr; // Only interested in DerivedToBase implicit casts. // Explicit casts can have different CastKinds. if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { if (ImplCastE->getCastKind() != CK_DerivedToBase) return nullptr; } // Region associated with the current cast expression. const MemRegion *M = State->getSVal(CastE, LC).getAsRegion(); if (!M) return nullptr; // Check if target region was marked as problematic previously. if (!BR.isInteresting(M)) return nullptr; // Stop traversal on this path. Satisfied = true; SmallString<256> Buf; llvm::raw_svector_ostream OS(Buf); 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); }
std::shared_ptr<PathDiagnosticPiece> MisusedMovedObjectChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { // We need only the last move of the reported object's region. // The visitor walks the ExplodedGraph backwards. if (Found) return nullptr; ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = PrevN->getState(); const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); const RegionState *TrackedObjectPrev = StatePrev->get<TrackedRegionMap>(Region); if (!TrackedObject) return nullptr; if (TrackedObjectPrev && TrackedObject) return nullptr; // Retrieve the associated statement. const Stmt *S = PathDiagnosticLocation::getStmt(N); if (!S) return nullptr; Found = true; std::string ObjectName; if (const auto DecReg = Region->getAs<DeclRegion>()) { const auto *RegionDecl = dyn_cast<NamedDecl>(DecReg->getDecl()); ObjectName = RegionDecl->getNameAsString(); } std::string InfoText; if (ObjectName != "") InfoText = "'" + ObjectName + "' became 'moved-from' here"; else InfoText = "Became 'moved-from' here"; // Generate the extra diagnostic. PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); }
std::shared_ptr<PathDiagnosticPiece> GenericTaintChecker::TaintBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { // Find the ExplodedNode where the taint was first introduced if (!N->getState()->isTainted(V) || PrevN->getState()->isTainted(V)) return nullptr; const Stmt *S = PathDiagnosticLocation::getStmt(N); if (!S) return nullptr; const LocationContext *NCtx = N->getLocationContext(); PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( L, "Taint originated here"); }
std::shared_ptr<PathDiagnosticPiece> DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, const ExplodedNode *Pred, BugReporterContext &BRC, BugReport &BR) { if (Satisfied) return nullptr; const Expr *E = nullptr; if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { BinaryOperator::Opcode Op = BO->getOpcode(); if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || Op == BO_RemAssign) { E = BO->getRHS(); } } if (!E) return nullptr; SVal S = Succ->getSVal(E); if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) { Satisfied = true; // Construct a new PathDiagnosticPiece. ProgramPoint P = Succ->getLocation(); PathDiagnosticLocation L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); if (!L.isValid() || !L.asLocation().isValid()) return nullptr; return std::make_shared<PathDiagnosticEventPiece>( L, "Division with compared value made here"); } return nullptr; }
std::shared_ptr<PathDiagnosticPiece> NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC, BugReport &BR) { ProgramStateRef State = N->getState(); ProgramStateRef StatePrev = PrevN->getState(); const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region); const NullabilityState *TrackedNullabPrev = StatePrev->get<NullabilityMap>(Region); if (!TrackedNullab) return nullptr; if (TrackedNullabPrev && TrackedNullabPrev->getValue() == TrackedNullab->getValue()) return nullptr; // Retrieve the associated statement. const Stmt *S = TrackedNullab->getNullabilitySource(); if (!S || S->getLocStart().isInvalid()) { S = PathDiagnosticLocation::getStmt(N); } if (!S) return nullptr; std::string InfoText = (llvm::Twine("Nullability '") + getNullabilityString(TrackedNullab->getValue()) + "' is inferred") .str(); // Generate the extra diagnostic. PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true, nullptr); }
static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, SVal V) { BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); }
void clang::bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) { BRC.addVisitor(new NilReceiverVisitor()); }
static void registerTrackConstraint(BugReporterContext& BRC, DefinedSVal Constraint, bool Assumption) { BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); }
std::shared_ptr<PathDiagnosticPiece> RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, const ExplodedNode *EndN, BugReport &BR) { // Tell the BugReporterContext to report cases when the tracked symbol is // assigned to different variables, etc. BR.markInteresting(Sym); // We are reporting a leak. Walk up the graph to get to the first node where // the symbol appeared, and also get the first VarDecl that tracked object // is stored to. AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); 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); std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "Object leaked: "; Optional<std::string> RegionDescription = describeRegion(FirstBinding); if (RegionDescription) { os << "object allocated and stored into '" << *RegionDescription << '\''; } else { os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) << "'"; } // Get the retain count. const RefVal* RV = getRefBinding(EndN->getState(), Sym); assert(RV); if (RV->getKind() == RefVal::ErrorLeakReturned) { // FIXME: Per comments in rdar://6320065, "create" only applies to CF // objects. Only "copy", "alloc", "retain" and "new" transfer ownership // to the caller for NS objects. const Decl *D = &EndN->getCodeDecl(); os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " : " is returned from a function "); if (D->hasAttr<CFReturnsNotRetainedAttr>()) { os << "that is annotated as CF_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { os << "that is annotated as NS_RETURNS_NOT_RETAINED"; } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { os << "that is annotated as OS_RETURNS_NOT_RETAINED"; } else { if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { os << "managed by Automatic Reference Counting"; } else { os << "whose name ('" << MD->getSelector().getAsString() << "') does not start with " "'copy', 'mutableCopy', 'alloc' or 'new'." " This violates the naming convention rules" " given in the Memory Management Guide for Cocoa"; } } else { const FunctionDecl *FD = cast<FunctionDecl>(D); os << "whose name ('" << *FD << "') does not contain 'Copy' or 'Create'. This violates the naming" " convention rules given in the Memory Management Guide for Core" " Foundation"; } } } else { os << " is not referenced later in this execution path and has a retain " "count of +" << RV->getCount(); } return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); }
std::shared_ptr<PathDiagnosticPiece> RefCountReportVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { const SourceManager &SM = BRC.getSourceManager(); CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); if (auto CE = N->getLocationAs<CallExitBegin>()) if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) return PD; // FIXME: We will eventually need to handle non-statement-based events // (__attribute__((cleanup))). if (!N->getLocation().getAs<StmtPoint>()) return nullptr; // Check if the type state has changed. const ExplodedNode *PrevNode = N->getFirstPred(); ProgramStateRef PrevSt = PrevNode->getState(); ProgramStateRef CurrSt = N->getState(); const LocationContext *LCtx = N->getLocationContext(); const RefVal* CurrT = getRefBinding(CurrSt, Sym); if (!CurrT) return nullptr; const RefVal &CurrV = *CurrT; const RefVal *PrevT = getRefBinding(PrevSt, Sym); // Create a string buffer to constain all the useful things we want // to tell the user. std::string sbuf; llvm::raw_string_ostream os(sbuf); // This is the allocation site since the previous node had no bindings // for this symbol. if (!PrevT) { const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); if (isa<ObjCIvarRefExpr>(S) && isSynthesizedAccessor(LCtx->getStackFrame())) { S = LCtx->getStackFrame()->getCallSite(); } if (isa<ObjCArrayLiteral>(S)) { os << "NSArray literal is an object with a +0 retain count"; } else if (isa<ObjCDictionaryLiteral>(S)) { os << "NSDictionary literal is an object with a +0 retain count"; } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { if (isNumericLiteralExpression(BL->getSubExpr())) os << "NSNumber literal is an object with a +0 retain count"; else { const ObjCInterfaceDecl *BoxClass = nullptr; if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) BoxClass = Method->getClassInterface(); // We should always be able to find the boxing class interface, // but consider this future-proofing. if (BoxClass) { os << *BoxClass << " b"; } else { os << "B"; } os << "oxed expression produces an object with a +0 retain count"; } } else if (isa<ObjCIvarRefExpr>(S)) { os << "Object loaded from instance variable"; } else { generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); } PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); } // Gather up the effects that were performed on the object at this // program point bool DeallocSent = false; if (N->getLocation().getTag() && N->getLocation().getTag()->getTagDescription().contains( RetainCountChecker::DeallocTagDescription)) { // We only have summaries attached to nodes after evaluating CallExpr and // ObjCMessageExprs. const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { // Iterate through the parameter expressions and see if the symbol // was ever passed as an argument. unsigned i = 0; for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { // Retrieve the value of the argument. Is it the symbol // we are interested in? if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) continue; // We have an argument. Get the effect! DeallocSent = true; } } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { if (const Expr *receiver = ME->getInstanceReceiver()) { if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) .getAsLocSymbol() == Sym) { // The symbol we are tracking is the receiver. DeallocSent = true; } } } } if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent)) return nullptr; if (os.str().empty()) return nullptr; // We have nothing to say! const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); PathDiagnosticLocation Pos(S, BRC.getSourceManager(), N->getLocationContext()); auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); // Add the range by scanning the children of the statement for any bindings // to Sym. for (const Stmt *Child : S->children()) if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { P->addRange(Exp->getSourceRange()); break; } return std::move(P); }