diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -56,6 +56,8 @@ void handleReset(const CallEvent &Call, CheckerContext &C) const; void handleRelease(const CallEvent &Call, CheckerContext &C) const; void handleSwap(const CallEvent &Call, CheckerContext &C) const; + bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; @@ -158,13 +160,16 @@ return false; if (const auto *CC = dyn_cast(&Call)) { - if (CC->getDecl()->isCopyOrMoveConstructor()) + if (CC->getDecl()->isCopyConstructor()) return false; const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); if (!ThisRegion) return false; + if (CC->getDecl()->isMoveConstructor()) + return handleMoveCtr(Call, C, ThisRegion); + if (Call.getNumArgs() == 0) { auto NullVal = C.getSValBuilder().makeNull(); State = State->set(ThisRegion, NullVal); @@ -345,6 +350,47 @@ })); } +bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisRegion) const { + const auto *ArgRegion = Call.getArgSVal(0).getAsRegion(); + if (!ArgRegion) + return false; + + ProgramStateRef State = C.getState(); + const auto *ArgInnerPtrVal = State->get(ArgRegion); + auto IsArgValNull = false; + if (ArgInnerPtrVal) { + State = State->set(ThisRegion, *ArgInnerPtrVal); + IsArgValNull = ArgInnerPtrVal->isZeroConstant(); + } else { + State = State->remove(ThisRegion); + } + // Tracking the moved argument as null. + auto NullVal = C.getSValBuilder().makeNull(); + State = State->set(ArgRegion, NullVal); + + C.addTransition(State, C.getNoteTag([ThisRegion, ArgRegion, + IsArgValNull](PathSensitiveBugReport &BR, + llvm::raw_ostream &OS) { + if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) + return; + if (BR.isInteresting(ArgRegion)) { + OS << "Smart pointer "; + ArgRegion->printPretty(OS); + OS << " is null after moved to construct "; + ThisRegion->printPretty(OS); + } + if (BR.isInteresting(ThisRegion) && IsArgValNull) { + OS << "Smart pointer "; + ThisRegion->printPretty(OS); + OS << " is constructed with null value moved from "; + ArgRegion->printPretty(OS); + BR.markInteresting(ArgRegion); + } + })); + return true; +} + void ento::registerSmartPtrModeling(CheckerManager &Mgr) { auto *Checker = Mgr.registerChecker(); Checker->ModelSmartPtrDereference = diff --git a/clang/test/Analysis/smart-ptr-text-output.cpp b/clang/test/Analysis/smart-ptr-text-output.cpp --- a/clang/test/Analysis/smart-ptr-text-output.cpp +++ b/clang/test/Analysis/smart-ptr-text-output.cpp @@ -116,3 +116,32 @@ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' of type 'std::unique_ptr' [cplusplus.Move]}} // expected-note@-1 {{Dereference of null smart pointer 'P' of type 'std::unique_ptr'}} } + +void derefMoveConstructedWithNullPtr() { + std::unique_ptr PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}} + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'P' is constructed with null value moved from 'PToMove'}} + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'P'}} +} + +void derefValidPtrMovedToConstruct() { + std::unique_ptr PToMove(new A()); // expected-note {{Smart pointer 'PToMove' is constructed}} + // FIXME: above note should go away once we fix marking region not interested. + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after moved to construct 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'PToMove'}} +} + +void derefNullPtrMovedToConstruct() { + std::unique_ptr PToMove; // expected-note {{Default constructed smart pointer 'PToMove' is null}} + // FIXME: above note should go away once we fix marking region not interested. + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after moved to construct 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'PToMove'}} +} + +void derefUnknownPtrMovedToConstruct(std::unique_ptr PToMove) { + std::unique_ptr P(std::move(PToMove)); // expected-note {{Smart pointer 'PToMove' is null after moved to construct 'P'}} + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} + // expected-note@-1{{Dereference of null smart pointer 'PToMove'}} +} diff --git a/clang/test/Analysis/smart-ptr.cpp b/clang/test/Analysis/smart-ptr.cpp --- a/clang/test/Analysis/smart-ptr.cpp +++ b/clang/test/Analysis/smart-ptr.cpp @@ -252,3 +252,44 @@ P->foo(); // No warning. PValid->foo(); // No warning. } + +void derefMoveConstructedWithValidPtr() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P(std::move(PToMove)); + P->foo(); // No warning. +} + +void derefMoveConstructedWithNullPtr() { + std::unique_ptr PToMove; + std::unique_ptr P(std::move(PToMove)); + P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} +} + +void derefMoveConstructedWithUnknownPtr(std::unique_ptr PToMove) { + std::unique_ptr P(std::move(PToMove)); + P->foo(); // No warning. +} + +void derefValidPtrMovedToConstruct() { + std::unique_ptr PToMove(new A()); + std::unique_ptr P(std::move(PToMove)); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +void derefNullPtrMovedToConstruct() { + std::unique_ptr PToMove; + std::unique_ptr P(std::move(PToMove)); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +void derefUnknownPtrMovedToConstruct(std::unique_ptr PToMove) { + std::unique_ptr P(std::move(PToMove)); + PToMove->foo(); // expected-warning {{Dereference of null smart pointer 'PToMove' [alpha.cplusplus.SmartPtr]}} +} + +std::unique_ptr &&functionReturnsRValueRef(); + +void derefMoveConstructedWithRValueRefReturn(std::unique_ptr PToMove) { + std::unique_ptr P(functionReturnsRValueRef()); + P->foo(); // No warning. +}