123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911 |
- // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines a checker that models various aspects of
- // C++ smart pointer behavior.
- //
- //===----------------------------------------------------------------------===//
- #include "Move.h"
- #include "SmartPtr.h"
- #include "clang/AST/DeclCXX.h"
- #include "clang/AST/DeclarationName.h"
- #include "clang/AST/ExprCXX.h"
- #include "clang/AST/Type.h"
- #include "clang/Basic/LLVM.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/CallDescription.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
- #include "llvm/ADT/StringMap.h"
- #include "llvm/Support/ErrorHandling.h"
- #include <string>
- using namespace clang;
- using namespace ento;
- namespace {
- class SmartPtrModeling
- : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
- check::LiveSymbols> {
- bool isBoolConversionMethod(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;
- ProgramStateRef
- checkRegionChanges(ProgramStateRef State,
- const InvalidatedSymbols *Invalidated,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions,
- const LocationContext *LCtx, const CallEvent *Call) const;
- void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
- const char *Sep) const override;
- void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
- private:
- void handleReset(const CallEvent &Call, CheckerContext &C) const;
- void handleRelease(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,
- const MemRegion *ThisRegion) const;
- bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
- const MemRegion *OtherSmartPtrRegion) const;
- void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
- bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
- bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
- bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
- CheckerContext &C) const;
- std::pair<SVal, ProgramStateRef>
- retrieveOrConjureInnerPtrVal(ProgramStateRef State,
- const MemRegion *ThisRegion, const Expr *E,
- QualType Type, CheckerContext &C) const;
- using SmartPtrMethodHandlerFn =
- void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
- CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
- {{"reset"}, &SmartPtrModeling::handleReset},
- {{"release"}, &SmartPtrModeling::handleRelease},
- {{"swap", 1}, &SmartPtrModeling::handleSwapMethod},
- {{"get"}, &SmartPtrModeling::handleGet}};
- const CallDescription StdSwapCall{{"std", "swap"}, 2};
- const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
- const CallDescription StdMakeUniqueForOverwriteCall{
- {"std", "make_unique_for_overwrite"}};
- };
- } // end of anonymous namespace
- REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
- // Checks if RD has name in Names and is in std namespace
- static bool hasStdClassWithName(const CXXRecordDecl *RD,
- ArrayRef<llvm::StringLiteral> Names) {
- if (!RD || !RD->getDeclContext()->isStdNamespace())
- return false;
- if (RD->getDeclName().isIdentifier())
- return llvm::is_contained(Names, RD->getName());
- return false;
- }
- constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
- "weak_ptr"};
- static bool isStdSmartPtr(const CXXRecordDecl *RD) {
- return hasStdClassWithName(RD, STD_PTR_NAMES);
- }
- static bool isStdSmartPtr(const Expr *E) {
- return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
- }
- // 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;
- return isStdSmartPtr(MethodDecl->getParent());
- }
- bool isStdSmartPtr(const CXXRecordDecl *RD) {
- if (!RD || !RD->getDeclContext()->isStdNamespace())
- return false;
- if (RD->getDeclName().isIdentifier()) {
- StringRef Name = RD->getName();
- return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
- }
- return false;
- }
- bool isStdSmartPtr(const Expr *E) {
- return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
- }
- bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
- const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
- return InnerPointVal &&
- !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
- }
- } // namespace smartptr
- } // namespace ento
- } // namespace clang
- // If a region is removed all of the subregions need to be removed too.
- static TrackedRegionMapTy
- removeTrackedSubregions(TrackedRegionMapTy RegionMap,
- TrackedRegionMapTy::Factory &RegionMapFactory,
- const MemRegion *Region) {
- if (!Region)
- return RegionMap;
- for (const auto &E : RegionMap) {
- if (E.first->isSubRegionOf(Region))
- RegionMap = RegionMapFactory.remove(RegionMap, E.first);
- }
- return RegionMap;
- }
- static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
- const MemRegion *Region,
- const SVal *RegionInnerPointerVal) {
- if (RegionInnerPointerVal) {
- State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
- } else {
- State = State->remove<TrackedRegionMap>(Region);
- }
- return State;
- }
- static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
- if (!RD || !RD->isInStdNamespace())
- return {};
- const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
- if (!TSD)
- return {};
- auto TemplateArgs = TSD->getTemplateArgs().asArray();
- if (TemplateArgs.empty())
- return {};
- auto InnerValueType = TemplateArgs[0].getAsType();
- return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
- }
- // This is for use with standalone-functions like std::make_unique,
- // std::make_unique_for_overwrite, etc. It reads the template parameter and
- // returns the pointer type corresponding to it,
- static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
- CheckerContext &C) {
- const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FD || !FD->isFunctionTemplateSpecialization())
- return {};
- const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
- if (TemplateArgs.size() == 0)
- return {};
- auto ValueType = TemplateArgs[0].getAsType();
- return C.getASTContext().getPointerType(ValueType.getCanonicalType());
- }
- // Helper method to get the inner pointer type of specialized smart pointer
- // Returns empty type if not found valid inner pointer type.
- static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
- const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
- if (!MethodDecl || !MethodDecl->getParent())
- return {};
- const auto *RecordDecl = MethodDecl->getParent();
- return getInnerPointerType(C, RecordDecl);
- }
- // Helper method to pretty print region and avoid extra spacing.
- static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
- const MemRegion *Region) {
- if (Region->canPrintPretty()) {
- OS << " ";
- Region->printPretty(OS);
- }
- }
- bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
- // TODO: Update CallDescription to support anonymous calls?
- // TODO: Handle other methods, such as .get() or .release().
- // But once we do, we'd need a visitor to explain null dereferences
- // that are found via such modeling.
- const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
- return CD && CD->getConversionType()->isBooleanType();
- }
- constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
- bool isStdBasicOstream(const Expr *E) {
- const auto *RD = E->getType()->getAsCXXRecordDecl();
- return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
- }
- static bool isStdFunctionCall(const CallEvent &Call) {
- return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
- }
- bool isStdOstreamOperatorCall(const CallEvent &Call) {
- if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
- return false;
- const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
- if (!FC)
- return false;
- const FunctionDecl *FD = FC->getDecl();
- if (!FD->isOverloadedOperator())
- return false;
- const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
- if (OOK != clang::OO_LessLess)
- return false;
- return isStdSmartPtr(Call.getArgExpr(1)) &&
- isStdBasicOstream(Call.getArgExpr(0));
- }
- static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
- if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
- return false;
- return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
- smartptr::isStdSmartPtr(Call.getArgExpr(1));
- }
- bool SmartPtrModeling::evalCall(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- // If any one of the arg is a unique_ptr, then
- // we can try this function
- if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
- if (handleComparisionOp(Call, C))
- return true;
- if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
- return handleOstreamOperator(Call, C);
- if (StdSwapCall.matches(Call)) {
- // 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;
- return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
- }
- if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
- if (!ModelSmartPtrDereference)
- return false;
-
- const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction();
- if (!ThisRegionOpt)
- return false;
- const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
- Call.getOriginExpr(), C.getLocationContext(),
- getPointerTypeFromTemplateArg(Call, C), C.blockCount());
- const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
- State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
- State = State->assume(PtrVal, true);
- // TODO: ExprEngine should do this for us.
- // For a bit more context:
- // 1) Why do we need this? Since we are modelling a "function"
- // that returns a constructed object we need to store this information in
- // the program state.
- //
- // 2) Why does this work?
- // `updateObjectsUnderConstruction` does exactly as it sounds.
- //
- // 3) How should it look like when moved to the Engine?
- // It would be nice if we can just
- // pretend we don't need to know about this - ie, completely automatic work.
- // However, realistically speaking, I think we would need to "signal" the
- // ExprEngine evalCall handler that we are constructing an object with this
- // function call (constructors obviously construct, hence can be
- // automatically deduced).
- auto &Engine = State->getStateManager().getOwningEngine();
- State = Engine.updateObjectsUnderConstruction(
- *ThisRegionOpt, nullptr, State, C.getLocationContext(),
- Call.getConstructionContext(), {});
- // We don't leave a note here since it is guaranteed the
- // unique_ptr from this call is non-null (hence is safe to de-reference).
- C.addTransition(State);
- return true;
- }
- if (!smartptr::isStdSmartPtrCall(Call))
- return false;
- if (isBoolConversionMethod(Call)) {
- const MemRegion *ThisR =
- cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
- if (ModelSmartPtrDereference) {
- // The check for the region is moved is duplicated in handleBoolOperation
- // method.
- // FIXME: Once we model std::move for smart pointers clean up this and use
- // that modeling.
- handleBoolConversion(Call, C);
- return true;
- } else {
- 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 (!ModelSmartPtrDereference)
- return false;
- if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
- 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<TrackedRegionMap>(ThisRegion, NullVal);
- C.addTransition(
- State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
- !BR.isInteresting(ThisRegion))
- return;
- OS << "Default constructed smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- OS << " is null";
- }));
- } else {
- const auto *TrackingExpr = Call.getArgExpr(0);
- assert(TrackingExpr->getType()->isPointerType() &&
- "Adding a non pointer value to TrackedRegionMap");
- auto ArgVal = Call.getArgSVal(0);
- State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
- C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
- ArgVal](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
- !BR.isInteresting(ThisRegion))
- return;
- bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
- OS << "Smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- if (ArgVal.isZeroConstant())
- OS << " is constructed using a null value";
- else
- OS << " is constructed";
- }));
- }
- return true;
- }
- if (handleAssignOp(Call, C))
- return true;
- const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
- if (!Handler)
- return false;
- (this->**Handler)(Call, C);
- return C.isDifferent();
- }
- std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
- ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
- QualType Type, CheckerContext &C) const {
- const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
- if (Ptr)
- return {*Ptr, State};
- auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
- Type, C.blockCount());
- State = State->set<TrackedRegionMap>(ThisRegion, Val);
- return {Val, State};
- }
- bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
- CheckerContext &C) const {
- const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
- if (!FC)
- return false;
- const FunctionDecl *FD = FC->getDecl();
- if (!FD->isOverloadedOperator())
- return false;
- const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
- if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
- OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
- OOK == OO_Spaceship))
- return false;
- // There are some special cases about which we can infer about
- // the resulting answer.
- // For reference, there is a discussion at https://reviews.llvm.org/D104616.
- // Also, the cppreference page is good to look at
- // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
- auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
- SVal S) -> std::pair<SVal, ProgramStateRef> {
- if (S.isZeroConstant()) {
- return {S, State};
- }
- const MemRegion *Reg = S.getAsRegion();
- assert(Reg &&
- "this pointer of std::unique_ptr should be obtainable as MemRegion");
- QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
- return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
- };
- SVal First = Call.getArgSVal(0);
- SVal Second = Call.getArgSVal(1);
- const auto *FirstExpr = Call.getArgExpr(0);
- const auto *SecondExpr = Call.getArgExpr(1);
- const auto *ResultExpr = Call.getOriginExpr();
- const auto *LCtx = C.getLocationContext();
- auto &Bldr = C.getSValBuilder();
- ProgramStateRef State = C.getState();
- SVal FirstPtrVal, SecondPtrVal;
- std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
- std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
- BinaryOperatorKind BOK =
- operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
- auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
- Call.getResultType());
- if (OOK != OO_Spaceship) {
- ProgramStateRef TrueState, FalseState;
- std::tie(TrueState, FalseState) =
- State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
- if (TrueState)
- C.addTransition(
- TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
- if (FalseState)
- C.addTransition(
- FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
- } else {
- C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
- }
- return true;
- }
- bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
- CheckerContext &C) const {
- // operator<< does not modify the smart pointer.
- // And we don't really have much of modelling of basic_ostream.
- // So, we are better off:
- // 1) Invalidating the mem-region of the ostream object at hand.
- // 2) Setting the SVal of the basic_ostream as the return value.
- // Not very satisfying, but it gets the job done, and is better
- // than the default handling. :)
- ProgramStateRef State = C.getState();
- const auto StreamVal = Call.getArgSVal(0);
- const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
- if (!StreamThisRegion)
- return false;
- State =
- State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
- C.blockCount(), C.getLocationContext(), false);
- State =
- State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
- C.addTransition(State);
- return true;
- }
- 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::printState(raw_ostream &Out, ProgramStateRef State,
- const char *NL, const char *Sep) const {
- TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
- if (!RS.isEmpty()) {
- Out << Sep << "Smart ptr regions :" << NL;
- for (auto I : RS) {
- I.first->dumpToStream(Out);
- if (smartptr::isNullSmartPtr(State, I.first))
- Out << ": Null";
- else
- Out << ": Non Null";
- Out << NL;
- }
- }
- }
- ProgramStateRef SmartPtrModeling::checkRegionChanges(
- ProgramStateRef State, const InvalidatedSymbols *Invalidated,
- ArrayRef<const MemRegion *> ExplicitRegions,
- ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
- const CallEvent *Call) const {
- TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
- TrackedRegionMapTy::Factory &RegionMapFactory =
- State->get_context<TrackedRegionMap>();
- for (const auto *Region : Regions)
- RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
- Region->getBaseRegion());
- return State->set<TrackedRegionMap>(RegionMap);
- }
- void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
- SymbolReaper &SR) const {
- // Marking tracked symbols alive
- TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
- for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
- SVal Val = I->second;
- for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
- SR.markLive(*si);
- }
- }
- }
- void SmartPtrModeling::handleReset(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
- if (!IC)
- return;
- const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
- if (!ThisRegion)
- return;
- assert(Call.getArgExpr(0)->getType()->isPointerType() &&
- "Adding a non pointer value to TrackedRegionMap");
- State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
- const auto *TrackingExpr = Call.getArgExpr(0);
- C.addTransition(
- State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
- !BR.isInteresting(ThisRegion))
- return;
- bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
- OS << "Smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- OS << " reset using a null value";
- }));
- // TODO: Make sure to ivalidate the region in the Store if we don't have
- // time to model all methods.
- }
- void SmartPtrModeling::handleRelease(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
- if (!IC)
- return;
- const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
- if (!ThisRegion)
- return;
- const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
- if (InnerPointVal) {
- State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- *InnerPointVal);
- }
- auto ValueToUpdate = C.getSValBuilder().makeNull();
- State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
- C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
- !BR.isInteresting(ThisRegion))
- return;
- OS << "Smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- OS << " is released and set to null";
- }));
- // TODO: Add support to enable MallocChecker to start tracking the raw
- // pointer.
- }
- void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
- CheckerContext &C) const {
- // To model unique_ptr::swap() method.
- const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
- if (!IC)
- return;
- auto State = C.getState();
- handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
- }
- bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
- SVal Second, CheckerContext &C) const {
- const MemRegion *FirstThisRegion = First.getAsRegion();
- if (!FirstThisRegion)
- return false;
- const MemRegion *SecondThisRegion = Second.getAsRegion();
- if (!SecondThisRegion)
- return false;
- const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
- const auto *SecondInnerPtrVal =
- State->get<TrackedRegionMap>(SecondThisRegion);
- State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
- State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
- C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
- PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
- return;
- if (BR.isInteresting(FirstThisRegion) &&
- !BR.isInteresting(SecondThisRegion)) {
- BR.markInteresting(SecondThisRegion);
- BR.markNotInteresting(FirstThisRegion);
- }
- if (BR.isInteresting(SecondThisRegion) &&
- !BR.isInteresting(FirstThisRegion)) {
- BR.markInteresting(FirstThisRegion);
- BR.markNotInteresting(SecondThisRegion);
- }
- // TODO: We need to emit some note here probably!!
- }));
- return true;
- }
- void SmartPtrModeling::handleGet(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
- if (!IC)
- return;
- const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
- if (!ThisRegion)
- return;
- SVal InnerPointerVal;
- std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
- State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
- State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- InnerPointerVal);
- // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
- C.addTransition(State);
- }
- bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
- CheckerContext &C) const {
- ProgramStateRef State = C.getState();
- const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
- if (!OC)
- return false;
- OverloadedOperatorKind OOK = OC->getOverloadedOperator();
- if (OOK != OO_Equal)
- return false;
- const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
- if (!ThisRegion)
- return false;
- const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
- // In case of 'nullptr' or '0' assigned
- if (!OtherSmartPtrRegion) {
- bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
- if (!AssignedNull)
- return false;
- auto NullVal = C.getSValBuilder().makeNull();
- State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
- C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
- !BR.isInteresting(ThisRegion))
- return;
- OS << "Smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- OS << " is assigned to null";
- }));
- return true;
- }
- return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
- }
- bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
- const MemRegion *ThisRegion) const {
- const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
- if (!OtherSmartPtrRegion)
- return false;
- return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
- }
- bool SmartPtrModeling::updateMovedSmartPointers(
- CheckerContext &C, const MemRegion *ThisRegion,
- const MemRegion *OtherSmartPtrRegion) const {
- ProgramStateRef State = C.getState();
- const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
- if (OtherInnerPtr) {
- State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
- auto NullVal = C.getSValBuilder().makeNull();
- State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
- bool IsArgValNull = OtherInnerPtr->isZeroConstant();
- C.addTransition(
- State,
- C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
- PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
- return;
- if (BR.isInteresting(OtherSmartPtrRegion)) {
- OS << "Smart pointer";
- checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
- OS << " is null after being moved to";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- }
- if (BR.isInteresting(ThisRegion) && IsArgValNull) {
- OS << "A null pointer value is moved to";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- BR.markInteresting(OtherSmartPtrRegion);
- }
- }));
- return true;
- } else {
- // In case we dont know anything about value we are moving from
- // remove the entry from map for which smart pointer got moved to.
- auto NullVal = C.getSValBuilder().makeNull();
- State = State->remove<TrackedRegionMap>(ThisRegion);
- State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
- C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
- ThisRegion](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
- !BR.isInteresting(OtherSmartPtrRegion))
- return;
- OS << "Smart pointer";
- checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
- OS << " is null after; previous value moved to";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- }));
- return true;
- }
- return false;
- }
- void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
- CheckerContext &C) const {
- // To model unique_ptr::operator bool
- ProgramStateRef State = C.getState();
- const Expr *CallExpr = Call.getOriginExpr();
- const MemRegion *ThisRegion =
- cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
- SVal InnerPointerVal;
- if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
- InnerPointerVal = *InnerValPtr;
- } else {
- // In case of inner pointer SVal is not available we create
- // conjureSymbolVal for inner pointer value.
- auto InnerPointerType = getInnerPointerType(Call, C);
- if (InnerPointerType.isNull())
- return;
- const LocationContext *LC = C.getLocationContext();
- InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
- CallExpr, LC, InnerPointerType, C.blockCount());
- State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
- }
- if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
- State = State->BindExpr(CallExpr, C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(false));
- C.addTransition(State);
- return;
- } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
- State = State->BindExpr(CallExpr, C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(true));
- C.addTransition(State);
- return;
- } else if (move::isMovedFrom(State, ThisRegion)) {
- C.addTransition(
- State->BindExpr(CallExpr, C.getLocationContext(),
- C.getSValBuilder().makeZeroVal(Call.getResultType())));
- return;
- } else {
- ProgramStateRef NotNullState, NullState;
- std::tie(NotNullState, NullState) =
- State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
- auto NullVal = C.getSValBuilder().makeNull();
- // Explicitly tracking the region as null.
- NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
- NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(false));
- C.addTransition(NullState, C.getNoteTag(
- [ThisRegion](PathSensitiveBugReport &BR,
- llvm::raw_ostream &OS) {
- OS << "Assuming smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- OS << " is null";
- },
- /*IsPrunable=*/true));
- NotNullState =
- NotNullState->BindExpr(CallExpr, C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(true));
- C.addTransition(
- NotNullState,
- C.getNoteTag(
- [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
- OS << "Assuming smart pointer";
- checkAndPrettyPrintRegion(OS, ThisRegion);
- OS << " is non-null";
- },
- /*IsPrunable=*/true));
- return;
- }
- }
- void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
- auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
- Checker->ModelSmartPtrDereference =
- Mgr.getAnalyzerOptions().getCheckerBooleanOption(
- Checker, "ModelSmartPtrDereference");
- }
- bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
- const LangOptions &LO = mgr.getLangOpts();
- return LO.CPlusPlus;
- }
|