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