diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h b/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtr.h @@ -23,6 +23,8 @@ /// Returns true if the event call is on smart pointer. bool isStdSmartPtrCall(const CallEvent &Call); +bool isStdSmartPtr(const CXXRecordDecl *RD); + /// Returns whether the smart pointer is null or not. bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion); 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 @@ -60,7 +60,7 @@ private: void handleReset(const CallEvent &Call, CheckerContext &C) const; void handleRelease(const CallEvent &Call, CheckerContext &C) const; - void handleSwap(const CallEvent &Call, CheckerContext &C) const; + void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const; void handleGet(const CallEvent &Call, CheckerContext &C) const; bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, @@ -68,14 +68,17 @@ bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, const MemRegion *OtherSmartPtrRegion) const; void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; + bool handleSwap(ProgramStateRef State, const MemRegion *FirstRegion, + const MemRegion *SecondRegion, CheckerContext &C) const; using SmartPtrMethodHandlerFn = void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; CallDescriptionMap SmartPtrMethodHandlers{ {{"reset"}, &SmartPtrModeling::handleReset}, {{"release"}, &SmartPtrModeling::handleRelease}, - {{"swap", 1}, &SmartPtrModeling::handleSwap}, + {{"swap", 1}, &SmartPtrModeling::handleSwapMethod}, {{"get"}, &SmartPtrModeling::handleGet}}; + const CallDescription StdSwapCall{{"std", "swap"}, 2}; }; } // end of anonymous namespace @@ -91,11 +94,15 @@ return false; const auto *RecordDecl = MethodDecl->getParent(); - if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) + return isStdSmartPtr(RecordDecl); +} + +bool isStdSmartPtr(const CXXRecordDecl *RD) { + if (!RD || !RD->getDeclContext()->isStdNamespace()) return false; - if (RecordDecl->getDeclName().isIdentifier()) { - StringRef Name = RecordDecl->getName(); + if (RD->getDeclName().isIdentifier()) { + StringRef Name = RD->getName(); return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; } return false; @@ -178,6 +185,24 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { ProgramStateRef State = C.getState(); + + if (Call.isCalled(StdSwapCall)) { + // Check the first arg, if it is of std::unique_ptr type. + assert(Call.getNumArgs() == 2 && "std::swap should have two arguments"); + const Expr *FirstArg = Call.getArgExpr(0); + if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl())) { + return false; + } + const MemRegion *FirstArgThisRegion = Call.getArgSVal(0).getAsRegion(); + if (!FirstArgThisRegion) + return false; + const MemRegion *SecondArgThisRegion = Call.getArgSVal(1).getAsRegion(); + if (!SecondArgThisRegion) + return false; + + return handleSwap(State, FirstArgThisRegion, SecondArgThisRegion, C); + } + if (!smartptr::isStdSmartPtrCall(Call)) return false; @@ -395,8 +420,8 @@ // pointer. } -void SmartPtrModeling::handleSwap(const CallEvent &Call, - CheckerContext &C) const { +void SmartPtrModeling::handleSwapMethod(const CallEvent &Call, + CheckerContext &C) const { // To model unique_ptr::swap() method. const auto *IC = dyn_cast(&Call); if (!IC) @@ -411,27 +436,24 @@ return; auto State = C.getState(); - const auto *ThisRegionInnerPointerVal = - State->get(ThisRegion); - const auto *ArgRegionInnerPointerVal = - State->get(ArgRegion); + handleSwap(State, ThisRegion, ArgRegion, C); +} - // Swap the tracked region values. - State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); - State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); +bool SmartPtrModeling::handleSwap(ProgramStateRef State, + const MemRegion *FirstThisRegion, + const MemRegion *SecondThisRegion, + CheckerContext &C) const { + const auto *FirstInnerPtrVal = State->get(FirstThisRegion); + const auto *SecondInnerPtrVal = + State->get(SecondThisRegion); - C.addTransition( - State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR, - llvm::raw_ostream &OS) { - if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || - !BR.isInteresting(ThisRegion)) - return; - BR.markInteresting(ArgRegion); - OS << "Swapped null smart pointer"; - checkAndPrettyPrintRegion(OS, ArgRegion); - OS << " with smart pointer"; - checkAndPrettyPrintRegion(OS, ThisRegion); - })); + State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal); + State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal); + + // TODO: We need to emit some note here probably!! + C.addTransition(State); + + return true; } void SmartPtrModeling::handleGet(const CallEvent &Call, 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 @@ -69,20 +69,17 @@ void derefOnSwappedNullPtr() { std::unique_ptr P(new A()); // expected-note {{Smart pointer 'P' is constructed}} - std::unique_ptr PNull; // expected-note {{Default constructed smart pointer 'PNull' is null}} - P.swap(PNull); // expected-note {{Swapped null smart pointer 'PNull' with smart pointer 'P'}} + std::unique_ptr PNull; + P.swap(PNull); PNull->foo(); // No warning. (*P).foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} // expected-note@-1{{Dereference of null smart pointer 'P'}} } -// FIXME: Fix this test when "std::swap" is modeled seperately. void derefOnStdSwappedNullPtr() { std::unique_ptr P; // expected-note {{Default constructed smart pointer 'P' is null}} - std::unique_ptr PNull; // expected-note {{Default constructed smart pointer 'PNull' is null}} - std::swap(P, PNull); // expected-note@Inputs/system-header-simulator-cxx.h:979 {{Swapped null smart pointer 'PNull' with smart pointer 'P'}} - // expected-note@-1 {{Calling 'swap'}} - // expected-note@-2 {{Returning from 'swap'}} + std::unique_ptr PNull; + std::swap(P, PNull); P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}} // expected-note@-1{{Dereference of null smart pointer 'P'}} }