xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp (revision a9ac8606c53d55cee9c3a39778b249c51df111ef)
1e5dd7070Spatrick // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines a checker that models various aspects of
10e5dd7070Spatrick // C++ smart pointer behavior.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick 
14e5dd7070Spatrick #include "Move.h"
15ec727ea7Spatrick #include "SmartPtr.h"
16e5dd7070Spatrick 
17ec727ea7Spatrick #include "clang/AST/DeclCXX.h"
18*a9ac8606Spatrick #include "clang/AST/DeclarationName.h"
19e5dd7070Spatrick #include "clang/AST/ExprCXX.h"
20ec727ea7Spatrick #include "clang/AST/Type.h"
21*a9ac8606Spatrick #include "clang/Basic/LLVM.h"
22e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
27e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28*a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
29*a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
30ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
31ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
32*a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
33*a9ac8606Spatrick #include "llvm/ADT/StringMap.h"
34*a9ac8606Spatrick #include "llvm/Support/ErrorHandling.h"
35*a9ac8606Spatrick #include <string>
36e5dd7070Spatrick 
37e5dd7070Spatrick using namespace clang;
38e5dd7070Spatrick using namespace ento;
39e5dd7070Spatrick 
40e5dd7070Spatrick namespace {
41ec727ea7Spatrick 
42*a9ac8606Spatrick class SmartPtrModeling
43*a9ac8606Spatrick     : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
44*a9ac8606Spatrick                      check::LiveSymbols> {
45*a9ac8606Spatrick 
46*a9ac8606Spatrick   bool isBoolConversionMethod(const CallEvent &Call) const;
47e5dd7070Spatrick 
48e5dd7070Spatrick public:
49ec727ea7Spatrick   // Whether the checker should model for null dereferences of smart pointers.
50ec727ea7Spatrick   DefaultBool ModelSmartPtrDereference;
51e5dd7070Spatrick   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
52ec727ea7Spatrick   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53ec727ea7Spatrick   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
54*a9ac8606Spatrick   ProgramStateRef
55*a9ac8606Spatrick   checkRegionChanges(ProgramStateRef State,
56*a9ac8606Spatrick                      const InvalidatedSymbols *Invalidated,
57*a9ac8606Spatrick                      ArrayRef<const MemRegion *> ExplicitRegions,
58*a9ac8606Spatrick                      ArrayRef<const MemRegion *> Regions,
59*a9ac8606Spatrick                      const LocationContext *LCtx, const CallEvent *Call) const;
60*a9ac8606Spatrick   void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
61*a9ac8606Spatrick                   const char *Sep) const override;
62*a9ac8606Spatrick   void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
63ec727ea7Spatrick 
64ec727ea7Spatrick private:
65ec727ea7Spatrick   void handleReset(const CallEvent &Call, CheckerContext &C) const;
66ec727ea7Spatrick   void handleRelease(const CallEvent &Call, CheckerContext &C) const;
67*a9ac8606Spatrick   void handleSwapMethod(const CallEvent &Call, CheckerContext &C) const;
68*a9ac8606Spatrick   void handleGet(const CallEvent &Call, CheckerContext &C) const;
69*a9ac8606Spatrick   bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const;
70*a9ac8606Spatrick   bool handleMoveCtr(const CallEvent &Call, CheckerContext &C,
71*a9ac8606Spatrick                      const MemRegion *ThisRegion) const;
72*a9ac8606Spatrick   bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion,
73*a9ac8606Spatrick                                 const MemRegion *OtherSmartPtrRegion) const;
74*a9ac8606Spatrick   void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
75*a9ac8606Spatrick   bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
76*a9ac8606Spatrick   bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
77*a9ac8606Spatrick   bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
78*a9ac8606Spatrick                   CheckerContext &C) const;
79*a9ac8606Spatrick   std::pair<SVal, ProgramStateRef>
80*a9ac8606Spatrick   retrieveOrConjureInnerPtrVal(ProgramStateRef State,
81*a9ac8606Spatrick                                const MemRegion *ThisRegion, const Expr *E,
82*a9ac8606Spatrick                                QualType Type, CheckerContext &C) const;
83ec727ea7Spatrick 
84ec727ea7Spatrick   using SmartPtrMethodHandlerFn =
85ec727ea7Spatrick       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
86ec727ea7Spatrick   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
87ec727ea7Spatrick       {{"reset"}, &SmartPtrModeling::handleReset},
88ec727ea7Spatrick       {{"release"}, &SmartPtrModeling::handleRelease},
89*a9ac8606Spatrick       {{"swap", 1}, &SmartPtrModeling::handleSwapMethod},
90*a9ac8606Spatrick       {{"get"}, &SmartPtrModeling::handleGet}};
91*a9ac8606Spatrick   const CallDescription StdSwapCall{{"std", "swap"}, 2};
92*a9ac8606Spatrick   const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
93*a9ac8606Spatrick   const CallDescription StdMakeUniqueForOverwriteCall{
94*a9ac8606Spatrick       {"std", "make_unique_for_overwrite"}};
95e5dd7070Spatrick };
96e5dd7070Spatrick } // end of anonymous namespace
97e5dd7070Spatrick 
98ec727ea7Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
99ec727ea7Spatrick 
100*a9ac8606Spatrick // Checks if RD has name in Names and is in std namespace
101*a9ac8606Spatrick static bool hasStdClassWithName(const CXXRecordDecl *RD,
102*a9ac8606Spatrick                                 ArrayRef<llvm::StringLiteral> Names) {
103*a9ac8606Spatrick   if (!RD || !RD->getDeclContext()->isStdNamespace())
104*a9ac8606Spatrick     return false;
105*a9ac8606Spatrick   if (RD->getDeclName().isIdentifier()) {
106*a9ac8606Spatrick     StringRef Name = RD->getName();
107*a9ac8606Spatrick     return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool {
108*a9ac8606Spatrick       return Name == GivenName;
109*a9ac8606Spatrick     });
110*a9ac8606Spatrick   }
111*a9ac8606Spatrick   return false;
112*a9ac8606Spatrick }
113*a9ac8606Spatrick 
114*a9ac8606Spatrick constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
115*a9ac8606Spatrick                                                  "weak_ptr"};
116*a9ac8606Spatrick 
117*a9ac8606Spatrick static bool isStdSmartPtr(const CXXRecordDecl *RD) {
118*a9ac8606Spatrick   return hasStdClassWithName(RD, STD_PTR_NAMES);
119*a9ac8606Spatrick }
120*a9ac8606Spatrick 
121*a9ac8606Spatrick static bool isStdSmartPtr(const Expr *E) {
122*a9ac8606Spatrick   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
123*a9ac8606Spatrick }
124*a9ac8606Spatrick 
125ec727ea7Spatrick // Define the inter-checker API.
126ec727ea7Spatrick namespace clang {
127ec727ea7Spatrick namespace ento {
128ec727ea7Spatrick namespace smartptr {
129ec727ea7Spatrick bool isStdSmartPtrCall(const CallEvent &Call) {
130ec727ea7Spatrick   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
131ec727ea7Spatrick   if (!MethodDecl || !MethodDecl->getParent())
132ec727ea7Spatrick     return false;
133*a9ac8606Spatrick   return isStdSmartPtr(MethodDecl->getParent());
134*a9ac8606Spatrick }
135ec727ea7Spatrick 
136*a9ac8606Spatrick bool isStdSmartPtr(const CXXRecordDecl *RD) {
137*a9ac8606Spatrick   if (!RD || !RD->getDeclContext()->isStdNamespace())
138ec727ea7Spatrick     return false;
139ec727ea7Spatrick 
140*a9ac8606Spatrick   if (RD->getDeclName().isIdentifier()) {
141*a9ac8606Spatrick     StringRef Name = RD->getName();
142ec727ea7Spatrick     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
143ec727ea7Spatrick   }
144ec727ea7Spatrick   return false;
145ec727ea7Spatrick }
146ec727ea7Spatrick 
147*a9ac8606Spatrick bool isStdSmartPtr(const Expr *E) {
148*a9ac8606Spatrick   return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
149*a9ac8606Spatrick }
150*a9ac8606Spatrick 
151ec727ea7Spatrick bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
152ec727ea7Spatrick   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
153*a9ac8606Spatrick   return InnerPointVal &&
154*a9ac8606Spatrick          !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
155ec727ea7Spatrick }
156ec727ea7Spatrick } // namespace smartptr
157ec727ea7Spatrick } // namespace ento
158ec727ea7Spatrick } // namespace clang
159ec727ea7Spatrick 
160*a9ac8606Spatrick // If a region is removed all of the subregions need to be removed too.
161*a9ac8606Spatrick static TrackedRegionMapTy
162*a9ac8606Spatrick removeTrackedSubregions(TrackedRegionMapTy RegionMap,
163*a9ac8606Spatrick                         TrackedRegionMapTy::Factory &RegionMapFactory,
164*a9ac8606Spatrick                         const MemRegion *Region) {
165*a9ac8606Spatrick   if (!Region)
166*a9ac8606Spatrick     return RegionMap;
167*a9ac8606Spatrick   for (const auto &E : RegionMap) {
168*a9ac8606Spatrick     if (E.first->isSubRegionOf(Region))
169*a9ac8606Spatrick       RegionMap = RegionMapFactory.remove(RegionMap, E.first);
170*a9ac8606Spatrick   }
171*a9ac8606Spatrick   return RegionMap;
172*a9ac8606Spatrick }
173*a9ac8606Spatrick 
174*a9ac8606Spatrick static ProgramStateRef updateSwappedRegion(ProgramStateRef State,
175*a9ac8606Spatrick                                            const MemRegion *Region,
176*a9ac8606Spatrick                                            const SVal *RegionInnerPointerVal) {
177*a9ac8606Spatrick   if (RegionInnerPointerVal) {
178*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
179*a9ac8606Spatrick   } else {
180*a9ac8606Spatrick     State = State->remove<TrackedRegionMap>(Region);
181*a9ac8606Spatrick   }
182*a9ac8606Spatrick   return State;
183*a9ac8606Spatrick }
184*a9ac8606Spatrick 
185*a9ac8606Spatrick static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
186*a9ac8606Spatrick   if (!RD || !RD->isInStdNamespace())
187*a9ac8606Spatrick     return {};
188*a9ac8606Spatrick 
189*a9ac8606Spatrick   const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
190*a9ac8606Spatrick   if (!TSD)
191*a9ac8606Spatrick     return {};
192*a9ac8606Spatrick 
193*a9ac8606Spatrick   auto TemplateArgs = TSD->getTemplateArgs().asArray();
194*a9ac8606Spatrick   if (TemplateArgs.empty())
195*a9ac8606Spatrick     return {};
196*a9ac8606Spatrick   auto InnerValueType = TemplateArgs[0].getAsType();
197*a9ac8606Spatrick   return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
198*a9ac8606Spatrick }
199*a9ac8606Spatrick 
200*a9ac8606Spatrick // This is for use with standalone-functions like std::make_unique,
201*a9ac8606Spatrick // std::make_unique_for_overwrite, etc. It reads the template parameter and
202*a9ac8606Spatrick // returns the pointer type corresponding to it,
203*a9ac8606Spatrick static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
204*a9ac8606Spatrick                                               CheckerContext &C) {
205*a9ac8606Spatrick   const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
206*a9ac8606Spatrick   if (!FD || !FD->isFunctionTemplateSpecialization())
207*a9ac8606Spatrick     return {};
208*a9ac8606Spatrick   const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
209*a9ac8606Spatrick   if (TemplateArgs.size() == 0)
210*a9ac8606Spatrick     return {};
211*a9ac8606Spatrick   auto ValueType = TemplateArgs[0].getAsType();
212*a9ac8606Spatrick   return C.getASTContext().getPointerType(ValueType.getCanonicalType());
213*a9ac8606Spatrick }
214*a9ac8606Spatrick 
215*a9ac8606Spatrick // Helper method to get the inner pointer type of specialized smart pointer
216*a9ac8606Spatrick // Returns empty type if not found valid inner pointer type.
217*a9ac8606Spatrick static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
218*a9ac8606Spatrick   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
219*a9ac8606Spatrick   if (!MethodDecl || !MethodDecl->getParent())
220*a9ac8606Spatrick     return {};
221*a9ac8606Spatrick 
222*a9ac8606Spatrick   const auto *RecordDecl = MethodDecl->getParent();
223*a9ac8606Spatrick   return getInnerPointerType(C, RecordDecl);
224*a9ac8606Spatrick }
225*a9ac8606Spatrick 
226*a9ac8606Spatrick // Helper method to pretty print region and avoid extra spacing.
227*a9ac8606Spatrick static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
228*a9ac8606Spatrick                                       const MemRegion *Region) {
229*a9ac8606Spatrick   if (Region->canPrintPretty()) {
230*a9ac8606Spatrick     OS << " ";
231*a9ac8606Spatrick     Region->printPretty(OS);
232*a9ac8606Spatrick   }
233*a9ac8606Spatrick }
234*a9ac8606Spatrick 
235*a9ac8606Spatrick bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
236e5dd7070Spatrick   // TODO: Update CallDescription to support anonymous calls?
237e5dd7070Spatrick   // TODO: Handle other methods, such as .get() or .release().
238e5dd7070Spatrick   // But once we do, we'd need a visitor to explain null dereferences
239e5dd7070Spatrick   // that are found via such modeling.
240e5dd7070Spatrick   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
241e5dd7070Spatrick   return CD && CD->getConversionType()->isBooleanType();
242e5dd7070Spatrick }
243e5dd7070Spatrick 
244*a9ac8606Spatrick constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
245*a9ac8606Spatrick 
246*a9ac8606Spatrick bool isStdBasicOstream(const Expr *E) {
247*a9ac8606Spatrick   const auto *RD = E->getType()->getAsCXXRecordDecl();
248*a9ac8606Spatrick   return hasStdClassWithName(RD, BASIC_OSTREAM_NAMES);
249*a9ac8606Spatrick }
250*a9ac8606Spatrick 
251*a9ac8606Spatrick static bool isStdFunctionCall(const CallEvent &Call) {
252*a9ac8606Spatrick   return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
253*a9ac8606Spatrick }
254*a9ac8606Spatrick 
255*a9ac8606Spatrick bool isStdOstreamOperatorCall(const CallEvent &Call) {
256*a9ac8606Spatrick   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
257*a9ac8606Spatrick     return false;
258*a9ac8606Spatrick   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
259*a9ac8606Spatrick   if (!FC)
260*a9ac8606Spatrick     return false;
261*a9ac8606Spatrick   const FunctionDecl *FD = FC->getDecl();
262*a9ac8606Spatrick   if (!FD->isOverloadedOperator())
263*a9ac8606Spatrick     return false;
264*a9ac8606Spatrick   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
265*a9ac8606Spatrick   if (OOK != clang::OO_LessLess)
266*a9ac8606Spatrick     return false;
267*a9ac8606Spatrick   return isStdSmartPtr(Call.getArgExpr(1)) &&
268*a9ac8606Spatrick          isStdBasicOstream(Call.getArgExpr(0));
269*a9ac8606Spatrick }
270*a9ac8606Spatrick 
271*a9ac8606Spatrick static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
272*a9ac8606Spatrick   if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
273*a9ac8606Spatrick     return false;
274*a9ac8606Spatrick   return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
275*a9ac8606Spatrick          smartptr::isStdSmartPtr(Call.getArgExpr(1));
276*a9ac8606Spatrick }
277*a9ac8606Spatrick 
278e5dd7070Spatrick bool SmartPtrModeling::evalCall(const CallEvent &Call,
279e5dd7070Spatrick                                 CheckerContext &C) const {
280ec727ea7Spatrick 
281*a9ac8606Spatrick   ProgramStateRef State = C.getState();
282*a9ac8606Spatrick 
283*a9ac8606Spatrick   // If any one of the arg is a unique_ptr, then
284*a9ac8606Spatrick   // we can try this function
285*a9ac8606Spatrick   if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
286*a9ac8606Spatrick     if (handleComparisionOp(Call, C))
287*a9ac8606Spatrick       return true;
288*a9ac8606Spatrick 
289*a9ac8606Spatrick   if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
290*a9ac8606Spatrick     return handleOstreamOperator(Call, C);
291*a9ac8606Spatrick 
292*a9ac8606Spatrick   if (Call.isCalled(StdSwapCall)) {
293*a9ac8606Spatrick     // Check the first arg, if it is of std::unique_ptr type.
294*a9ac8606Spatrick     assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
295*a9ac8606Spatrick     const Expr *FirstArg = Call.getArgExpr(0);
296*a9ac8606Spatrick     if (!smartptr::isStdSmartPtr(FirstArg->getType()->getAsCXXRecordDecl()))
297*a9ac8606Spatrick       return false;
298*a9ac8606Spatrick     return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
299*a9ac8606Spatrick   }
300*a9ac8606Spatrick 
301*a9ac8606Spatrick   if (Call.isCalled(StdMakeUniqueCall) ||
302*a9ac8606Spatrick       Call.isCalled(StdMakeUniqueForOverwriteCall)) {
303*a9ac8606Spatrick     if (!ModelSmartPtrDereference)
304*a9ac8606Spatrick       return false;
305*a9ac8606Spatrick 
306*a9ac8606Spatrick     const Optional<SVal> ThisRegionOpt = Call.getReturnValueUnderConstruction();
307*a9ac8606Spatrick     if (!ThisRegionOpt)
308*a9ac8606Spatrick       return false;
309*a9ac8606Spatrick 
310*a9ac8606Spatrick     const auto PtrVal = C.getSValBuilder().getConjuredHeapSymbolVal(
311*a9ac8606Spatrick         Call.getOriginExpr(), C.getLocationContext(),
312*a9ac8606Spatrick         getPointerTypeFromTemplateArg(Call, C), C.blockCount());
313*a9ac8606Spatrick 
314*a9ac8606Spatrick     const MemRegion *ThisRegion = ThisRegionOpt->getAsRegion();
315*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(ThisRegion, PtrVal);
316*a9ac8606Spatrick     State = State->assume(PtrVal, true);
317*a9ac8606Spatrick 
318*a9ac8606Spatrick     // TODO: ExprEngine should do this for us.
319*a9ac8606Spatrick     // For a bit more context:
320*a9ac8606Spatrick     // 1) Why do we need this? Since we are modelling a "function"
321*a9ac8606Spatrick     // that returns a constructed object we need to store this information in
322*a9ac8606Spatrick     // the program state.
323*a9ac8606Spatrick     //
324*a9ac8606Spatrick     // 2) Why does this work?
325*a9ac8606Spatrick     // `updateObjectsUnderConstruction` does exactly as it sounds.
326*a9ac8606Spatrick     //
327*a9ac8606Spatrick     // 3) How should it look like when moved to the Engine?
328*a9ac8606Spatrick     // It would be nice if we can just
329*a9ac8606Spatrick     // pretend we don't need to know about this - ie, completely automatic work.
330*a9ac8606Spatrick     // However, realistically speaking, I think we would need to "signal" the
331*a9ac8606Spatrick     // ExprEngine evalCall handler that we are constructing an object with this
332*a9ac8606Spatrick     // function call (constructors obviously construct, hence can be
333*a9ac8606Spatrick     // automatically deduced).
334*a9ac8606Spatrick     auto &Engine = State->getStateManager().getOwningEngine();
335*a9ac8606Spatrick     State = Engine.updateObjectsUnderConstruction(
336*a9ac8606Spatrick         *ThisRegionOpt, nullptr, State, C.getLocationContext(),
337*a9ac8606Spatrick         Call.getConstructionContext(), {});
338*a9ac8606Spatrick 
339*a9ac8606Spatrick     // We don't leave a note here since it is guaranteed the
340*a9ac8606Spatrick     // unique_ptr from this call is non-null (hence is safe to de-reference).
341*a9ac8606Spatrick     C.addTransition(State);
342*a9ac8606Spatrick     return true;
343*a9ac8606Spatrick   }
344*a9ac8606Spatrick 
345ec727ea7Spatrick   if (!smartptr::isStdSmartPtrCall(Call))
346e5dd7070Spatrick     return false;
347e5dd7070Spatrick 
348*a9ac8606Spatrick   if (isBoolConversionMethod(Call)) {
349e5dd7070Spatrick     const MemRegion *ThisR =
350e5dd7070Spatrick         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
351e5dd7070Spatrick 
352*a9ac8606Spatrick     if (ModelSmartPtrDereference) {
353*a9ac8606Spatrick       // The check for the region is moved is duplicated in handleBoolOperation
354*a9ac8606Spatrick       // method.
355*a9ac8606Spatrick       // FIXME: Once we model std::move for smart pointers clean up this and use
356*a9ac8606Spatrick       // that modeling.
357*a9ac8606Spatrick       handleBoolConversion(Call, C);
358*a9ac8606Spatrick       return true;
359*a9ac8606Spatrick     } else {
360e5dd7070Spatrick       if (!move::isMovedFrom(State, ThisR)) {
361*a9ac8606Spatrick         // TODO: Model this case as well. At least, avoid invalidation of
362*a9ac8606Spatrick         // globals.
363e5dd7070Spatrick         return false;
364e5dd7070Spatrick       }
365e5dd7070Spatrick 
366e5dd7070Spatrick       // TODO: Add a note to bug reports describing this decision.
367*a9ac8606Spatrick       C.addTransition(State->BindExpr(
368*a9ac8606Spatrick           Call.getOriginExpr(), C.getLocationContext(),
369e5dd7070Spatrick           C.getSValBuilder().makeZeroVal(Call.getResultType())));
370*a9ac8606Spatrick 
371e5dd7070Spatrick       return true;
372e5dd7070Spatrick     }
373*a9ac8606Spatrick   }
374e5dd7070Spatrick 
375ec727ea7Spatrick   if (!ModelSmartPtrDereference)
376ec727ea7Spatrick     return false;
377ec727ea7Spatrick 
378ec727ea7Spatrick   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
379*a9ac8606Spatrick     if (CC->getDecl()->isCopyConstructor())
380ec727ea7Spatrick       return false;
381ec727ea7Spatrick 
382*a9ac8606Spatrick     const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
383*a9ac8606Spatrick     if (!ThisRegion)
384ec727ea7Spatrick       return false;
385ec727ea7Spatrick 
386*a9ac8606Spatrick     if (CC->getDecl()->isMoveConstructor())
387*a9ac8606Spatrick       return handleMoveCtr(Call, C, ThisRegion);
388*a9ac8606Spatrick 
389*a9ac8606Spatrick     if (Call.getNumArgs() == 0) {
390*a9ac8606Spatrick       auto NullVal = C.getSValBuilder().makeNull();
391*a9ac8606Spatrick       State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
392*a9ac8606Spatrick 
393*a9ac8606Spatrick       C.addTransition(
394*a9ac8606Spatrick           State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
395*a9ac8606Spatrick                                            llvm::raw_ostream &OS) {
396*a9ac8606Spatrick             if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
397*a9ac8606Spatrick                 !BR.isInteresting(ThisRegion))
398*a9ac8606Spatrick               return;
399*a9ac8606Spatrick             OS << "Default constructed smart pointer";
400*a9ac8606Spatrick             checkAndPrettyPrintRegion(OS, ThisRegion);
401*a9ac8606Spatrick             OS << " is null";
402*a9ac8606Spatrick           }));
403*a9ac8606Spatrick     } else {
404*a9ac8606Spatrick       const auto *TrackingExpr = Call.getArgExpr(0);
405*a9ac8606Spatrick       assert(TrackingExpr->getType()->isPointerType() &&
406*a9ac8606Spatrick              "Adding a non pointer value to TrackedRegionMap");
407*a9ac8606Spatrick       auto ArgVal = Call.getArgSVal(0);
408*a9ac8606Spatrick       State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
409*a9ac8606Spatrick 
410*a9ac8606Spatrick       C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
411*a9ac8606Spatrick                                            ArgVal](PathSensitiveBugReport &BR,
412*a9ac8606Spatrick                                                    llvm::raw_ostream &OS) {
413*a9ac8606Spatrick         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
414*a9ac8606Spatrick             !BR.isInteresting(ThisRegion))
415*a9ac8606Spatrick           return;
416*a9ac8606Spatrick         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
417*a9ac8606Spatrick         OS << "Smart pointer";
418*a9ac8606Spatrick         checkAndPrettyPrintRegion(OS, ThisRegion);
419*a9ac8606Spatrick         if (ArgVal.isZeroConstant())
420*a9ac8606Spatrick           OS << " is constructed using a null value";
421*a9ac8606Spatrick         else
422*a9ac8606Spatrick           OS << " is constructed";
423*a9ac8606Spatrick       }));
424*a9ac8606Spatrick     }
425ec727ea7Spatrick     return true;
426e5dd7070Spatrick   }
427e5dd7070Spatrick 
428*a9ac8606Spatrick   if (handleAssignOp(Call, C))
429*a9ac8606Spatrick     return true;
430*a9ac8606Spatrick 
431ec727ea7Spatrick   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
432ec727ea7Spatrick   if (!Handler)
433ec727ea7Spatrick     return false;
434ec727ea7Spatrick   (this->**Handler)(Call, C);
435ec727ea7Spatrick 
436ec727ea7Spatrick   return C.isDifferent();
437ec727ea7Spatrick }
438ec727ea7Spatrick 
439*a9ac8606Spatrick std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
440*a9ac8606Spatrick     ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
441*a9ac8606Spatrick     QualType Type, CheckerContext &C) const {
442*a9ac8606Spatrick   const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
443*a9ac8606Spatrick   if (Ptr)
444*a9ac8606Spatrick     return {*Ptr, State};
445*a9ac8606Spatrick   auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
446*a9ac8606Spatrick                                                  Type, C.blockCount());
447*a9ac8606Spatrick   State = State->set<TrackedRegionMap>(ThisRegion, Val);
448*a9ac8606Spatrick   return {Val, State};
449*a9ac8606Spatrick }
450*a9ac8606Spatrick 
451*a9ac8606Spatrick bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
452*a9ac8606Spatrick                                            CheckerContext &C) const {
453*a9ac8606Spatrick   const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
454*a9ac8606Spatrick   if (!FC)
455*a9ac8606Spatrick     return false;
456*a9ac8606Spatrick   const FunctionDecl *FD = FC->getDecl();
457*a9ac8606Spatrick   if (!FD->isOverloadedOperator())
458*a9ac8606Spatrick     return false;
459*a9ac8606Spatrick   const OverloadedOperatorKind OOK = FD->getOverloadedOperator();
460*a9ac8606Spatrick   if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
461*a9ac8606Spatrick         OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
462*a9ac8606Spatrick         OOK == OO_Spaceship))
463*a9ac8606Spatrick     return false;
464*a9ac8606Spatrick 
465*a9ac8606Spatrick   // There are some special cases about which we can infer about
466*a9ac8606Spatrick   // the resulting answer.
467*a9ac8606Spatrick   // For reference, there is a discussion at https://reviews.llvm.org/D104616.
468*a9ac8606Spatrick   // Also, the cppreference page is good to look at
469*a9ac8606Spatrick   // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
470*a9ac8606Spatrick 
471*a9ac8606Spatrick   auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
472*a9ac8606Spatrick                                 SVal S) -> std::pair<SVal, ProgramStateRef> {
473*a9ac8606Spatrick     if (S.isZeroConstant()) {
474*a9ac8606Spatrick       return {S, State};
475*a9ac8606Spatrick     }
476*a9ac8606Spatrick     const MemRegion *Reg = S.getAsRegion();
477*a9ac8606Spatrick     assert(Reg &&
478*a9ac8606Spatrick            "this pointer of std::unique_ptr should be obtainable as MemRegion");
479*a9ac8606Spatrick     QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
480*a9ac8606Spatrick     return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
481*a9ac8606Spatrick   };
482*a9ac8606Spatrick 
483*a9ac8606Spatrick   SVal First = Call.getArgSVal(0);
484*a9ac8606Spatrick   SVal Second = Call.getArgSVal(1);
485*a9ac8606Spatrick   const auto *FirstExpr = Call.getArgExpr(0);
486*a9ac8606Spatrick   const auto *SecondExpr = Call.getArgExpr(1);
487*a9ac8606Spatrick 
488*a9ac8606Spatrick   const auto *ResultExpr = Call.getOriginExpr();
489*a9ac8606Spatrick   const auto *LCtx = C.getLocationContext();
490*a9ac8606Spatrick   auto &Bldr = C.getSValBuilder();
491*a9ac8606Spatrick   ProgramStateRef State = C.getState();
492*a9ac8606Spatrick 
493*a9ac8606Spatrick   SVal FirstPtrVal, SecondPtrVal;
494*a9ac8606Spatrick   std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
495*a9ac8606Spatrick   std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
496*a9ac8606Spatrick   BinaryOperatorKind BOK =
497*a9ac8606Spatrick       operationKindFromOverloadedOperator(OOK, true).GetBinaryOpUnsafe();
498*a9ac8606Spatrick   auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
499*a9ac8606Spatrick                                Call.getResultType());
500*a9ac8606Spatrick 
501*a9ac8606Spatrick   if (OOK != OO_Spaceship) {
502*a9ac8606Spatrick     ProgramStateRef TrueState, FalseState;
503*a9ac8606Spatrick     std::tie(TrueState, FalseState) =
504*a9ac8606Spatrick         State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
505*a9ac8606Spatrick     if (TrueState)
506*a9ac8606Spatrick       C.addTransition(
507*a9ac8606Spatrick           TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
508*a9ac8606Spatrick     if (FalseState)
509*a9ac8606Spatrick       C.addTransition(
510*a9ac8606Spatrick           FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
511*a9ac8606Spatrick   } else {
512*a9ac8606Spatrick     C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
513*a9ac8606Spatrick   }
514*a9ac8606Spatrick   return true;
515*a9ac8606Spatrick }
516*a9ac8606Spatrick 
517*a9ac8606Spatrick bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
518*a9ac8606Spatrick                                              CheckerContext &C) const {
519*a9ac8606Spatrick   // operator<< does not modify the smart pointer.
520*a9ac8606Spatrick   // And we don't really have much of modelling of basic_ostream.
521*a9ac8606Spatrick   // So, we are better off:
522*a9ac8606Spatrick   // 1) Invalidating the mem-region of the ostream object at hand.
523*a9ac8606Spatrick   // 2) Setting the SVal of the basic_ostream as the return value.
524*a9ac8606Spatrick   // Not very satisfying, but it gets the job done, and is better
525*a9ac8606Spatrick   // than the default handling. :)
526*a9ac8606Spatrick 
527*a9ac8606Spatrick   ProgramStateRef State = C.getState();
528*a9ac8606Spatrick   const auto StreamVal = Call.getArgSVal(0);
529*a9ac8606Spatrick   const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
530*a9ac8606Spatrick   if (!StreamThisRegion)
531*a9ac8606Spatrick     return false;
532*a9ac8606Spatrick   State =
533*a9ac8606Spatrick       State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
534*a9ac8606Spatrick                                C.blockCount(), C.getLocationContext(), false);
535*a9ac8606Spatrick   State =
536*a9ac8606Spatrick       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
537*a9ac8606Spatrick   C.addTransition(State);
538*a9ac8606Spatrick   return true;
539*a9ac8606Spatrick }
540*a9ac8606Spatrick 
541ec727ea7Spatrick void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
542ec727ea7Spatrick                                         CheckerContext &C) const {
543ec727ea7Spatrick   ProgramStateRef State = C.getState();
544ec727ea7Spatrick   // Clean up dead regions from the region map.
545ec727ea7Spatrick   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
546ec727ea7Spatrick   for (auto E : TrackedRegions) {
547ec727ea7Spatrick     const MemRegion *Region = E.first;
548ec727ea7Spatrick     bool IsRegDead = !SymReaper.isLiveRegion(Region);
549ec727ea7Spatrick 
550ec727ea7Spatrick     if (IsRegDead)
551ec727ea7Spatrick       State = State->remove<TrackedRegionMap>(Region);
552ec727ea7Spatrick   }
553ec727ea7Spatrick   C.addTransition(State);
554ec727ea7Spatrick }
555ec727ea7Spatrick 
556*a9ac8606Spatrick void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
557*a9ac8606Spatrick                                   const char *NL, const char *Sep) const {
558*a9ac8606Spatrick   TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
559*a9ac8606Spatrick 
560*a9ac8606Spatrick   if (!RS.isEmpty()) {
561*a9ac8606Spatrick     Out << Sep << "Smart ptr regions :" << NL;
562*a9ac8606Spatrick     for (auto I : RS) {
563*a9ac8606Spatrick       I.first->dumpToStream(Out);
564*a9ac8606Spatrick       if (smartptr::isNullSmartPtr(State, I.first))
565*a9ac8606Spatrick         Out << ": Null";
566*a9ac8606Spatrick       else
567*a9ac8606Spatrick         Out << ": Non Null";
568*a9ac8606Spatrick       Out << NL;
569*a9ac8606Spatrick     }
570*a9ac8606Spatrick   }
571*a9ac8606Spatrick }
572*a9ac8606Spatrick 
573*a9ac8606Spatrick ProgramStateRef SmartPtrModeling::checkRegionChanges(
574*a9ac8606Spatrick     ProgramStateRef State, const InvalidatedSymbols *Invalidated,
575*a9ac8606Spatrick     ArrayRef<const MemRegion *> ExplicitRegions,
576*a9ac8606Spatrick     ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
577*a9ac8606Spatrick     const CallEvent *Call) const {
578*a9ac8606Spatrick   TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
579*a9ac8606Spatrick   TrackedRegionMapTy::Factory &RegionMapFactory =
580*a9ac8606Spatrick       State->get_context<TrackedRegionMap>();
581*a9ac8606Spatrick   for (const auto *Region : Regions)
582*a9ac8606Spatrick     RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
583*a9ac8606Spatrick                                         Region->getBaseRegion());
584*a9ac8606Spatrick   return State->set<TrackedRegionMap>(RegionMap);
585*a9ac8606Spatrick }
586*a9ac8606Spatrick 
587*a9ac8606Spatrick void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
588*a9ac8606Spatrick                                         SymbolReaper &SR) const {
589*a9ac8606Spatrick   // Marking tracked symbols alive
590*a9ac8606Spatrick   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
591*a9ac8606Spatrick   for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
592*a9ac8606Spatrick     SVal Val = I->second;
593*a9ac8606Spatrick     for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
594*a9ac8606Spatrick       SR.markLive(*si);
595*a9ac8606Spatrick     }
596*a9ac8606Spatrick   }
597*a9ac8606Spatrick }
598*a9ac8606Spatrick 
599ec727ea7Spatrick void SmartPtrModeling::handleReset(const CallEvent &Call,
600ec727ea7Spatrick                                    CheckerContext &C) const {
601*a9ac8606Spatrick   ProgramStateRef State = C.getState();
602ec727ea7Spatrick   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
603ec727ea7Spatrick   if (!IC)
604ec727ea7Spatrick     return;
605ec727ea7Spatrick 
606*a9ac8606Spatrick   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
607*a9ac8606Spatrick   if (!ThisRegion)
608ec727ea7Spatrick     return;
609*a9ac8606Spatrick 
610*a9ac8606Spatrick   assert(Call.getArgExpr(0)->getType()->isPointerType() &&
611*a9ac8606Spatrick          "Adding a non pointer value to TrackedRegionMap");
612*a9ac8606Spatrick   State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
613*a9ac8606Spatrick   const auto *TrackingExpr = Call.getArgExpr(0);
614*a9ac8606Spatrick   C.addTransition(
615*a9ac8606Spatrick       State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
616*a9ac8606Spatrick                                                      llvm::raw_ostream &OS) {
617*a9ac8606Spatrick         if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
618*a9ac8606Spatrick             !BR.isInteresting(ThisRegion))
619*a9ac8606Spatrick           return;
620*a9ac8606Spatrick         bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
621*a9ac8606Spatrick         OS << "Smart pointer";
622*a9ac8606Spatrick         checkAndPrettyPrintRegion(OS, ThisRegion);
623*a9ac8606Spatrick         OS << " reset using a null value";
624*a9ac8606Spatrick       }));
625*a9ac8606Spatrick   // TODO: Make sure to ivalidate the region in the Store if we don't have
626ec727ea7Spatrick   // time to model all methods.
627ec727ea7Spatrick }
628ec727ea7Spatrick 
629ec727ea7Spatrick void SmartPtrModeling::handleRelease(const CallEvent &Call,
630ec727ea7Spatrick                                      CheckerContext &C) const {
631*a9ac8606Spatrick   ProgramStateRef State = C.getState();
632ec727ea7Spatrick   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
633ec727ea7Spatrick   if (!IC)
634ec727ea7Spatrick     return;
635ec727ea7Spatrick 
636*a9ac8606Spatrick   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
637*a9ac8606Spatrick   if (!ThisRegion)
638ec727ea7Spatrick     return;
639ec727ea7Spatrick 
640*a9ac8606Spatrick   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
641ec727ea7Spatrick 
642ec727ea7Spatrick   if (InnerPointVal) {
643ec727ea7Spatrick     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
644ec727ea7Spatrick                             *InnerPointVal);
645ec727ea7Spatrick   }
646*a9ac8606Spatrick 
647*a9ac8606Spatrick   auto ValueToUpdate = C.getSValBuilder().makeNull();
648*a9ac8606Spatrick   State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
649*a9ac8606Spatrick 
650*a9ac8606Spatrick   C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
651*a9ac8606Spatrick                                                    llvm::raw_ostream &OS) {
652*a9ac8606Spatrick     if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
653*a9ac8606Spatrick         !BR.isInteresting(ThisRegion))
654*a9ac8606Spatrick       return;
655*a9ac8606Spatrick 
656*a9ac8606Spatrick     OS << "Smart pointer";
657*a9ac8606Spatrick     checkAndPrettyPrintRegion(OS, ThisRegion);
658*a9ac8606Spatrick     OS << " is released and set to null";
659*a9ac8606Spatrick   }));
660ec727ea7Spatrick   // TODO: Add support to enable MallocChecker to start tracking the raw
661ec727ea7Spatrick   // pointer.
662ec727ea7Spatrick }
663ec727ea7Spatrick 
664*a9ac8606Spatrick void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
665ec727ea7Spatrick                                         CheckerContext &C) const {
666*a9ac8606Spatrick   // To model unique_ptr::swap() method.
667*a9ac8606Spatrick   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
668*a9ac8606Spatrick   if (!IC)
669*a9ac8606Spatrick     return;
670*a9ac8606Spatrick 
671*a9ac8606Spatrick   auto State = C.getState();
672*a9ac8606Spatrick   handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
673ec727ea7Spatrick }
674ec727ea7Spatrick 
675*a9ac8606Spatrick bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
676*a9ac8606Spatrick                                   SVal Second, CheckerContext &C) const {
677*a9ac8606Spatrick   const MemRegion *FirstThisRegion = First.getAsRegion();
678*a9ac8606Spatrick   if (!FirstThisRegion)
679*a9ac8606Spatrick     return false;
680*a9ac8606Spatrick   const MemRegion *SecondThisRegion = Second.getAsRegion();
681*a9ac8606Spatrick   if (!SecondThisRegion)
682*a9ac8606Spatrick     return false;
683*a9ac8606Spatrick 
684*a9ac8606Spatrick   const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
685*a9ac8606Spatrick   const auto *SecondInnerPtrVal =
686*a9ac8606Spatrick       State->get<TrackedRegionMap>(SecondThisRegion);
687*a9ac8606Spatrick 
688*a9ac8606Spatrick   State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
689*a9ac8606Spatrick   State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
690*a9ac8606Spatrick 
691*a9ac8606Spatrick   C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
692*a9ac8606Spatrick                                           PathSensitiveBugReport &BR,
693*a9ac8606Spatrick                                           llvm::raw_ostream &OS) {
694*a9ac8606Spatrick     if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
695*a9ac8606Spatrick       return;
696*a9ac8606Spatrick     if (BR.isInteresting(FirstThisRegion) &&
697*a9ac8606Spatrick         !BR.isInteresting(SecondThisRegion)) {
698*a9ac8606Spatrick       BR.markInteresting(SecondThisRegion);
699*a9ac8606Spatrick       BR.markNotInteresting(FirstThisRegion);
700*a9ac8606Spatrick     }
701*a9ac8606Spatrick     if (BR.isInteresting(SecondThisRegion) &&
702*a9ac8606Spatrick         !BR.isInteresting(FirstThisRegion)) {
703*a9ac8606Spatrick       BR.markInteresting(FirstThisRegion);
704*a9ac8606Spatrick       BR.markNotInteresting(SecondThisRegion);
705*a9ac8606Spatrick     }
706*a9ac8606Spatrick     // TODO: We need to emit some note here probably!!
707*a9ac8606Spatrick   }));
708*a9ac8606Spatrick 
709*a9ac8606Spatrick   return true;
710*a9ac8606Spatrick }
711*a9ac8606Spatrick 
712*a9ac8606Spatrick void SmartPtrModeling::handleGet(const CallEvent &Call,
713*a9ac8606Spatrick                                  CheckerContext &C) const {
714ec727ea7Spatrick   ProgramStateRef State = C.getState();
715*a9ac8606Spatrick   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
716*a9ac8606Spatrick   if (!IC)
717*a9ac8606Spatrick     return;
718ec727ea7Spatrick 
719*a9ac8606Spatrick   const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
720*a9ac8606Spatrick   if (!ThisRegion)
721*a9ac8606Spatrick     return;
722*a9ac8606Spatrick 
723*a9ac8606Spatrick   SVal InnerPointerVal;
724*a9ac8606Spatrick   std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
725*a9ac8606Spatrick       State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
726*a9ac8606Spatrick   State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
727*a9ac8606Spatrick                           InnerPointerVal);
728*a9ac8606Spatrick   // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
729*a9ac8606Spatrick   C.addTransition(State);
730ec727ea7Spatrick }
731ec727ea7Spatrick 
732*a9ac8606Spatrick bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
733*a9ac8606Spatrick                                       CheckerContext &C) const {
734*a9ac8606Spatrick   ProgramStateRef State = C.getState();
735*a9ac8606Spatrick   const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
736*a9ac8606Spatrick   if (!OC)
737*a9ac8606Spatrick     return false;
738*a9ac8606Spatrick   OverloadedOperatorKind OOK = OC->getOverloadedOperator();
739*a9ac8606Spatrick   if (OOK != OO_Equal)
740*a9ac8606Spatrick     return false;
741*a9ac8606Spatrick   const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
742*a9ac8606Spatrick   if (!ThisRegion)
743*a9ac8606Spatrick     return false;
744*a9ac8606Spatrick 
745*a9ac8606Spatrick   const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
746*a9ac8606Spatrick   // In case of 'nullptr' or '0' assigned
747*a9ac8606Spatrick   if (!OtherSmartPtrRegion) {
748*a9ac8606Spatrick     bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
749*a9ac8606Spatrick     if (!AssignedNull)
750*a9ac8606Spatrick       return false;
751*a9ac8606Spatrick     auto NullVal = C.getSValBuilder().makeNull();
752*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
753*a9ac8606Spatrick     C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
754*a9ac8606Spatrick                                                      llvm::raw_ostream &OS) {
755*a9ac8606Spatrick       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
756*a9ac8606Spatrick           !BR.isInteresting(ThisRegion))
757*a9ac8606Spatrick         return;
758*a9ac8606Spatrick       OS << "Smart pointer";
759*a9ac8606Spatrick       checkAndPrettyPrintRegion(OS, ThisRegion);
760*a9ac8606Spatrick       OS << " is assigned to null";
761*a9ac8606Spatrick     }));
762*a9ac8606Spatrick     return true;
763*a9ac8606Spatrick   }
764*a9ac8606Spatrick 
765*a9ac8606Spatrick   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
766*a9ac8606Spatrick }
767*a9ac8606Spatrick 
768*a9ac8606Spatrick bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
769*a9ac8606Spatrick                                      const MemRegion *ThisRegion) const {
770*a9ac8606Spatrick   const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
771*a9ac8606Spatrick   if (!OtherSmartPtrRegion)
772*a9ac8606Spatrick     return false;
773*a9ac8606Spatrick 
774*a9ac8606Spatrick   return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
775*a9ac8606Spatrick }
776*a9ac8606Spatrick 
777*a9ac8606Spatrick bool SmartPtrModeling::updateMovedSmartPointers(
778*a9ac8606Spatrick     CheckerContext &C, const MemRegion *ThisRegion,
779*a9ac8606Spatrick     const MemRegion *OtherSmartPtrRegion) const {
780*a9ac8606Spatrick   ProgramStateRef State = C.getState();
781*a9ac8606Spatrick   const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
782*a9ac8606Spatrick   if (OtherInnerPtr) {
783*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
784*a9ac8606Spatrick     auto NullVal = C.getSValBuilder().makeNull();
785*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
786*a9ac8606Spatrick     bool IsArgValNull = OtherInnerPtr->isZeroConstant();
787*a9ac8606Spatrick 
788*a9ac8606Spatrick     C.addTransition(
789*a9ac8606Spatrick         State,
790*a9ac8606Spatrick         C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
791*a9ac8606Spatrick                          PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
792*a9ac8606Spatrick           if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
793*a9ac8606Spatrick             return;
794*a9ac8606Spatrick           if (BR.isInteresting(OtherSmartPtrRegion)) {
795*a9ac8606Spatrick             OS << "Smart pointer";
796*a9ac8606Spatrick             checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
797*a9ac8606Spatrick             OS << " is null after being moved to";
798*a9ac8606Spatrick             checkAndPrettyPrintRegion(OS, ThisRegion);
799*a9ac8606Spatrick           }
800*a9ac8606Spatrick           if (BR.isInteresting(ThisRegion) && IsArgValNull) {
801*a9ac8606Spatrick             OS << "A null pointer value is moved to";
802*a9ac8606Spatrick             checkAndPrettyPrintRegion(OS, ThisRegion);
803*a9ac8606Spatrick             BR.markInteresting(OtherSmartPtrRegion);
804*a9ac8606Spatrick           }
805*a9ac8606Spatrick         }));
806*a9ac8606Spatrick     return true;
807*a9ac8606Spatrick   } else {
808*a9ac8606Spatrick     // In case we dont know anything about value we are moving from
809*a9ac8606Spatrick     // remove the entry from map for which smart pointer got moved to.
810*a9ac8606Spatrick     auto NullVal = C.getSValBuilder().makeNull();
811*a9ac8606Spatrick     State = State->remove<TrackedRegionMap>(ThisRegion);
812*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
813*a9ac8606Spatrick     C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
814*a9ac8606Spatrick                                          ThisRegion](PathSensitiveBugReport &BR,
815*a9ac8606Spatrick                                                      llvm::raw_ostream &OS) {
816*a9ac8606Spatrick       if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
817*a9ac8606Spatrick           !BR.isInteresting(OtherSmartPtrRegion))
818*a9ac8606Spatrick         return;
819*a9ac8606Spatrick       OS << "Smart pointer";
820*a9ac8606Spatrick       checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
821*a9ac8606Spatrick       OS << " is null after; previous value moved to";
822*a9ac8606Spatrick       checkAndPrettyPrintRegion(OS, ThisRegion);
823*a9ac8606Spatrick     }));
824*a9ac8606Spatrick     return true;
825*a9ac8606Spatrick   }
826*a9ac8606Spatrick   return false;
827*a9ac8606Spatrick }
828*a9ac8606Spatrick 
829*a9ac8606Spatrick void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
830*a9ac8606Spatrick                                             CheckerContext &C) const {
831*a9ac8606Spatrick   // To model unique_ptr::operator bool
832*a9ac8606Spatrick   ProgramStateRef State = C.getState();
833*a9ac8606Spatrick   const Expr *CallExpr = Call.getOriginExpr();
834*a9ac8606Spatrick   const MemRegion *ThisRegion =
835*a9ac8606Spatrick       cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
836*a9ac8606Spatrick 
837*a9ac8606Spatrick   SVal InnerPointerVal;
838*a9ac8606Spatrick   if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
839*a9ac8606Spatrick     InnerPointerVal = *InnerValPtr;
840*a9ac8606Spatrick   } else {
841*a9ac8606Spatrick     // In case of inner pointer SVal is not available we create
842*a9ac8606Spatrick     // conjureSymbolVal for inner pointer value.
843*a9ac8606Spatrick     auto InnerPointerType = getInnerPointerType(Call, C);
844*a9ac8606Spatrick     if (InnerPointerType.isNull())
845*a9ac8606Spatrick       return;
846*a9ac8606Spatrick 
847*a9ac8606Spatrick     const LocationContext *LC = C.getLocationContext();
848*a9ac8606Spatrick     InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
849*a9ac8606Spatrick         CallExpr, LC, InnerPointerType, C.blockCount());
850*a9ac8606Spatrick     State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
851*a9ac8606Spatrick   }
852*a9ac8606Spatrick 
853*a9ac8606Spatrick   if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
854*a9ac8606Spatrick     State = State->BindExpr(CallExpr, C.getLocationContext(),
855*a9ac8606Spatrick                             C.getSValBuilder().makeTruthVal(false));
856*a9ac8606Spatrick 
857*a9ac8606Spatrick     C.addTransition(State);
858*a9ac8606Spatrick     return;
859*a9ac8606Spatrick   } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
860*a9ac8606Spatrick     State = State->BindExpr(CallExpr, C.getLocationContext(),
861*a9ac8606Spatrick                             C.getSValBuilder().makeTruthVal(true));
862*a9ac8606Spatrick 
863*a9ac8606Spatrick     C.addTransition(State);
864*a9ac8606Spatrick     return;
865*a9ac8606Spatrick   } else if (move::isMovedFrom(State, ThisRegion)) {
866*a9ac8606Spatrick     C.addTransition(
867*a9ac8606Spatrick         State->BindExpr(CallExpr, C.getLocationContext(),
868*a9ac8606Spatrick                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
869*a9ac8606Spatrick     return;
870*a9ac8606Spatrick   } else {
871*a9ac8606Spatrick     ProgramStateRef NotNullState, NullState;
872*a9ac8606Spatrick     std::tie(NotNullState, NullState) =
873*a9ac8606Spatrick         State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
874*a9ac8606Spatrick 
875*a9ac8606Spatrick     auto NullVal = C.getSValBuilder().makeNull();
876*a9ac8606Spatrick     // Explicitly tracking the region as null.
877*a9ac8606Spatrick     NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
878*a9ac8606Spatrick 
879*a9ac8606Spatrick     NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
880*a9ac8606Spatrick                                     C.getSValBuilder().makeTruthVal(false));
881*a9ac8606Spatrick     C.addTransition(NullState, C.getNoteTag(
882*a9ac8606Spatrick                                    [ThisRegion](PathSensitiveBugReport &BR,
883*a9ac8606Spatrick                                                 llvm::raw_ostream &OS) {
884*a9ac8606Spatrick                                      OS << "Assuming smart pointer";
885*a9ac8606Spatrick                                      checkAndPrettyPrintRegion(OS, ThisRegion);
886*a9ac8606Spatrick                                      OS << " is null";
887*a9ac8606Spatrick                                    },
888*a9ac8606Spatrick                                    /*IsPrunable=*/true));
889*a9ac8606Spatrick     NotNullState =
890*a9ac8606Spatrick         NotNullState->BindExpr(CallExpr, C.getLocationContext(),
891*a9ac8606Spatrick                                C.getSValBuilder().makeTruthVal(true));
892*a9ac8606Spatrick     C.addTransition(
893*a9ac8606Spatrick         NotNullState,
894*a9ac8606Spatrick         C.getNoteTag(
895*a9ac8606Spatrick             [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
896*a9ac8606Spatrick               OS << "Assuming smart pointer";
897*a9ac8606Spatrick               checkAndPrettyPrintRegion(OS, ThisRegion);
898*a9ac8606Spatrick               OS << " is non-null";
899*a9ac8606Spatrick             },
900*a9ac8606Spatrick             /*IsPrunable=*/true));
901*a9ac8606Spatrick     return;
902*a9ac8606Spatrick   }
903ec727ea7Spatrick }
904ec727ea7Spatrick 
905ec727ea7Spatrick void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
906ec727ea7Spatrick   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
907ec727ea7Spatrick   Checker->ModelSmartPtrDereference =
908ec727ea7Spatrick       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
909ec727ea7Spatrick           Checker, "ModelSmartPtrDereference");
910ec727ea7Spatrick }
911ec727ea7Spatrick 
912ec727ea7Spatrick bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
913ec727ea7Spatrick   const LangOptions &LO = mgr.getLangOpts();
914e5dd7070Spatrick   return LO.CPlusPlus;
915e5dd7070Spatrick }
916