24#include "llvm/ADT/StringSet.h"
32 enum Kind { Moved, Reported } K;
33 RegionState(Kind InK) : K(InK) {}
36 bool isReported()
const {
return K == Reported; }
37 bool isMoved()
const {
return K == Moved; }
39 static RegionState getReported() {
return RegionState(Reported); }
40 static RegionState getMoved() {
return RegionState(Moved); }
42 bool operator==(
const RegionState &
X)
const {
return K ==
X.K; }
43 void Profile(llvm::FoldingSetNodeID &ID)
const {
ID.AddInteger(K); }
49 :
public Checker<check::PreCall, check::PostCall,
50 check::DeadSymbols, check::RegionChanges> {
52 void checkPreCall(
const CallEvent &MC, CheckerContext &
C)
const;
53 void checkPostCall(
const CallEvent &MC, CheckerContext &
C)
const;
54 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &
C)
const;
58 ArrayRef<const MemRegion *> RequestedRegions,
59 ArrayRef<const MemRegion *> InvalidatedRegions,
60 const LocationContext *LCtx,
const CallEvent *
Call)
const;
62 const char *NL,
const char *Sep)
const override;
65 enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
66 enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
68 enum AggressivenessKind {
71 AK_KnownsAndLocals = 1,
76 static bool misuseCausesCrash(MisuseKind MK) {
77 return MK == MK_Dereference;
84 StdObjectKind StdKind;
90 const llvm::StringSet<> StdSmartPtrClasses = {
101 const llvm::StringSet<> StdSafeClasses = {
115 bool shouldBeTracked(ObjectKind OK)
const {
129 return (Aggressiveness == AK_All) ||
130 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
131 OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
136 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK)
const {
139 return shouldBeTracked(OK) &&
140 ((Aggressiveness == AK_All) ||
141 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
142 OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
148 const CXXRecordDecl *RD)
const;
153 const MemRegion *MR,
const CXXRecordDecl *RD,
154 MisuseKind MK)
const;
156 bool belongsTo(
const CXXRecordDecl *RD,
const llvm::StringSet<> &
Set)
const;
158 class MovedBugVisitor :
public BugReporterVisitor {
160 MovedBugVisitor(
const MoveChecker &Chk,
const MemRegion *R,
161 const CXXRecordDecl *RD, MisuseKind MK)
162 : Chk(Chk), Region(R), RD(RD), MK(MK), Found(
false) {}
164 void Profile(llvm::FoldingSetNodeID &ID)
const override {
167 ID.AddPointer(Region);
175 BugReporterContext &BRC,
176 PathSensitiveBugReport &BR)
override;
179 const MoveChecker &Chk;
181 const MemRegion *Region;
183 const CXXRecordDecl *RD;
189 AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
192 void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
194 llvm::StringSwitch<AggressivenessKind>(Str)
195 .Case(
"KnownsOnly", AK_KnownsOnly)
196 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
198 .Default(AK_Invalid);
200 if (Aggressiveness == AK_Invalid)
202 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
213 const CXXRecordDecl *RD, MisuseKind MK,
214 CheckerContext &
C)
const;
219 ExplodedNode *tryToReportBug(
const MemRegion *Region,
const CXXRecordDecl *RD,
220 CheckerContext &
C, MisuseKind MK)
const;
222 bool isInMoveSafeContext(
const LocationContext *LC)
const;
223 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
224 bool isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const;
225 const ExplodedNode *getMoveLocation(
const ExplodedNode *N,
226 const MemRegion *Region,
227 CheckerContext &
C)
const;
238 const RegionState *RS = State->get<TrackedRegionMap>(Region);
239 return RS && (RS->isMoved() || RS->isReported());
250 for (
auto &E : State->get<TrackedRegionMap>()) {
251 if (E.first->isSubRegionOf(Region))
252 State = State->remove<TrackedRegionMap>(E.first);
259 for (
auto &E : State->get<TrackedRegionMap>()) {
267 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
277MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
286 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
287 const RegionState *TrackedObjectPrev =
288 StatePrev->get<TrackedRegionMap>(Region);
291 if (TrackedObjectPrev && TrackedObject)
300 SmallString<128> Str;
301 llvm::raw_svector_ostream
OS(Str);
303 ObjectKind OK = Chk.classifyObject(State, Region, RD);
304 switch (OK.StdKind) {
306 if (MK == MK_Dereference) {
307 OS <<
"Smart pointer";
308 Chk.explainObject(State,
OS, Region, RD, MK);
309 OS <<
" is reset to null when moved from";
319 Chk.explainObject(State,
OS, Region, RD, MK);
324 Chk.explainObject(State,
OS, Region, RD, MK);
325 OS <<
" is left in a valid but unspecified state after move";
332 return std::make_shared<PathDiagnosticEventPiece>(Pos,
OS.str(),
true);
335const ExplodedNode *MoveChecker::getMoveLocation(
const ExplodedNode *N,
336 const MemRegion *Region,
337 CheckerContext &
C)
const {
340 const ExplodedNode *MoveNode = N;
344 if (!State->get<TrackedRegionMap>(Region))
352void MoveChecker::modelUse(
ProgramStateRef State,
const MemRegion *Region,
353 const CXXRecordDecl *RD, MisuseKind MK,
354 CheckerContext &
C)
const {
355 assert(!
C.isDifferent() &&
"No transitions should have been made by now");
356 const RegionState *RS = State->get<TrackedRegionMap>(Region);
357 ObjectKind OK = classifyObject(State, Region, RD);
361 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
364 if (!RS || !shouldWarnAbout(OK, MK)
365 || isInMoveSafeContext(
C.getLocationContext())) {
367 C.addTransition(State);
375 if (misuseCausesCrash(MK)) {
376 C.generateSink(State,
C.getPredecessor());
378 C.addTransition(State);
383 ExplodedNode *N = tryToReportBug(Region, RD,
C, MK);
389 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
390 C.addTransition(State, N);
393ExplodedNode *MoveChecker::tryToReportBug(
const MemRegion *Region,
394 const CXXRecordDecl *RD,
396 MisuseKind MK)
const {
397 if (ExplodedNode *N = misuseCausesCrash(MK) ?
C.generateErrorNode()
398 :
C.generateNonFatalErrorNode()) {
400 PathDiagnosticLocation LocUsedForUniqueing;
401 const ExplodedNode *MoveNode = getMoveLocation(N, Region,
C);
408 llvm::SmallString<128> Str;
409 llvm::raw_svector_ostream
OS(Str);
413 OS <<
"Method called on moved-from object";
414 explainObject(State,
OS, Region, RD, MK);
417 OS <<
"Moved-from object";
418 explainObject(State,
OS, Region, RD, MK);
422 OS <<
"Moved-from object";
423 explainObject(State,
OS, Region, RD, MK);
427 OS <<
"Dereference of null smart pointer";
428 explainObject(State,
OS, Region, RD, MK);
432 auto R = std::make_unique<PathSensitiveBugReport>(
433 BT,
OS.str(), N, LocUsedForUniqueing,
435 R->addVisitor(std::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
436 C.emitReport(std::move(R));
442void MoveChecker::checkPostCall(
const CallEvent &
Call,
443 CheckerContext &
C)
const {
444 const auto *AFC = dyn_cast<AnyFunctionCall>(&
Call);
449 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
456 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
457 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
460 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
463 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
468 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&
Call);
469 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
472 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
473 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
476 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
478 if (BaseRegion->
getAs<CXXTempObjectRegion>() ||
479 AFC->getArgExpr(0)->isPRValue())
483 if (State->get<TrackedRegionMap>(ArgRegion))
486 const CXXRecordDecl *RD = MethodDecl->
getParent();
487 ObjectKind OK = classifyObject(State, ArgRegion, RD);
488 if (shouldBeTracked(OK)) {
490 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
491 C.addTransition(State);
494 assert(!
C.isDifferent() &&
"Should not have made transitions on this path!");
497bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
499 if (
const auto *ConversionDec =
500 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
501 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
509 (MethodDec->
getName().lower() ==
"empty" ||
510 MethodDec->
getName().lower() ==
"isempty"));
513bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
516 if (MethodDec->
hasAttr<ReinitializesAttr>())
519 std::string MethodName = MethodDec->
getName().lower();
522 if (MethodName ==
"assign" || MethodName ==
"clear" ||
523 MethodName ==
"destroy" || MethodName ==
"reset" ||
524 MethodName ==
"resize" || MethodName ==
"shrink")
532bool MoveChecker::isInMoveSafeContext(
const LocationContext *LC)
const {
534 const auto *CtxDec = LC->
getDecl();
535 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
536 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
537 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
538 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
541 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
547bool MoveChecker::belongsTo(
const CXXRecordDecl *RD,
548 const llvm::StringSet<> &
Set)
const {
553MoveChecker::ObjectKind
555 const CXXRecordDecl *RD)
const {
561 isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
565 return { IsLocal, SK_NonStd };
567 if (belongsTo(RD, StdSmartPtrClasses))
568 return { IsLocal, SK_SmartPtr };
570 if (belongsTo(RD, StdSafeClasses))
571 return { IsLocal, SK_Safe };
573 return { IsLocal, SK_Unsafe };
577 const MemRegion *MR,
const CXXRecordDecl *RD,
578 MisuseKind MK)
const {
584 OS <<
" '" << RegionDecl->getDeclName() <<
"'";
587 ObjectKind OK = classifyObject(State, MR, RD);
588 switch (OK.StdKind) {
593 if (MK != MK_Dereference)
604void MoveChecker::checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const {
611 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&
Call)) {
613 auto CtorDec = CC->getDecl();
615 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
616 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
617 const CXXRecordDecl *RD = CtorDec->
getParent();
618 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
619 modelUse(State, ArgRegion, RD, MK,
C);
624 const auto IC = dyn_cast<CXXInstanceCall>(&
Call);
628 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
633 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
645 if (isStateResetMethod(MethodDecl)) {
647 C.addTransition(State);
651 if (isMoveSafeMethod(MethodDecl))
655 const CXXRecordDecl *RD = MethodDecl->
getParent();
657 if (MethodDecl->isOverloadedOperator()) {
660 if (OOK == OO_Equal) {
665 if (MethodDecl->isCopyAssignmentOperator() ||
666 MethodDecl->isMoveAssignmentOperator()) {
667 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
669 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
670 modelUse(State, ArgRegion, RD, MK,
C);
673 C.addTransition(State);
677 if (OOK == OO_Star || OOK == OO_Arrow) {
678 modelUse(State, ThisRegion, RD, MK_Dereference,
C);
683 modelUse(State, ThisRegion, RD, MK_FunCall,
C);
686void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
687 CheckerContext &
C)
const {
689 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
690 for (
auto E : TrackedRegions) {
691 const MemRegion *Region = E.first;
696 State = State->remove<TrackedRegionMap>(Region);
699 C.addTransition(State);
704 ArrayRef<const MemRegion *> RequestedRegions,
705 ArrayRef<const MemRegion *> InvalidatedRegions,
706 const LocationContext *LCtx,
const CallEvent *
Call)
const {
713 const MemRegion *ThisRegion =
nullptr;
714 if (
const auto *IC = dyn_cast<CXXInstanceCall>(
Call))
715 ThisRegion = IC->getCXXThisVal().getAsRegion();
720 for (
const auto *Region : RequestedRegions) {
721 if (ThisRegion != Region &&
722 llvm::is_contained(InvalidatedRegions, Region))
728 for (
const auto *Region : InvalidatedRegions)
736 const char *NL,
const char *Sep)
const {
738 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
741 Out << Sep <<
"Moved-from objects :" << NL;
743 I.first->dumpToStream(Out);
744 if (I.second.isMoved())
747 Out <<
": moved and reported";
752void ento::registerMoveChecker(CheckerManager &mgr) {
754 chk->setAggressiveness(
758bool ento::shouldRegisterMoveChecker(
const CheckerManager &mgr) {
Defines the clang::Expr interface and subclasses for C++ expressions.
static bool isAnyBaseRegionReported(ProgramStateRef State, const MemRegion *Region)
static ProgramStateRef removeFromState(ProgramStateRef State, const MemRegion *Region)
static const MemRegion * unwrapRValueReferenceIndirection(const MemRegion *MR)
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
StringRef getCheckerStringOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Query an option's string value.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
bool isStdNamespace() const
DeclContext * getDeclContext()
bool isIdentifier() const
Predicate functions for querying what type of name this is.
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
StringRef getName() const
Return the actual identifier string.
const Decl * getDecl() const
const LocationContext * getParent() const
It might return null.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
std::string getQualifiedNameAsString() const
bool isBooleanType() const
bool isRValueReferenceType() const
bool isVoidPointerType() const
const SourceManager & getSourceManager() const
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
void reportInvalidCheckerOptionValue(const CheckerFrontend *Checker, StringRef OptionName, StringRef ExpectedValueDesc) const
Emits an error through a DiagnosticsEngine about an invalid user supplied checker option value.
Simple checker classes that implement one frontend (i.e.
const ProgramStateRef & getState() const
pred_iterator pred_begin()
const Stmt * getStmtForDiagnostics() const
If the node's program point corresponds to a statement, retrieve that statement.
const LocationContext * getLocationContext() const
ExplodedNode * getFirstPred()
MemRegion - The root abstract class for all memory regions.
bool hasMemorySpace(ProgramStateRef State) const
virtual bool isSubRegionOf(const MemRegion *R) const
Check if the region is a subregion of the given region.
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
const RegionTy * getAs() const
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getMostDerivedObjectRegion() const
Recursively retrieve the region of the most derived class instance of regions of C++ base class insta...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
virtual const MemRegion * getOriginRegion() const
Find the region from which this symbol originates.
virtual QualType getType() const =0
bool isLiveRegion(const MemRegion *region)
const char *const CXXMoveSemantics
bool isMovedFrom(ProgramStateRef State, const MemRegion *Region)
Returns true if the object is known to have been recently std::moved.
llvm::DenseSet< SymbolRef > InvalidatedSymbols
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const SymExpr * SymbolRef
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
The JSON file list parser is used to communicate input to InstallAPI.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isa(CodeGen::Address addr)
bool operator==(const CallGraphNode::CallRecord &LHS, const CallGraphNode::CallRecord &RHS)
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
@ Type
The name was classified as a type.
U cast(CodeGen::Address addr)