Index: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -14,25 +14,75 @@ #include "Move.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/Type.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" using namespace clang; using namespace ento; namespace { -class SmartPtrModeling : public Checker { +struct RegionState { +private: + enum Kind { Null, NonNull, Unknown } K; + RegionState(Kind InK) : K(InK) {} + +public: + bool isNull() const { return K == Null; } + + static RegionState getNull() { return RegionState(Null); } + static RegionState getNonNull() { return RegionState(NonNull); } + static RegionState getUnKnown() { return RegionState(Unknown); } + + bool operator==(const RegionState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; +} // end of anonymous namespace + +namespace { +class SmartPtrModeling : public Checker { bool isNullAfterMoveMethod(const CallEvent &Call) const; public: bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + +private: + mutable std::unique_ptr BT; + void reportBug(CheckerContext &C, const CallEvent &Call) const; + bool isSmartPointer(const CXXRecordDecl *RD) const; + bool isResetMethod(const CXXMethodDecl *MethodDec) const; + + // STL smart pointers which we are trying to model + const llvm::StringSet<> StdSmartPtrs = { + "shared_ptr", + "unique_ptr", + "weak_ptr", + }; + + // STL smart pointer methods which resets to null + const llvm::StringSet<> ResetMethods = {"reset", "release", "swap"}; + + // STL smart pointer methods which resets to null via null argument + const llvm::StringSet<> NullResetMethods = {"reset", "swap"}; }; } // end of anonymous namespace +// to track the mem region and curresponding states +REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) +// to track the Symbols which will get inner raw pointer via unique_ptr.get() +// method +REGISTER_MAP_WITH_PROGRAMSTATE(SymbolRegionMap, const SymbolRef, MemRegion) + bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). @@ -63,6 +113,129 @@ return true; } +void SmartPtrModeling::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const auto OC = dyn_cast(&Call); + if (!OC) + return; + const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + const auto *MethodDecl = dyn_cast_or_null(OC->getDecl()); + + if (MethodDecl->isOverloadedOperator()) { + OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); + if (OOK == OO_Star || OOK == OO_Arrow) { + const RegionState *RS = State->get(ThisRegion); + if (RS && RS->isNull()) { + reportBug(C, Call); + } + } + } +} + +void SmartPtrModeling::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + auto State = C.getState(); + + // Default constructor being null + if (const auto *CC = dyn_cast(&Call)) { + const auto *CtorDec = CC->getDecl(); + auto isEmptyCtr = CC->getNumArgs() == 0; + const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion(); + if (ThisValRegion && CtorDec && isEmptyCtr) { + const CXXRecordDecl *RD = CtorDec->getParent(); + if (isSmartPointer(RD)) { + State = + State->set(ThisValRegion, RegionState::getNull()); + C.addTransition(State); + return; + } + } + } + + // calling reset or making it empty + if (const auto IC = dyn_cast(&Call)) { + const auto MethodDecl = dyn_cast_or_null(IC->getDecl()); + if (!MethodDecl) + return; + auto ArgsNum = IC->getNumArgs(); + + if (ArgsNum == 0 && isResetMethod(MethodDecl)) { + const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); + State = + State->set(ThisValRegion, RegionState::getNull()); + C.addTransition(State); + return; + } + + // in case of reset to null or proper value + if (ArgsNum == 1 && isResetMethod(MethodDecl)) { + const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); + auto ArgVal = IC->getArgSVal(0); + if (ArgVal.isZeroConstant()) { + State = + State->set(ThisValRegion, RegionState::getNull()); + } else { + State = State->set(ThisValRegion, + RegionState::getNonNull()); + } + C.addTransition(State); + return; + } + } +} + +void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + // Clean up dead regions from the region map. + TrackedRegionMapTy TrackedRegions = State->get(); + for (auto E : TrackedRegions) { + const MemRegion *Region = E.first; + bool IsRegDead = !SymReaper.isLiveRegion(Region); + + if (IsRegDead) { + State = State->remove(Region); + } + } + C.addTransition(State); +} + +void SmartPtrModeling::reportBug(CheckerContext &C, + const CallEvent &Call) const { + ExplodedNode *ErrNode = C.generateErrorNode(); + if (!ErrNode) + return; + + if (!BT) + BT.reset(new BugType(this, "unique_ptr", "Dont call unique_ptr")); + auto R = std::make_unique( + *BT, "Dereference of uninitialized smart pointer", ErrNode); + R->addRange(Call.getSourceRange()); + C.emitReport(std::move(R)); +} + +bool SmartPtrModeling::isSmartPointer(const CXXRecordDecl *RecordDec) const { + if (!RecordDec) + return false; + if (RecordDec->getDeclName().isIdentifier()) { + return StdSmartPtrs.count(RecordDec->getName().lower()); + } + return false; +} + +bool SmartPtrModeling::isResetMethod(const CXXMethodDecl *MethodDec) const { + if (!MethodDec) + return false; + if (MethodDec->getDeclName().isIdentifier()) { + return ResetMethods.count(MethodDec->getName().lower()); + } + return false; +} + void ento::registerSmartPtrModeling(CheckerManager &Mgr) { Mgr.registerChecker(); }