vrnithinkumar created this revision. vrnithinkumar added reviewers: vsavchenko, xazax.hun, NoQ. Herald added subscribers: cfe-commits, martong, Charusso, rnkovacs. Herald added a project: clang.
This is just a prototype review for my changes since I thought it will be easier to ask code related doubts here. I just wanted to share how I prototyped the checker for default constructed unique pointer dereferences. It is incomplete. Not added tests. Not all cases covered. Reporting part is not proper. **This may be a throw away code.** I am sharing this so that if I am fundamentally wrong in any of my directions it will be much better to catch early and rectify. - Added a two maps to track mem region and corresponding states and Symbols to mem region - Created a RegionState to track information about the state of memory region whether it is null or not or unknown - Using PostCall to update the states of mem region - Using PreCall to check the null pointer dereferences **Few doubts:** I am not sure about whether I should use `eval::Call` or both `check::PreCall` and `check::PostCall`. In the `eval::Call` documentation I found this "Note, that only one checker can evaluate a call.". So I am little bit confused about using it. Using one map for tracking the mem region and states then one more for Symbols to region to track which all Symbol has the inner pointer. I am just looking is there any better approach for this. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D81315 Files: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
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<eval::Call> { +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<eval::Call, check::PreCall, + check::PostCall, check::DeadSymbols> { 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<BugType> 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<CXXMemberOperatorCall>(&Call); + if (!OC) + return; + const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(OC->getDecl()); + + if (MethodDecl->isOverloadedOperator()) { + OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); + if (OOK == OO_Star || OOK == OO_Arrow) { + const RegionState *RS = State->get<TrackedRegionMap>(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<CXXConstructorCall>(&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<TrackedRegionMap>(ThisValRegion, RegionState::getNull()); + C.addTransition(State); + return; + } + } + } + + // calling reset or making it empty + if (const auto IC = dyn_cast<CXXInstanceCall>(&Call)) { + const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); + if (!MethodDecl) + return; + auto ArgsNum = IC->getNumArgs(); + + if (ArgsNum == 0 && isResetMethod(MethodDecl)) { + const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); + State = + State->set<TrackedRegionMap>(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<TrackedRegionMap>(ThisValRegion, RegionState::getNull()); + } else { + State = State->set<TrackedRegionMap>(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<TrackedRegionMap>(); + for (auto E : TrackedRegions) { + const MemRegion *Region = E.first; + bool IsRegDead = !SymReaper.isLiveRegion(Region); + + if (IsRegDead) { + State = State->remove<TrackedRegionMap>(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<PathSensitiveBugReport>( + *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<SmartPtrModeling>(); }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits