23 #include "llvm/ADT/StringSet.h" 25 using namespace clang;
31 enum Kind { Moved, Reported } K;
32 RegionState(
Kind InK) : K(InK) {}
35 bool isReported()
const {
return K == Reported; }
36 bool isMoved()
const {
return K == Moved; }
38 static RegionState getReported() {
return RegionState(Reported); }
39 static RegionState getMoved() {
return RegionState(Moved); }
41 bool operator==(
const RegionState &
X)
const {
return K == X.K; }
42 void Profile(llvm::FoldingSetNodeID &
ID)
const { ID.AddInteger(K); }
48 :
public Checker<check::PreCall, check::PostCall,
49 check::DeadSymbols, check::RegionChanges> {
51 void checkEndFunction(
const ReturnStmt *RS, CheckerContext &C)
const;
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;
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);
147 ObjectKind classifyObject(
const MemRegion *MR,
const CXXRecordDecl *RD)
const;
151 void explainObject(llvm::raw_ostream &
OS,
const MemRegion *MR,
154 bool belongsTo(
const CXXRecordDecl *RD,
const llvm::StringSet<> &Set)
const;
158 MovedBugVisitor(
const MoveChecker &Chk,
const MemRegion *R,
160 : Chk(Chk), Region(R), RD(RD), MK(MK), Found(
false) {}
162 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
165 ID.AddPointer(Region);
172 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
173 BugReporterContext &BRC,
174 BugReport &BR)
override;
177 const MoveChecker &Chk;
179 const MemRegion *Region;
187 AggressivenessKind Aggressiveness;
190 void setAggressiveness(StringRef Str) {
192 llvm::StringSwitch<AggressivenessKind>(Str)
193 .Case(
"KnownsOnly", AK_KnownsOnly)
194 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
196 .Default(AK_KnownsAndLocals);
200 mutable std::unique_ptr<BugType> BT;
208 CheckerContext &C)
const;
212 ExplodedNode *reportBug(
const MemRegion *Region,
const CXXRecordDecl *RD,
213 CheckerContext &C, MisuseKind MK)
const;
216 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
218 const ExplodedNode *getMoveLocation(
const ExplodedNode *N,
219 const MemRegion *Region,
220 CheckerContext &C)
const;
228 const MemRegion *Region) {
231 for (
auto &E : State->get<TrackedRegionMap>()) {
232 if (E.first->isSubRegionOf(Region))
233 State = State->remove<TrackedRegionMap>(E.first);
239 const MemRegion *Region) {
240 for (
auto &E : State->get<TrackedRegionMap>()) {
241 if (Region->isSubRegionOf(E.first) && E.second.isReported())
248 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
250 if (Sym->getType()->isRValueReferenceType())
251 if (
const MemRegion *OriginMR = Sym->getOriginRegion())
257 std::shared_ptr<PathDiagnosticPiece>
258 MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
259 BugReporterContext &BRC, BugReport &BR) {
266 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
267 const RegionState *TrackedObjectPrev =
268 StatePrev->get<TrackedRegionMap>(Region);
271 if (TrackedObjectPrev && TrackedObject)
281 llvm::raw_svector_ostream
OS(Str);
283 ObjectKind OK = Chk.classifyObject(Region, RD);
284 switch (OK.StdKind) {
286 if (MK == MK_Dereference) {
287 OS <<
"Smart pointer";
288 Chk.explainObject(OS, Region, RD, MK);
289 OS <<
" is reset to null when moved from";
299 Chk.explainObject(OS, Region, RD, MK);
304 Chk.explainObject(OS, Region, RD, MK);
305 OS <<
" is left in a valid but unspecified state after move";
310 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
311 N->getLocationContext());
312 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
true);
315 const ExplodedNode *MoveChecker::getMoveLocation(
const ExplodedNode *N,
316 const MemRegion *Region,
317 CheckerContext &C)
const {
320 const ExplodedNode *MoveNode = N;
324 if (!State->get<TrackedRegionMap>(Region))
327 N = N->pred_empty() ? nullptr : *(N->pred_begin());
334 CheckerContext &C)
const {
335 assert(!C.isDifferent() &&
"No transitions should have been made by now");
336 const RegionState *RS = State->get<TrackedRegionMap>(Region);
337 ObjectKind OK = classifyObject(Region, RD);
341 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
344 if (!RS || !shouldWarnAbout(OK, MK)
345 || isInMoveSafeContext(C.getLocationContext())) {
347 C.addTransition(State);
355 if (misuseCausesCrash(MK)) {
356 C.generateSink(State, C.getPredecessor());
358 C.addTransition(State);
363 ExplodedNode *N = reportBug(Region, RD, C, MK);
369 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
370 C.addTransition(State, N);
373 ExplodedNode *MoveChecker::reportBug(
const MemRegion *Region,
375 MisuseKind MK)
const {
376 if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
377 : C.generateNonFatalErrorNode()) {
380 BT.reset(
new BugType(
this,
"Use-after-move",
381 "C++ move semantics"));
384 PathDiagnosticLocation LocUsedForUniqueing;
385 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
389 MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
393 llvm::raw_svector_ostream
OS(Str);
396 OS <<
"Method called on moved-from object";
397 explainObject(OS, Region, RD, MK);
400 OS <<
"Moved-from object";
401 explainObject(OS, Region, RD, MK);
405 OS <<
"Moved-from object";
406 explainObject(OS, Region, RD, MK);
410 OS <<
"Dereference of null smart pointer";
411 explainObject(OS, Region, RD, MK);
416 llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing,
417 MoveNode->getLocationContext()->getDecl());
418 R->addVisitor(llvm::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
419 C.emitReport(std::move(R));
425 void MoveChecker::checkPostCall(
const CallEvent &Call,
426 CheckerContext &C)
const {
432 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
440 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
443 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
446 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
451 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
452 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
455 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
456 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
459 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
461 if (BaseRegion->getAs<CXXTempObjectRegion>() ||
462 AFC->getArgExpr(0)->isRValue())
466 if (State->get<TrackedRegionMap>(ArgRegion))
470 ObjectKind OK = classifyObject(ArgRegion, RD);
471 if (shouldBeTracked(OK)) {
473 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
474 C.addTransition(State);
477 assert(!C.isDifferent() &&
"Should not have made transitions on this path!");
480 bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
482 if (
const auto *ConversionDec =
483 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
484 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
492 (MethodDec->
getName().lower() ==
"empty" ||
493 MethodDec->
getName().lower() ==
"isempty"));
496 bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
499 if (MethodDec->
hasAttr<ReinitializesAttr>())
502 std::string MethodName = MethodDec->
getName().lower();
505 if (MethodName ==
"reset" || MethodName ==
"clear" ||
506 MethodName ==
"destroy" || MethodName ==
"resize" ||
507 MethodName ==
"shrink")
515 bool MoveChecker::isInMoveSafeContext(
const LocationContext *LC)
const {
517 const auto *CtxDec = LC->
getDecl();
518 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
519 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
520 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
521 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
522 (MethodDec && MethodDec->isOverloadedOperator() &&
523 MethodDec->getOverloadedOperator() == OO_Equal) ||
524 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
531 const llvm::StringSet<> &Set)
const {
533 return II && Set.count(II->
getName());
536 MoveChecker::ObjectKind
537 MoveChecker::classifyObject(
const MemRegion *MR,
544 MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
547 return { IsLocal, SK_NonStd };
549 if (belongsTo(RD, StdSmartPtrClasses))
550 return { IsLocal, SK_SmartPtr };
552 if (belongsTo(RD, StdSafeClasses))
553 return { IsLocal, SK_Safe };
555 return { IsLocal, SK_Unsafe };
558 void MoveChecker::explainObject(llvm::raw_ostream &
OS,
const MemRegion *MR,
564 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
565 OS <<
" '" << RegionDecl->getNameAsString() <<
"'";
568 ObjectKind OK = classifyObject(MR, RD);
569 switch (OK.StdKind) {
574 if (MK != MK_Dereference)
585 void MoveChecker::checkPreCall(
const CallEvent &Call, CheckerContext &C)
const {
592 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
594 auto CtorDec = CC->getDecl();
596 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
597 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
599 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
600 modelUse(State, ArgRegion, RD, MK, C);
610 if (isa<CXXDestructorCall>(IC))
613 const MemRegion *ThisRegion = IC->
getCXXThisVal().getAsRegion();
618 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
624 ThisRegion = ThisRegion->getMostDerivedObjectRegion();
626 if (isStateResetMethod(MethodDecl)) {
628 C.addTransition(State);
632 if (isMoveSafeMethod(MethodDecl))
638 if (MethodDecl->isOverloadedOperator()) {
641 if (OOK == OO_Equal) {
646 if (MethodDecl->isCopyAssignmentOperator() ||
647 MethodDecl->isMoveAssignmentOperator()) {
648 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
650 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
651 modelUse(State, ArgRegion, RD, MK, C);
654 C.addTransition(State);
658 if (OOK == OO_Star || OOK == OO_Arrow) {
659 modelUse(State, ThisRegion, RD, MK_Dereference, C);
664 modelUse(State, ThisRegion, RD, MK_FunCall, C);
667 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
668 CheckerContext &C)
const {
670 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
671 for (TrackedRegionMapTy::value_type E : TrackedRegions) {
672 const MemRegion *Region = E.first;
673 bool IsRegDead = !SymReaper.isLiveRegion(Region);
677 State = State->remove<TrackedRegionMap>(Region);
680 C.addTransition(State);
694 const MemRegion *ThisRegion =
nullptr;
695 if (
const auto *IC = dyn_cast<CXXInstanceCall>(Call))
696 ThisRegion = IC->getCXXThisVal().getAsRegion();
701 for (
const auto *Region : RequestedRegions) {
702 if (ThisRegion != Region) {
703 if (llvm::find(InvalidatedRegions, Region) !=
704 std::end(InvalidatedRegions)) {
712 for (
const auto *Region : InvalidatedRegions)
720 const char *NL,
const char *Sep)
const {
722 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
725 Out << Sep <<
"Moved-from objects :" << NL;
727 I.first->dumpToStream(Out);
728 if (I.second.isMoved())
731 Out <<
": moved and reported";
736 void ento::registerMoveChecker(CheckerManager &mgr) {
737 MoveChecker *chk = mgr.registerChecker<MoveChecker>();
738 chk->setAggressiveness(
739 mgr.getAnalyzerOptions().getCheckerStringOption(
"WarnOn",
"", chk));
bool operator==(CanQual< T > x, CanQual< U > y)
llvm::DenseSet< SymbolRef > InvalidatedSymbols
const SymExpr * SymbolRef
Stmt - This represents one statement.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
The base class of the type hierarchy.
static ProgramStateRef removeFromState(ProgramStateRef State, const MemRegion *Region)
Represents a C++ constructor within a class.
Defines the clang::Expr interface and subclasses for C++ expressions.
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
One of these records is kept for each identifier that is lexed.
const LocationContext * getParent() const
Represents a non-static C++ member function call, no matter how it is written.
DeclContext * getDeclContext()
virtual SVal getCXXThisVal() const
Returns the value of the implicit 'this' object.
static bool isAnyBaseRegionReported(ProgramStateRef State, const MemRegion *Region)
bool isIdentifier() const
Predicate functions for querying what type of name this is.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
ReturnStmt - This represents a return, optionally of an expression: return; return 4;...
bool isVoidPointerType() const
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
static const MemRegion * unwrapRValueReferenceIndirection(const MemRegion *MR)
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Represents a static or instance method of a struct/union/class.
bool isStdNamespace() const
StringRef getName() const
Return the actual identifier string.
Dataflow Directional Tag Classes.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isBooleanType() const
const Decl * getDecl() const
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
Represents a C++ struct/union/class.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
std::string getQualifiedNameAsString() const