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, CheckerManager &Mgr) {
192 llvm::StringSwitch<AggressivenessKind>(Str)
193 .Case(
"KnownsOnly", AK_KnownsOnly)
194 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
196 .Default(AK_Invalid);
198 if (Aggressiveness == AK_Invalid)
199 Mgr.reportInvalidCheckerOptionValue(
this,
"WarnOn",
200 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
204 mutable std::unique_ptr<BugType> BT;
212 CheckerContext &C)
const;
216 ExplodedNode *reportBug(
const MemRegion *Region,
const CXXRecordDecl *RD,
217 CheckerContext &C, MisuseKind MK)
const;
220 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
222 const ExplodedNode *getMoveLocation(
const ExplodedNode *N,
223 const MemRegion *Region,
224 CheckerContext &C)
const;
235 const RegionState *RS = State->get<TrackedRegionMap>(Region);
236 return RS && (RS->isMoved() || RS->isReported());
244 const MemRegion *Region) {
247 for (
auto &E : State->get<TrackedRegionMap>()) {
248 if (E.first->isSubRegionOf(Region))
249 State = State->remove<TrackedRegionMap>(E.first);
255 const MemRegion *Region) {
256 for (
auto &E : State->get<TrackedRegionMap>()) {
257 if (Region->isSubRegionOf(E.first) && E.second.isReported())
264 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
266 if (Sym->getType()->isRValueReferenceType())
267 if (
const MemRegion *OriginMR = Sym->getOriginRegion())
273 std::shared_ptr<PathDiagnosticPiece>
274 MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
275 BugReporterContext &BRC, BugReport &BR) {
282 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
283 const RegionState *TrackedObjectPrev =
284 StatePrev->get<TrackedRegionMap>(Region);
287 if (TrackedObjectPrev && TrackedObject)
297 llvm::raw_svector_ostream
OS(Str);
299 ObjectKind OK = Chk.classifyObject(Region, RD);
300 switch (OK.StdKind) {
302 if (MK == MK_Dereference) {
303 OS <<
"Smart pointer";
304 Chk.explainObject(OS, Region, RD, MK);
305 OS <<
" is reset to null when moved from";
315 Chk.explainObject(OS, Region, RD, MK);
320 Chk.explainObject(OS, Region, RD, MK);
321 OS <<
" is left in a valid but unspecified state after move";
326 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
327 N->getLocationContext());
328 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
true);
331 const ExplodedNode *MoveChecker::getMoveLocation(
const ExplodedNode *N,
332 const MemRegion *Region,
333 CheckerContext &C)
const {
336 const ExplodedNode *MoveNode = N;
340 if (!State->get<TrackedRegionMap>(Region))
343 N = N->pred_empty() ? nullptr : *(N->pred_begin());
350 CheckerContext &C)
const {
351 assert(!C.isDifferent() &&
"No transitions should have been made by now");
352 const RegionState *RS = State->get<TrackedRegionMap>(Region);
353 ObjectKind OK = classifyObject(Region, RD);
357 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
360 if (!RS || !shouldWarnAbout(OK, MK)
361 || isInMoveSafeContext(C.getLocationContext())) {
363 C.addTransition(State);
371 if (misuseCausesCrash(MK)) {
372 C.generateSink(State, C.getPredecessor());
374 C.addTransition(State);
379 ExplodedNode *N = reportBug(Region, RD, C, MK);
385 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
386 C.addTransition(State, N);
389 ExplodedNode *MoveChecker::reportBug(
const MemRegion *Region,
391 MisuseKind MK)
const {
392 if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
393 : C.generateNonFatalErrorNode()) {
396 BT.reset(
new BugType(
this,
"Use-after-move",
397 "C++ move semantics"));
400 PathDiagnosticLocation LocUsedForUniqueing;
401 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
405 MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
409 llvm::raw_svector_ostream
OS(Str);
412 OS <<
"Method called on moved-from object";
413 explainObject(OS, Region, RD, MK);
416 OS <<
"Moved-from object";
417 explainObject(OS, Region, RD, MK);
421 OS <<
"Moved-from object";
422 explainObject(OS, Region, RD, MK);
426 OS <<
"Dereference of null smart pointer";
427 explainObject(OS, Region, RD, MK);
432 llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing,
433 MoveNode->getLocationContext()->getDecl());
434 R->addVisitor(llvm::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
435 C.emitReport(std::move(R));
441 void MoveChecker::checkPostCall(
const CallEvent &Call,
442 CheckerContext &C)
const {
448 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
456 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
459 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
462 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
467 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
468 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
471 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
472 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
475 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
477 if (BaseRegion->getAs<CXXTempObjectRegion>() ||
478 AFC->getArgExpr(0)->isRValue())
482 if (State->get<TrackedRegionMap>(ArgRegion))
486 ObjectKind OK = classifyObject(ArgRegion, RD);
487 if (shouldBeTracked(OK)) {
489 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
490 C.addTransition(State);
493 assert(!C.isDifferent() &&
"Should not have made transitions on this path!");
496 bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
498 if (
const auto *ConversionDec =
499 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
500 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
508 (MethodDec->
getName().lower() ==
"empty" ||
509 MethodDec->
getName().lower() ==
"isempty"));
512 bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
515 if (MethodDec->
hasAttr<ReinitializesAttr>())
518 std::string MethodName = MethodDec->
getName().lower();
521 if (MethodName ==
"assign" || MethodName ==
"clear" ||
522 MethodName ==
"destroy" || MethodName ==
"reset" ||
523 MethodName ==
"resize" || MethodName ==
"shrink")
531 bool MoveChecker::isInMoveSafeContext(
const LocationContext *LC)
const {
533 const auto *CtxDec = LC->
getDecl();
534 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
535 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
536 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
537 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
538 (MethodDec && MethodDec->isOverloadedOperator() &&
539 MethodDec->getOverloadedOperator() == OO_Equal) ||
540 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
547 const llvm::StringSet<> &Set)
const {
549 return II && Set.count(II->
getName());
552 MoveChecker::ObjectKind
553 MoveChecker::classifyObject(
const MemRegion *MR,
560 MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
563 return { IsLocal, SK_NonStd };
565 if (belongsTo(RD, StdSmartPtrClasses))
566 return { IsLocal, SK_SmartPtr };
568 if (belongsTo(RD, StdSafeClasses))
569 return { IsLocal, SK_Safe };
571 return { IsLocal, SK_Unsafe };
574 void MoveChecker::explainObject(llvm::raw_ostream &
OS,
const MemRegion *MR,
580 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
581 OS <<
" '" << RegionDecl->getNameAsString() <<
"'";
584 ObjectKind OK = classifyObject(MR, RD);
585 switch (OK.StdKind) {
590 if (MK != MK_Dereference)
601 void MoveChecker::checkPreCall(
const CallEvent &Call, CheckerContext &C)
const {
608 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
610 auto CtorDec = CC->getDecl();
612 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
613 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
615 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
616 modelUse(State, ArgRegion, RD, MK, C);
626 if (isa<CXXDestructorCall>(IC))
629 const MemRegion *ThisRegion = IC->
getCXXThisVal().getAsRegion();
634 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
640 ThisRegion = ThisRegion->getMostDerivedObjectRegion();
642 if (isStateResetMethod(MethodDecl)) {
644 C.addTransition(State);
648 if (isMoveSafeMethod(MethodDecl))
654 if (MethodDecl->isOverloadedOperator()) {
657 if (OOK == OO_Equal) {
662 if (MethodDecl->isCopyAssignmentOperator() ||
663 MethodDecl->isMoveAssignmentOperator()) {
664 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
666 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
667 modelUse(State, ArgRegion, RD, MK, C);
670 C.addTransition(State);
674 if (OOK == OO_Star || OOK == OO_Arrow) {
675 modelUse(State, ThisRegion, RD, MK_Dereference, C);
680 modelUse(State, ThisRegion, RD, MK_FunCall, C);
683 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
684 CheckerContext &C)
const {
686 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
687 for (TrackedRegionMapTy::value_type E : TrackedRegions) {
688 const MemRegion *Region = E.first;
689 bool IsRegDead = !SymReaper.isLiveRegion(Region);
693 State = State->remove<TrackedRegionMap>(Region);
696 C.addTransition(State);
710 const MemRegion *ThisRegion =
nullptr;
711 if (
const auto *IC = dyn_cast<CXXInstanceCall>(Call))
712 ThisRegion = IC->getCXXThisVal().getAsRegion();
717 for (
const auto *Region : RequestedRegions) {
718 if (ThisRegion != Region) {
719 if (llvm::find(InvalidatedRegions, Region) !=
720 std::end(InvalidatedRegions)) {
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";
752 void ento::registerMoveChecker(CheckerManager &mgr) {
753 MoveChecker *chk = mgr.registerChecker<MoveChecker>();
754 chk->setAggressiveness(
755 mgr.getAnalyzerOptions().getCheckerStringOption(chk,
"WarnOn"), mgr);
758 bool ento::shouldRegisterMoveChecker(
const LangOptions &LO) {
MemRegion - The root abstract class for all memory regions.
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.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
const LocationContext * getParent() const
bool isMovedFrom(ProgramStateRef State, const MemRegion *Region)
Returns true if the object is known to have been recently std::moved.
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