diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp')
-rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp | 189 |
1 files changed, 175 insertions, 14 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp index fd372aafa50d9..bcc7d4103c1c6 100644 --- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp @@ -12,27 +12,81 @@ //===----------------------------------------------------------------------===// #include "Move.h" +#include "SmartPtr.h" +#include "clang/AST/DeclCXX.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> { +class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> { + bool isNullAfterMoveMethod(const CallEvent &Call) const; public: + // Whether the checker should model for null dereferences of smart pointers. + DefaultBool ModelSmartPtrDereference; bool evalCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + +private: + ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisValRegion) const; + void handleReset(const CallEvent &Call, CheckerContext &C) const; + void handleRelease(const CallEvent &Call, CheckerContext &C) const; + void handleSwap(const CallEvent &Call, CheckerContext &C) const; + + using SmartPtrMethodHandlerFn = + void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; + CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ + {{"reset"}, &SmartPtrModeling::handleReset}, + {{"release"}, &SmartPtrModeling::handleRelease}, + {{"swap", 1}, &SmartPtrModeling::handleSwap}}; }; } // end of anonymous namespace +REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) + +// Define the inter-checker API. +namespace clang { +namespace ento { +namespace smartptr { +bool isStdSmartPtrCall(const CallEvent &Call) { + const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (!MethodDecl || !MethodDecl->getParent()) + return false; + + const auto *RecordDecl = MethodDecl->getParent(); + if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) + return false; + + if (RecordDecl->getDeclName().isIdentifier()) { + StringRef Name = RecordDecl->getName(); + return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"; + } + return false; +} + +bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { + const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); + return InnerPointVal && InnerPointVal->isZeroConstant(); +} +} // namespace smartptr +} // namespace ento +} // namespace clang + bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { // TODO: Update CallDescription to support anonymous calls? // TODO: Handle other methods, such as .get() or .release(). @@ -44,29 +98,136 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const { bool SmartPtrModeling::evalCall(const CallEvent &Call, CheckerContext &C) const { - if (!isNullAfterMoveMethod(Call)) + + if (!smartptr::isStdSmartPtrCall(Call)) return false; - ProgramStateRef State = C.getState(); - const MemRegion *ThisR = - cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + if (isNullAfterMoveMethod(Call)) { + ProgramStateRef State = C.getState(); + const MemRegion *ThisR = + cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); + + if (!move::isMovedFrom(State, ThisR)) { + // TODO: Model this case as well. At least, avoid invalidation of globals. + return false; + } + + // TODO: Add a note to bug reports describing this decision. + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeZeroVal(Call.getResultType()))); + return true; + } - if (!move::isMovedFrom(State, ThisR)) { - // TODO: Model this case as well. At least, avoid invalidation of globals. + if (!ModelSmartPtrDereference) return false; + + if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { + if (CC->getDecl()->isCopyOrMoveConstructor()) + return false; + + const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion(); + if (!ThisValRegion) + return false; + + auto State = updateTrackedRegion(Call, C, ThisValRegion); + C.addTransition(State); + return true; + } + + const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); + if (!Handler) + return false; + (this->**Handler)(Call, C); + + return C.isDifferent(); +} + +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::handleReset(const CallEvent &Call, + CheckerContext &C) const { + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisValRegion) + return; + auto State = updateTrackedRegion(Call, C, ThisValRegion); + C.addTransition(State); + // TODO: Make sure to ivalidate the the region in the Store if we don't have + // time to model all methods. +} + +void SmartPtrModeling::handleRelease(const CallEvent &Call, + CheckerContext &C) const { + const auto *IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisValRegion) + return; + + auto State = updateTrackedRegion(Call, C, ThisValRegion); + + const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion); + if (InnerPointVal) { + State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + *InnerPointVal); + } + C.addTransition(State); + // TODO: Add support to enable MallocChecker to start tracking the raw + // pointer. +} + +void SmartPtrModeling::handleSwap(const CallEvent &Call, + CheckerContext &C) const { + // TODO: Add support to handle swap method. +} + +ProgramStateRef +SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C, + const MemRegion *ThisValRegion) const { + // TODO: Refactor and clean up handling too many things. + ProgramStateRef State = C.getState(); + auto NumArgs = Call.getNumArgs(); + + if (NumArgs == 0) { + auto NullSVal = C.getSValBuilder().makeNull(); + State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal); + } else if (NumArgs == 1) { + auto ArgVal = Call.getArgSVal(0); + assert(Call.getArgExpr(0)->getType()->isPointerType() && + "Adding a non pointer value to TrackedRegionMap"); + State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal); } - // TODO: Add a note to bug reports describing this decision. - C.addTransition( - State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), - C.getSValBuilder().makeZeroVal(Call.getResultType()))); - return true; + return State; } void ento::registerSmartPtrModeling(CheckerManager &Mgr) { - Mgr.registerChecker<SmartPtrModeling>(); + auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); + Checker->ModelSmartPtrDereference = + Mgr.getAnalyzerOptions().getCheckerBooleanOption( + Checker, "ModelSmartPtrDereference"); } -bool ento::shouldRegisterSmartPtrModeling(const LangOptions &LO) { +bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { + const LangOptions &LO = mgr.getLangOpts(); return LO.CPlusPlus; } |