24 #include "llvm/ADT/StringSet.h" 26 using namespace clang;
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 checkEndFunction(
const ReturnStmt *RS, CheckerContext &C)
const;
53 void checkPreCall(
const CallEvent &MC, CheckerContext &C)
const;
54 void checkPostCall(
const CallEvent &MC, CheckerContext &C)
const;
55 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C)
const;
63 const char *NL,
const char *Sep)
const override;
66 enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
67 enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
69 enum AggressivenessKind {
72 AK_KnownsAndLocals = 1,
77 static bool misuseCausesCrash(MisuseKind MK) {
78 return MK == MK_Dereference;
85 StdObjectKind StdKind;
91 const llvm::StringSet<> StdSmartPtrClasses = {
102 const llvm::StringSet<> StdSafeClasses = {
116 bool shouldBeTracked(ObjectKind OK)
const {
130 return (Aggressiveness == AK_All) ||
131 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
132 OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
137 bool shouldWarnAbout(ObjectKind OK, MisuseKind MK)
const {
140 return shouldBeTracked(OK) &&
141 ((Aggressiveness == AK_All) ||
142 (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
143 OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
148 ObjectKind classifyObject(
const MemRegion *MR,
const CXXRecordDecl *RD)
const;
152 void explainObject(llvm::raw_ostream &
OS,
const MemRegion *MR,
155 bool belongsTo(
const CXXRecordDecl *RD,
const llvm::StringSet<> &Set)
const;
159 MovedBugVisitor(
const MoveChecker &Chk,
const MemRegion *R,
161 : Chk(Chk), Region(R), RD(RD), MK(MK), Found(
false) {}
163 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
166 ID.AddPointer(Region);
174 BugReporterContext &BRC,
175 PathSensitiveBugReport &BR)
override;
178 const MoveChecker &Chk;
180 const MemRegion *Region;
188 AggressivenessKind Aggressiveness;
191 void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
193 llvm::StringSwitch<AggressivenessKind>(Str)
194 .Case(
"KnownsOnly", AK_KnownsOnly)
195 .Case(
"KnownsAndLocals", AK_KnownsAndLocals)
197 .Default(AK_Invalid);
199 if (Aggressiveness == AK_Invalid)
200 Mgr.reportInvalidCheckerOptionValue(
this,
"WarnOn",
201 "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
205 mutable std::unique_ptr<BugType> BT;
213 CheckerContext &C)
const;
217 ExplodedNode *reportBug(
const MemRegion *Region,
const CXXRecordDecl *RD,
218 CheckerContext &C, MisuseKind MK)
const;
221 bool isStateResetMethod(
const CXXMethodDecl *MethodDec)
const;
223 const ExplodedNode *getMoveLocation(
const ExplodedNode *N,
224 const MemRegion *Region,
225 CheckerContext &C)
const;
236 const RegionState *RS = State->get<TrackedRegionMap>(Region);
237 return RS && (RS->isMoved() || RS->isReported());
245 const MemRegion *Region) {
248 for (
auto &E : State->get<TrackedRegionMap>()) {
249 if (E.first->isSubRegionOf(Region))
250 State = State->remove<TrackedRegionMap>(E.first);
256 const MemRegion *Region) {
257 for (
auto &E : State->get<TrackedRegionMap>()) {
258 if (Region->isSubRegionOf(E.first) && E.second.isReported())
265 if (
const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
267 if (Sym->getType()->isRValueReferenceType())
268 if (
const MemRegion *OriginMR = Sym->getOriginRegion())
275 MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
276 BugReporterContext &BRC,
277 PathSensitiveBugReport &BR) {
284 const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
285 const RegionState *TrackedObjectPrev =
286 StatePrev->get<TrackedRegionMap>(Region);
289 if (TrackedObjectPrev && TrackedObject)
293 const Stmt *S = N->getStmtForDiagnostics();
299 llvm::raw_svector_ostream
OS(Str);
301 ObjectKind OK = Chk.classifyObject(Region, RD);
302 switch (OK.StdKind) {
304 if (MK == MK_Dereference) {
305 OS <<
"Smart pointer";
306 Chk.explainObject(OS, Region, RD, MK);
307 OS <<
" is reset to null when moved from";
317 Chk.explainObject(OS, Region, RD, MK);
322 Chk.explainObject(OS, Region, RD, MK);
323 OS <<
" is left in a valid but unspecified state after move";
328 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
329 N->getLocationContext());
330 return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(),
true);
333 const ExplodedNode *MoveChecker::getMoveLocation(
const ExplodedNode *N,
334 const MemRegion *Region,
335 CheckerContext &C)
const {
338 const ExplodedNode *MoveNode = N;
342 if (!State->get<TrackedRegionMap>(Region))
345 N = N->pred_empty() ? nullptr : *(N->pred_begin());
352 CheckerContext &C)
const {
353 assert(!C.isDifferent() &&
"No transitions should have been made by now");
354 const RegionState *RS = State->get<TrackedRegionMap>(Region);
355 ObjectKind OK = classifyObject(Region, RD);
359 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
362 if (!RS || !shouldWarnAbout(OK, MK)
363 || isInMoveSafeContext(C.getLocationContext())) {
365 C.addTransition(State);
373 if (misuseCausesCrash(MK)) {
374 C.generateSink(State, C.getPredecessor());
376 C.addTransition(State);
381 ExplodedNode *N = reportBug(Region, RD, C, MK);
387 State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
388 C.addTransition(State, N);
391 ExplodedNode *MoveChecker::reportBug(
const MemRegion *Region,
393 MisuseKind MK)
const {
394 if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
395 : C.generateNonFatalErrorNode()) {
398 BT.reset(
new BugType(
this,
"Use-after-move",
399 "C++ move semantics"));
402 PathDiagnosticLocation LocUsedForUniqueing;
403 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
405 if (
const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
407 MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
411 llvm::raw_svector_ostream
OS(Str);
414 OS <<
"Method called on moved-from object";
415 explainObject(OS, Region, RD, MK);
418 OS <<
"Moved-from object";
419 explainObject(OS, Region, RD, MK);
423 OS <<
"Moved-from object";
424 explainObject(OS, Region, RD, MK);
428 OS <<
"Dereference of null smart pointer";
429 explainObject(OS, Region, RD, MK);
433 auto R = std::make_unique<PathSensitiveBugReport>(
434 *BT, OS.str(), N, LocUsedForUniqueing,
435 MoveNode->getLocationContext()->getDecl());
436 R->addVisitor(std::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
437 C.emitReport(std::move(R));
443 void MoveChecker::checkPostCall(
const CallEvent &Call,
444 CheckerContext &C)
const {
450 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
458 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
461 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
464 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
469 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
470 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
473 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
474 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
477 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
479 if (BaseRegion->getAs<CXXTempObjectRegion>() ||
480 AFC->getArgExpr(0)->isRValue())
484 if (State->get<TrackedRegionMap>(ArgRegion))
488 ObjectKind OK = classifyObject(ArgRegion, RD);
489 if (shouldBeTracked(OK)) {
491 State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
492 C.addTransition(State);
495 assert(!C.isDifferent() &&
"Should not have made transitions on this path!");
498 bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
500 if (
const auto *ConversionDec =
501 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
502 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
510 (MethodDec->
getName().lower() ==
"empty" ||
511 MethodDec->
getName().lower() ==
"isempty"));
514 bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
517 if (MethodDec->
hasAttr<ReinitializesAttr>())
520 std::string MethodName = MethodDec->
getName().lower();
523 if (MethodName ==
"assign" || MethodName ==
"clear" ||
524 MethodName ==
"destroy" || MethodName ==
"reset" ||
525 MethodName ==
"resize" || MethodName ==
"shrink")
533 bool MoveChecker::isInMoveSafeContext(
const LocationContext *LC)
const {
535 const auto *CtxDec = LC->
getDecl();
536 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
537 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
538 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
539 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
540 (MethodDec && MethodDec->isOverloadedOperator() &&
541 MethodDec->getOverloadedOperator() == OO_Equal) ||
542 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
549 const llvm::StringSet<> &Set)
const {
551 return II && Set.count(II->
getName());
554 MoveChecker::ObjectKind
555 MoveChecker::classifyObject(
const MemRegion *MR,
562 MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
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 };
576 void MoveChecker::explainObject(llvm::raw_ostream &
OS,
const MemRegion *MR,
582 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
583 OS <<
" '" << RegionDecl->getNameAsString() <<
"'";
586 ObjectKind OK = classifyObject(MR, RD);
587 switch (OK.StdKind) {
592 if (MK != MK_Dereference)
603 void MoveChecker::checkPreCall(
const CallEvent &Call, CheckerContext &C)
const {
610 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
612 auto CtorDec = CC->getDecl();
614 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
615 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
617 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
618 modelUse(State, ArgRegion, RD, MK, C);
628 if (isa<CXXDestructorCall>(IC))
631 const MemRegion *ThisRegion = IC->
getCXXThisVal().getAsRegion();
636 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
642 ThisRegion = ThisRegion->getMostDerivedObjectRegion();
644 if (isStateResetMethod(MethodDecl)) {
646 C.addTransition(State);
650 if (isMoveSafeMethod(MethodDecl))
656 if (MethodDecl->isOverloadedOperator()) {
659 if (OOK == OO_Equal) {
664 if (MethodDecl->isCopyAssignmentOperator() ||
665 MethodDecl->isMoveAssignmentOperator()) {
666 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
668 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
669 modelUse(State, ArgRegion, RD, MK, C);
672 C.addTransition(State);
676 if (OOK == OO_Star || OOK == OO_Arrow) {
677 modelUse(State, ThisRegion, RD, MK_Dereference, C);
682 modelUse(State, ThisRegion, RD, MK_FunCall, C);
685 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
686 CheckerContext &C)
const {
688 TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
689 for (
auto E : TrackedRegions) {
690 const MemRegion *Region = E.first;
691 bool IsRegDead = !SymReaper.isLiveRegion(Region);
695 State = State->remove<TrackedRegionMap>(Region);
698 C.addTransition(State);
712 const MemRegion *ThisRegion =
nullptr;
713 if (
const auto *IC = dyn_cast<CXXInstanceCall>(Call))
714 ThisRegion = IC->getCXXThisVal().getAsRegion();
719 for (
const auto *Region : RequestedRegions) {
720 if (ThisRegion != Region) {
721 if (llvm::find(InvalidatedRegions, Region) !=
722 std::end(InvalidatedRegions)) {
730 for (
const auto *Region : InvalidatedRegions)
738 const char *NL,
const char *Sep)
const {
740 TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
743 Out << Sep <<
"Moved-from objects :" << NL;
745 I.first->dumpToStream(Out);
746 if (I.second.isMoved())
749 Out <<
": moved and reported";
754 void ento::registerMoveChecker(CheckerManager &mgr) {
755 MoveChecker *chk = mgr.registerChecker<MoveChecker>();
756 chk->setAggressiveness(
757 mgr.getAnalyzerOptions().getCheckerStringOption(chk,
"WarnOn"), mgr);
760 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
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 SymExpr * SymbolRef
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
std::shared_ptr< PathDiagnosticPiece > PathDiagnosticPieceRef
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 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