27#include "llvm/ADT/StringSet.h"
36 enum Kind { Moved, Reported } K;
37 RegionState(Kind InK) : K(InK) {}
40 bool isReported()
const {
return K == Reported; }
41 bool isMoved()
const {
return K == Moved; }
43 static RegionState getReported() {
return RegionState(Reported); }
44 static RegionState getMoved() {
return RegionState(Moved); }
46 bool operator==(
const RegionState &
X)
const {
return K ==
X.K; }
47 void Profile(llvm::FoldingSetNodeID &ID)
const {
ID.AddInteger(K); }
53 :
public Checker<check::PreCall, check::PostCall, check::DeadSymbols,
54 check::RegionChanges, eval::Call> {
56 void checkPreCall(
const CallEvent &MC, CheckerContext &
C)
const;
57 void checkPostCall(
const CallEvent &MC, CheckerContext &
C)
const;
58 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &
C)
const;
59 bool evalCall(
const CallEvent &
Call, CheckerContext &
C)
const;
63 ArrayRef<const MemRegion *> RequestedRegions,
64 ArrayRef<const MemRegion *> InvalidatedRegions,
65 const StackFrame *SF,
const CallEvent *
Call)
const;
67 const char *NL,
const char *Sep)
const override;
70 enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
71 enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
73 enum AggressivenessKind {
76 AK_KnownsAndLocals = 1,
81 static bool misuseCausesCrash(MisuseKind MK) {
82 return MK == MK_Dereference;
89 StdObjectKind StdKind;
95 const llvm::StringSet<> StdSmartPtrClasses = {
106 const llvm::StringSet<> StdSafeClasses = {
120 bool shouldBeTracked(ObjectKind OK)
const {
134 return (Aggressiveness == AK_All) ||
135 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
136 OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
141 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK)
const {
144 return shouldBeTracked(OK) &&
145 ((Aggressiveness == AK_All) ||
146 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
147 OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
153 const CXXRecordDecl *RD)
const;
158 const MemRegion *MR,
const CXXRecordDecl *RD,
159 MisuseKind MK)
const;
161 bool belongsTo(
const CXXRecordDecl *RD,
const llvm::StringSet<> &
Set)
const;
163 class MovedBugVisitor :
public BugReporterVisitor {
165 MovedBugVisitor(
const MoveChecker &Chk,
const MemRegion *R,
166 const CXXRecordDecl *RD, MisuseKind MK)
167 : Chk(Chk), Region(
R), RD(RD), MK(MK), Found(
false) {}
169 void Profile(llvm::FoldingSetNodeID &ID)
const override {
172 ID.AddPointer(Region);
180 BugReporterContext &BRC,
181 PathSensitiveBugReport &BR)
override;
184 const MoveChecker &Chk;
186 const MemRegion *Region;
188 const CXXRecordDecl *RD;
194 AggressivenessKind Aggressiveness = AK_KnownsAndLocals;
197 void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
199 llvm::StringSwitch<AggressivenessKind>(Str)
200 .Case(
"KnownsOnly", AK_KnownsOnly)
201 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
203 .Default(AK_Invalid);
205 if (Aggressiveness == AK_Invalid)
207 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
214 const CallDescription StdMoveCall{CDM::SimpleFunc, {
"std",
"move"}, 3};
221 const CXXRecordDecl *RD, MisuseKind MK,
222 CheckerContext &
C)
const;
227 ExplodedNode *tryToReportBug(
const MemRegion *Region,
const CXXRecordDecl *RD,
228 CheckerContext &
C, MisuseKind MK)
const;
230 bool isInMoveSafeStackFrame(
const StackFrame *SF)
const;
231 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
232 bool isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const;
233 const ExplodedNode *getMoveLocation(
const ExplodedNode *N,
234 const MemRegion *Region,
235 CheckerContext &
C)
const;
251 const RegionState *RS = State->get<TrackedRegionMap>(Region);
252 return RS && (RS->isMoved() || RS->isReported());
261 bool Strict =
false) {
264 for (
auto &E : State->get<TrackedRegionMap>()) {
265 if ((!Strict || E.first != Region) && E.first->isSubRegionOf(Region))
266 State = State->remove<TrackedRegionMap>(E.first);
273 for (
auto &E : State->get<TrackedRegionMap>()) {
281 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
291MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
300 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
301 const RegionState *TrackedObjectPrev =
302 StatePrev->get<TrackedRegionMap>(Region);
305 if (TrackedObjectPrev && TrackedObject)
314 SmallString<128> Str;
315 llvm::raw_svector_ostream
OS(Str);
317 ObjectKind OK = Chk.classifyObject(State, Region, RD);
318 switch (OK.StdKind) {
320 if (MK == MK_Dereference) {
321 OS <<
"Smart pointer";
322 Chk.explainObject(State,
OS, Region, RD, MK);
323 OS <<
" is reset to null when moved from";
333 Chk.explainObject(State,
OS, Region, RD, MK);
338 Chk.explainObject(State,
OS, Region, RD, MK);
339 OS <<
" is left in a valid but unspecified state after move";
345 return std::make_shared<PathDiagnosticEventPiece>(Pos,
OS.str(),
true);
348const ExplodedNode *MoveChecker::getMoveLocation(
const ExplodedNode *N,
349 const MemRegion *Region,
350 CheckerContext &
C)
const {
353 const ExplodedNode *MoveNode = N;
357 if (!State->get<TrackedRegionMap>(Region))
365void MoveChecker::modelUse(
ProgramStateRef State,
const MemRegion *Region,
366 const CXXRecordDecl *RD, MisuseKind MK,
367 CheckerContext &
C)
const {
368 assert(!
C.isDifferent() &&
"No transitions should have been made by now");
369 const RegionState *RS = State->get<TrackedRegionMap>(Region);
370 ObjectKind OK = classifyObject(State, Region, RD);
374 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
377 if (!RS || !shouldWarnAbout(OK, MK) ||
378 isInMoveSafeStackFrame(
C.getStackFrame())) {
380 C.addTransition(State);
388 if (misuseCausesCrash(MK)) {
389 C.generateSink(State,
C.getPredecessor());
391 C.addTransition(State);
396 ExplodedNode *N = tryToReportBug(Region, RD,
C, MK);
402 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
403 C.addTransition(State, N);
406ExplodedNode *MoveChecker::tryToReportBug(
const MemRegion *Region,
407 const CXXRecordDecl *RD,
409 MisuseKind MK)
const {
410 if (ExplodedNode *N = misuseCausesCrash(MK) ?
C.generateErrorNode()
411 :
C.generateNonFatalErrorNode()) {
413 PathDiagnosticLocation LocUsedForUniqueing;
414 const ExplodedNode *MoveNode = getMoveLocation(N, Region,
C);
421 llvm::SmallString<128> Str;
422 llvm::raw_svector_ostream
OS(Str);
426 OS <<
"Method called on moved-from object";
427 explainObject(State,
OS, Region, RD, MK);
430 OS <<
"Moved-from object";
431 explainObject(State,
OS, Region, RD, MK);
435 OS <<
"Moved-from object";
436 explainObject(State,
OS, Region, RD, MK);
440 OS <<
"Dereference of null smart pointer";
441 explainObject(State,
OS, Region, RD, MK);
445 auto R = std::make_unique<PathSensitiveBugReport>(
446 BT,
OS.str(), N, LocUsedForUniqueing,
448 R->addVisitor(std::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
449 C.emitReport(std::move(R));
455void MoveChecker::checkPostCall(
const CallEvent &
Call,
456 CheckerContext &
C)
const {
457 const auto *AFC = dyn_cast<AnyFunctionCall>(&
Call);
462 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
469 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
470 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
473 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
476 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
481 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&
Call);
482 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
485 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
486 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
489 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
491 if (BaseRegion->
getAs<CXXTempObjectRegion>() ||
492 AFC->getArgExpr(0)->isPRValue())
496 if (State->get<TrackedRegionMap>(ArgRegion))
499 const CXXRecordDecl *RD = MethodDecl->
getParent();
500 ObjectKind OK = classifyObject(State, ArgRegion, RD);
501 if (shouldBeTracked(OK)) {
503 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
504 C.addTransition(State);
507 assert(!
C.isDifferent() &&
"Should not have made transitions on this path!");
510bool MoveChecker::evalCall(
const CallEvent &
Call, CheckerContext &
C)
const {
512 const auto *CE = dyn_cast_if_present<CallExpr>(
Call.getOriginExpr());
525 const MemRegion *ContainerRegion = POS->getContainer();
526 if (!ContainerRegion)
529 const auto *TypedRegion = dyn_cast<TypedValueRegion>(ContainerRegion);
534 QualType ObjTy = TypedRegion->getValueType();
540 ObjectKind OK = classifyObject(State, ContainerRegion, RD);
546 const auto *BackInsCall = dyn_cast<CallExpr>(CE->getArg(2)->IgnoreImpCasts());
550 const Expr *DestExpr = BackInsCall->getArg(0)->IgnoreImpCasts();
554 const auto *DestDRE = dyn_cast<DeclRefExpr>(DestExpr);
558 const auto *DestVD = dyn_cast<VarDecl>(DestDRE->getDecl());
562 const MemRegion *DestRegion =
563 State->getLValue(DestVD,
C.getStackFrame()).getAsRegion();
567 SValBuilder &SVB = State->getStateManager().getSValBuilder();
569 State = State->BindExpr(CE,
C.getStackFrame(), ReturnVal);
571 State = State->invalidateRegions({DestRegion},
Call.getCFGElementRef(),
572 C.blockCount(),
C.getStackFrame(),
575 if (shouldBeTracked(OK))
576 State = State->set<TrackedContentsMap>(ContainerRegion,
577 RegionState::getMoved());
579 C.addTransition(State);
583bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
585 if (
const auto *ConversionDec =
586 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
587 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
595 (MethodDec->
getName().lower() ==
"empty" ||
596 MethodDec->
getName().lower() ==
"isempty"));
599bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
602 if (MethodDec->
hasAttr<ReinitializesAttr>())
605 std::string MethodName = MethodDec->
getName().lower();
608 if (MethodName ==
"assign" || MethodName ==
"clear" ||
609 MethodName ==
"destroy" || MethodName ==
"reset" ||
610 MethodName ==
"resize" || MethodName ==
"shrink")
618bool MoveChecker::isInMoveSafeStackFrame(
const StackFrame *SF)
const {
620 const auto *SFDec = SF->
getDecl();
621 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(SFDec);
622 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(SFDec);
623 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(SFDec);
624 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
627 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
633bool MoveChecker::belongsTo(
const CXXRecordDecl *RD,
634 const llvm::StringSet<> &
Set)
const {
639MoveChecker::ObjectKind
641 const CXXRecordDecl *RD)
const {
647 isa_and_nonnull<VarRegion, CXXLifetimeExtendedObjectRegion>(MR) &&
651 return { IsLocal, SK_NonStd };
653 if (belongsTo(RD, StdSmartPtrClasses))
654 return { IsLocal, SK_SmartPtr };
656 if (belongsTo(RD, StdSafeClasses))
657 return { IsLocal, SK_Safe };
659 return { IsLocal, SK_Unsafe };
663 const MemRegion *MR,
const CXXRecordDecl *RD,
664 MisuseKind MK)
const {
670 OS <<
" '" << RegionDecl->getDeclName() <<
"'";
673 ObjectKind OK = classifyObject(State, MR, RD);
674 switch (OK.StdKind) {
679 if (MK != MK_Dereference)
690void MoveChecker::checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const {
697 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&
Call)) {
699 auto CtorDec = CC->getDecl();
701 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
702 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
703 const CXXRecordDecl *RD = CtorDec->
getParent();
704 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
705 modelUse(State, ArgRegion, RD, MK,
C);
710 const auto IC = dyn_cast<CXXInstanceCall>(&
Call);
714 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
719 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
732 const CXXRecordDecl *RD = MethodDecl->
getParent();
734 if (MethodDecl->getOverloadedOperator() == OO_Star ||
735 MethodDecl->getOverloadedOperator() == OO_Arrow) {
736 SVal Val = IC->getCXXThisVal();
739 const MemRegion *ContainerRegion = POS->getContainer();
740 if (!ContainerRegion)
743 const auto *TypedRegion = dyn_cast<TypedValueRegion>(ContainerRegion);
747 QualType ObjTy = TypedRegion->getValueType();
752 if (State->get<TrackedContentsMap>(ContainerRegion)) {
753 ExplodedNode *N = tryToReportBug(ContainerRegion, R,
C, MK_FunCall);
757 State = State->set<TrackedContentsMap>(ContainerRegion,
758 RegionState::getReported());
759 C.addTransition(State, N);
765 if (isStateResetMethod(MethodDecl)) {
767 C.addTransition(State);
771 if (isMoveSafeMethod(MethodDecl))
774 if (MethodDecl->isOverloadedOperator()) {
777 if (OOK == OO_Equal) {
782 if (MethodDecl->isCopyAssignmentOperator() ||
783 MethodDecl->isMoveAssignmentOperator()) {
784 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
786 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
787 modelUse(State, ArgRegion, RD, MK,
C);
790 C.addTransition(State);
794 if (OOK == OO_Star || OOK == OO_Arrow) {
795 modelUse(State, ThisRegion, RD, MK_Dereference,
C);
800 modelUse(State, ThisRegion, RD, MK_FunCall,
C);
803void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
804 CheckerContext &
C)
const {
806 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
807 for (
auto E : TrackedRegions) {
808 const MemRegion *Region = E.first;
813 State = State->remove<TrackedRegionMap>(Region);
816 C.addTransition(State);
821 ArrayRef<const MemRegion *> RequestedRegions,
822 ArrayRef<const MemRegion *> InvalidatedRegions,
const StackFrame *SF,
823 const CallEvent *
Call)
const {
835 for (
const auto *Region : RequestedRegions) {
836 if (llvm::is_contained(InvalidatedRegions, Region))
843 for (
const auto *Region : InvalidatedRegions)
851 const char *NL,
const char *Sep)
const {
853 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
856 Out << Sep <<
"Moved-from objects :" << NL;
858 I.first->dumpToStream(Out);
859 if (I.second.isMoved())
862 Out <<
": moved and reported";
867void ento::registerMoveChecker(CheckerManager &mgr) {
869 chk->setAggressiveness(
873bool 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, bool Strict=false)
static const MemRegion * unwrapRValueReferenceIndirection(const MemRegion *MR)
Defines an enumeration for C++ overloaded operators.
#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.
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
const Decl * getDecl() const
const StackFrame * getParent() const
It might return null.
bool isBooleanType() const
bool isRValueReferenceType() const
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
bool isVoidPointerType() const
const SourceManager & getSourceManager() const
bool matches(const CallEvent &Call) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
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.
ExplodedNode * getFirstPred()
const StackFrame * getStackFrame() const
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.
DefinedOrUnknownSVal conjureSymbolVal(const void *symbolTag, ConstCFGElementRef elem, const StackFrame *SF, unsigned count)
Create a new symbol with a unique 'name'.
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
const IteratorPosition * getIteratorPosition(ProgramStateRef State, SVal Val)
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)