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 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;
156 class MovedBugVisitor :
public BugReporterVisitor {
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);
173 BugReporterContext &BRC,
174 PathSensitiveBugReport &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");
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())
274 MoveChecker::MovedBugVisitor::VisitNode(
const ExplodedNode *N,
275 BugReporterContext &BRC,
276 PathSensitiveBugReport &BR) {
283 const RegionState *TrackedObject =
State->get<TrackedRegionMap>(Region);
284 const RegionState *TrackedObjectPrev =
285 StatePrev->get<TrackedRegionMap>(Region);
288 if (TrackedObjectPrev && TrackedObject)
292 const Stmt *S = N->getStmtForDiagnostics();
298 llvm::raw_svector_ostream
OS(Str);
300 ObjectKind OK = Chk.classifyObject(Region, RD);
301 switch (OK.StdKind) {
303 if (MK == MK_Dereference) {
304 OS <<
"Smart pointer";
305 Chk.explainObject(OS, Region, RD, MK);
306 OS <<
" is reset to null when moved from";
316 Chk.explainObject(OS, Region, RD, MK);
321 Chk.explainObject(OS, Region, RD, MK);
322 OS <<
" is left in a valid but unspecified state after move";
327 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
328 N->getLocationContext());
329 return std::make_shared<PathDiagnosticEventPiece>(Pos,
OS.str(),
true);
332 const ExplodedNode *MoveChecker::getMoveLocation(
const ExplodedNode *N,
333 const MemRegion *Region,
334 CheckerContext &C)
const {
337 const ExplodedNode *MoveNode = N;
341 if (!
State->get<TrackedRegionMap>(Region))
344 N = N->pred_empty() ? nullptr : *(N->pred_begin());
351 CheckerContext &C)
const {
352 assert(!
C.isDifferent() &&
"No transitions should have been made by now");
353 const RegionState *RS =
State->get<TrackedRegionMap>(Region);
354 ObjectKind OK = classifyObject(Region, RD);
358 if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
361 if (!RS || !shouldWarnAbout(OK, MK)
362 || isInMoveSafeContext(
C.getLocationContext())) {
372 if (misuseCausesCrash(MK)) {
373 C.generateSink(
State,
C.getPredecessor());
380 ExplodedNode *N = reportBug(Region, RD, C, MK);
386 State =
State->set<TrackedRegionMap>(Region, RegionState::getReported());
387 C.addTransition(
State, N);
390 ExplodedNode *MoveChecker::reportBug(
const MemRegion *Region,
392 MisuseKind MK)
const {
393 if (ExplodedNode *N = misuseCausesCrash(MK) ?
C.generateErrorNode()
394 :
C.generateNonFatalErrorNode()) {
396 PathDiagnosticLocation LocUsedForUniqueing;
397 const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
399 if (
const Stmt *MoveStmt = MoveNode->getStmtForDiagnostics())
401 MoveStmt,
C.getSourceManager(), MoveNode->getLocationContext());
405 llvm::raw_svector_ostream
OS(Str);
408 OS <<
"Method called on moved-from object";
409 explainObject(OS, Region, RD, MK);
412 OS <<
"Moved-from object";
413 explainObject(OS, Region, RD, MK);
417 OS <<
"Moved-from object";
418 explainObject(OS, Region, RD, MK);
422 OS <<
"Dereference of null smart pointer";
423 explainObject(OS, Region, RD, MK);
427 auto R = std::make_unique<PathSensitiveBugReport>(
428 BT,
OS.str(), N, LocUsedForUniqueing,
429 MoveNode->getLocationContext()->getDecl());
430 R->addVisitor(std::make_unique<MovedBugVisitor>(*
this, Region, RD, MK));
431 C.emitReport(std::move(R));
437 void MoveChecker::checkPostCall(
const CallEvent &Call,
438 CheckerContext &C)
const {
439 const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
444 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
451 const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
452 if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
455 if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
458 const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
463 const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
464 if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
467 if (
const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
468 if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
471 const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
473 if (BaseRegion->getAs<CXXTempObjectRegion>() ||
474 AFC->getArgExpr(0)->isPRValue())
478 if (
State->get<TrackedRegionMap>(ArgRegion))
482 ObjectKind OK = classifyObject(ArgRegion, RD);
483 if (shouldBeTracked(OK)) {
485 State =
State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
489 assert(!
C.isDifferent() &&
"Should not have made transitions on this path!");
492 bool MoveChecker::isMoveSafeMethod(
const CXXMethodDecl *MethodDec)
const {
494 if (
const auto *ConversionDec =
495 dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
496 const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
504 (MethodDec->
getName().lower() ==
"empty" ||
505 MethodDec->
getName().lower() ==
"isempty"));
508 bool MoveChecker::isStateResetMethod(
const CXXMethodDecl *MethodDec)
const {
511 if (MethodDec->
hasAttr<ReinitializesAttr>())
517 if (MethodName ==
"assign" || MethodName ==
"clear" ||
518 MethodName ==
"destroy" || MethodName ==
"reset" ||
519 MethodName ==
"resize" || MethodName ==
"shrink")
527 bool MoveChecker::isInMoveSafeContext(
const LocationContext *LC)
const {
529 const auto *CtxDec = LC->
getDecl();
530 auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
531 auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
532 auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
533 if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
536 isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
543 const llvm::StringSet<> &Set)
const {
545 return II && Set.count(II->
getName());
548 MoveChecker::ObjectKind
549 MoveChecker::classifyObject(
const MemRegion *MR,
555 bool IsLocal = isa_and_nonnull<VarRegion>(MR) &&
556 isa<StackSpaceRegion>(MR->getMemorySpace());
559 return { IsLocal, SK_NonStd };
561 if (belongsTo(RD, StdSmartPtrClasses))
562 return { IsLocal, SK_SmartPtr };
564 if (belongsTo(RD, StdSafeClasses))
565 return { IsLocal, SK_Safe };
567 return { IsLocal, SK_Unsafe };
570 void MoveChecker::explainObject(llvm::raw_ostream &OS,
const MemRegion *MR,
576 const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
577 OS <<
" '" << RegionDecl->getDeclName() <<
"'";
580 ObjectKind OK = classifyObject(MR, RD);
581 switch (OK.StdKind) {
586 if (MK != MK_Dereference)
597 void MoveChecker::checkPreCall(
const CallEvent &Call, CheckerContext &C)
const {
604 if (
const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
606 auto CtorDec = CC->getDecl();
608 if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
609 const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
611 MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
612 modelUse(
State, ArgRegion, RD, MK, C);
617 const auto IC = dyn_cast<CXXInstanceCall>(&Call);
622 if (isa<CXXDestructorCall>(IC))
625 const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
630 const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
636 ThisRegion = ThisRegion->getMostDerivedObjectRegion();
638 if (isStateResetMethod(MethodDecl)) {
644 if (isMoveSafeMethod(MethodDecl))
650 if (MethodDecl->isOverloadedOperator()) {
653 if (OOK == OO_Equal) {
658 if (MethodDecl->isCopyAssignmentOperator() ||
659 MethodDecl->isMoveAssignmentOperator()) {
660 const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
662 MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
663 modelUse(
State, ArgRegion, RD, MK, C);
670 if (OOK == OO_Star || OOK == OO_Arrow) {
671 modelUse(
State, ThisRegion, RD, MK_Dereference, C);
676 modelUse(
State, ThisRegion, RD, MK_FunCall, C);
679 void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
680 CheckerContext &C)
const {
682 TrackedRegionMapTy TrackedRegions =
State->get<TrackedRegionMap>();
683 for (
auto E : TrackedRegions) {
684 const MemRegion *Region = E.first;
685 bool IsRegDead = !SymReaper.isLiveRegion(Region);
689 State =
State->remove<TrackedRegionMap>(Region);
706 const MemRegion *ThisRegion =
nullptr;
707 if (
const auto *IC = dyn_cast<CXXInstanceCall>(Call))
708 ThisRegion = IC->getCXXThisVal().getAsRegion();
713 for (
const auto *Region : RequestedRegions) {
714 if (ThisRegion != Region &&
715 llvm::is_contained(InvalidatedRegions, Region))
721 for (
const auto *Region : InvalidatedRegions)
729 const char *NL,
const char *Sep)
const {
731 TrackedRegionMapTy RS =
State->get<TrackedRegionMap>();
734 Out << Sep <<
"Moved-from objects :" << NL;
736 I.first->dumpToStream(Out);
737 if (I.second.isMoved())
740 Out <<
": moved and reported";
745 void ento::registerMoveChecker(CheckerManager &mgr) {
746 MoveChecker *chk = mgr.registerChecker<MoveChecker>();
747 chk->setAggressiveness(
748 mgr.getAnalyzerOptions().getCheckerStringOption(chk,
"WarnOn"), mgr);
751 bool ento::shouldRegisterMoveChecker(
const CheckerManager &mgr) {