xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp (revision dd41de95a84d979615a2ef11df6850622bf6184e)
1 // SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a checker that models various aspects of
10 // C++ smart pointer behavior.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "Move.h"
15 #include "SmartPtr.h"
16 
17 #include "clang/AST/DeclCXX.h"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/AST/Type.h"
20 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
21 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
22 #include "clang/StaticAnalyzer/Core/Checker.h"
23 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
28 
29 using namespace clang;
30 using namespace ento;
31 
32 namespace {
33 class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> {
34 
35   bool isNullAfterMoveMethod(const CallEvent &Call) const;
36 
37 public:
38   // Whether the checker should model for null dereferences of smart pointers.
39   DefaultBool ModelSmartPtrDereference;
40   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
41   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
42   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
43 
44 private:
45   ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
46                                       const MemRegion *ThisValRegion) const;
47   void handleReset(const CallEvent &Call, CheckerContext &C) const;
48   void handleRelease(const CallEvent &Call, CheckerContext &C) const;
49   void handleSwap(const CallEvent &Call, CheckerContext &C) const;
50 
51   using SmartPtrMethodHandlerFn =
52       void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
53   CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
54       {{"reset"}, &SmartPtrModeling::handleReset},
55       {{"release"}, &SmartPtrModeling::handleRelease},
56       {{"swap", 1}, &SmartPtrModeling::handleSwap}};
57 };
58 } // end of anonymous namespace
59 
60 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
61 
62 // Define the inter-checker API.
63 namespace clang {
64 namespace ento {
65 namespace smartptr {
66 bool isStdSmartPtrCall(const CallEvent &Call) {
67   const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
68   if (!MethodDecl || !MethodDecl->getParent())
69     return false;
70 
71   const auto *RecordDecl = MethodDecl->getParent();
72   if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace())
73     return false;
74 
75   if (RecordDecl->getDeclName().isIdentifier()) {
76     StringRef Name = RecordDecl->getName();
77     return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
78   }
79   return false;
80 }
81 
82 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
83   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
84   return InnerPointVal && InnerPointVal->isZeroConstant();
85 }
86 } // namespace smartptr
87 } // namespace ento
88 } // namespace clang
89 
90 bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
91   // TODO: Update CallDescription to support anonymous calls?
92   // TODO: Handle other methods, such as .get() or .release().
93   // But once we do, we'd need a visitor to explain null dereferences
94   // that are found via such modeling.
95   const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
96   return CD && CD->getConversionType()->isBooleanType();
97 }
98 
99 bool SmartPtrModeling::evalCall(const CallEvent &Call,
100                                 CheckerContext &C) const {
101 
102   if (!smartptr::isStdSmartPtrCall(Call))
103     return false;
104 
105   if (isNullAfterMoveMethod(Call)) {
106     ProgramStateRef State = C.getState();
107     const MemRegion *ThisR =
108         cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
109 
110     if (!move::isMovedFrom(State, ThisR)) {
111       // TODO: Model this case as well. At least, avoid invalidation of globals.
112       return false;
113     }
114 
115     // TODO: Add a note to bug reports describing this decision.
116     C.addTransition(
117         State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
118                         C.getSValBuilder().makeZeroVal(Call.getResultType())));
119     return true;
120   }
121 
122   if (!ModelSmartPtrDereference)
123     return false;
124 
125   if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
126     if (CC->getDecl()->isCopyOrMoveConstructor())
127       return false;
128 
129     const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
130     if (!ThisValRegion)
131       return false;
132 
133     auto State = updateTrackedRegion(Call, C, ThisValRegion);
134     C.addTransition(State);
135     return true;
136   }
137 
138   const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
139   if (!Handler)
140     return false;
141   (this->**Handler)(Call, C);
142 
143   return C.isDifferent();
144 }
145 
146 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
147                                         CheckerContext &C) const {
148   ProgramStateRef State = C.getState();
149   // Clean up dead regions from the region map.
150   TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
151   for (auto E : TrackedRegions) {
152     const MemRegion *Region = E.first;
153     bool IsRegDead = !SymReaper.isLiveRegion(Region);
154 
155     if (IsRegDead)
156       State = State->remove<TrackedRegionMap>(Region);
157   }
158   C.addTransition(State);
159 }
160 
161 void SmartPtrModeling::handleReset(const CallEvent &Call,
162                                    CheckerContext &C) const {
163   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
164   if (!IC)
165     return;
166 
167   const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
168   if (!ThisValRegion)
169     return;
170   auto State = updateTrackedRegion(Call, C, ThisValRegion);
171   C.addTransition(State);
172   // TODO: Make sure to ivalidate the the region in the Store if we don't have
173   // time to model all methods.
174 }
175 
176 void SmartPtrModeling::handleRelease(const CallEvent &Call,
177                                      CheckerContext &C) const {
178   const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
179   if (!IC)
180     return;
181 
182   const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
183   if (!ThisValRegion)
184     return;
185 
186   auto State = updateTrackedRegion(Call, C, ThisValRegion);
187 
188   const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
189   if (InnerPointVal) {
190     State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
191                             *InnerPointVal);
192   }
193   C.addTransition(State);
194   // TODO: Add support to enable MallocChecker to start tracking the raw
195   // pointer.
196 }
197 
198 void SmartPtrModeling::handleSwap(const CallEvent &Call,
199                                   CheckerContext &C) const {
200   // TODO: Add support to handle swap method.
201 }
202 
203 ProgramStateRef
204 SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
205                                       const MemRegion *ThisValRegion) const {
206   // TODO: Refactor and clean up handling too many things.
207   ProgramStateRef State = C.getState();
208   auto NumArgs = Call.getNumArgs();
209 
210   if (NumArgs == 0) {
211     auto NullSVal = C.getSValBuilder().makeNull();
212     State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal);
213   } else if (NumArgs == 1) {
214     auto ArgVal = Call.getArgSVal(0);
215     assert(Call.getArgExpr(0)->getType()->isPointerType() &&
216            "Adding a non pointer value to TrackedRegionMap");
217     State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal);
218   }
219 
220   return State;
221 }
222 
223 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
224   auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
225   Checker->ModelSmartPtrDereference =
226       Mgr.getAnalyzerOptions().getCheckerBooleanOption(
227           Checker, "ModelSmartPtrDereference");
228 }
229 
230 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
231   const LangOptions &LO = mgr.getLangOpts();
232   return LO.CPlusPlus;
233 }
234