/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/CallEvent.h" |
27 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
28 | | #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" |
29 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" |
30 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" |
31 | | #include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" |
32 | | #include <string> |
33 | | |
34 | | using namespace clang; |
35 | | using namespace ento; |
36 | | |
37 | | namespace { |
38 | | class SmartPtrModeling |
39 | | : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges, |
40 | | check::LiveSymbols> { |
41 | | |
42 | | bool isBoolConversionMethod(const CallEvent &Call) const; |
43 | | |
44 | | public: |
45 | | // Whether the checker should model for null dereferences of smart pointers. |
46 | | DefaultBool ModelSmartPtrDereference; |
47 | | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
48 | | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
49 | | void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; |
50 | | ProgramStateRef |
51 | | checkRegionChanges(ProgramStateRef State, |
52 | | const InvalidatedSymbols *Invalidated, |
53 | | ArrayRef<const MemRegion *> ExplicitRegions, |
54 | | ArrayRef<const MemRegion *> Regions, |
55 | | const LocationContext *LCtx, const CallEvent *Call) const; |
56 | | void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, |
57 | | const char *Sep) const override; |
58 | | void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; |
59 | | |
60 | | private: |
61 | | void handleReset(const CallEvent &Call, CheckerContext &C) const; |
62 | | void handleRelease(const CallEvent &Call, CheckerContext &C) const; |
63 | | void handleSwap(const CallEvent &Call, CheckerContext &C) const; |
64 | | void handleGet(const CallEvent &Call, CheckerContext &C) const; |
65 | | bool handleAssignOp(const CallEvent &Call, CheckerContext &C) const; |
66 | | bool handleMoveCtr(const CallEvent &Call, CheckerContext &C, |
67 | | const MemRegion *ThisRegion) const; |
68 | | bool updateMovedSmartPointers(CheckerContext &C, const MemRegion *ThisRegion, |
69 | | const MemRegion *OtherSmartPtrRegion) const; |
70 | | void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const; |
71 | | |
72 | | using SmartPtrMethodHandlerFn = |
73 | | void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const; |
74 | | CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{ |
75 | | {{"reset"}, &SmartPtrModeling::handleReset}, |
76 | | {{"release"}, &SmartPtrModeling::handleRelease}, |
77 | | {{"swap", 1}, &SmartPtrModeling::handleSwap}, |
78 | | {{"get"}, &SmartPtrModeling::handleGet}}; |
79 | | }; |
80 | | } // end of anonymous namespace |
81 | | |
82 | | REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal) |
83 | | |
84 | | // Define the inter-checker API. |
85 | | namespace clang { |
86 | | namespace ento { |
87 | | namespace smartptr { |
88 | 41.0k | bool isStdSmartPtrCall(const CallEvent &Call) { |
89 | 41.0k | const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); |
90 | 41.0k | if (!MethodDecl || !MethodDecl->getParent()15.0k ) |
91 | 25.9k | return false; |
92 | | |
93 | 15.0k | const auto *RecordDecl = MethodDecl->getParent(); |
94 | 15.0k | if (!RecordDecl || !RecordDecl->getDeclContext()->isStdNamespace()) |
95 | 11.2k | return false; |
96 | | |
97 | 3.79k | if (RecordDecl->getDeclName().isIdentifier()) { |
98 | 3.79k | StringRef Name = RecordDecl->getName(); |
99 | 3.79k | return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr"3.21k ; |
100 | 3.79k | } |
101 | 0 | return false; |
102 | 0 | } |
103 | | |
104 | 86 | bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) { |
105 | 86 | const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); |
106 | 86 | return InnerPointVal && |
107 | 75 | !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true); |
108 | 86 | } |
109 | | } // namespace smartptr |
110 | | } // namespace ento |
111 | | } // namespace clang |
112 | | |
113 | | // If a region is removed all of the subregions need to be removed too. |
114 | | static TrackedRegionMapTy |
115 | | removeTrackedSubregions(TrackedRegionMapTy RegionMap, |
116 | | TrackedRegionMapTy::Factory &RegionMapFactory, |
117 | 84.5k | const MemRegion *Region) { |
118 | 84.5k | if (!Region) |
119 | 0 | return RegionMap; |
120 | 84.5k | for (const auto &E : RegionMap) { |
121 | 108 | if (E.first->isSubRegionOf(Region)) |
122 | 6 | RegionMap = RegionMapFactory.remove(RegionMap, E.first); |
123 | 108 | } |
124 | 84.5k | return RegionMap; |
125 | 84.5k | } |
126 | | |
127 | | static ProgramStateRef updateSwappedRegion(ProgramStateRef State, |
128 | | const MemRegion *Region, |
129 | 18 | const SVal *RegionInnerPointerVal) { |
130 | 18 | if (RegionInnerPointerVal) { |
131 | 16 | State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal); |
132 | 2 | } else { |
133 | 2 | State = State->remove<TrackedRegionMap>(Region); |
134 | 2 | } |
135 | 18 | return State; |
136 | 18 | } |
137 | | |
138 | | // Helper method to get the inner pointer type of specialized smart pointer |
139 | | // Returns empty type if not found valid inner pointer type. |
140 | 7 | static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) { |
141 | 7 | const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); |
142 | 7 | if (!MethodDecl || !MethodDecl->getParent()) |
143 | 0 | return {}; |
144 | | |
145 | 7 | const auto *RecordDecl = MethodDecl->getParent(); |
146 | 7 | if (!RecordDecl || !RecordDecl->isInStdNamespace()) |
147 | 0 | return {}; |
148 | | |
149 | 7 | const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl); |
150 | 7 | if (!TSD) |
151 | 0 | return {}; |
152 | | |
153 | 7 | auto TemplateArgs = TSD->getTemplateArgs().asArray(); |
154 | 7 | if (TemplateArgs.size() == 0) |
155 | 0 | return {}; |
156 | 7 | auto InnerValueType = TemplateArgs[0].getAsType(); |
157 | 7 | return C.getASTContext().getPointerType(InnerValueType.getCanonicalType()); |
158 | 7 | } |
159 | | |
160 | | // Helper method to pretty print region and avoid extra spacing. |
161 | | static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, |
162 | 114 | const MemRegion *Region) { |
163 | 114 | if (Region->canPrintPretty()) { |
164 | 114 | OS << " "; |
165 | 114 | Region->printPretty(OS); |
166 | 114 | } |
167 | 114 | } |
168 | | |
169 | 293 | bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const { |
170 | | // TODO: Update CallDescription to support anonymous calls? |
171 | | // TODO: Handle other methods, such as .get() or .release(). |
172 | | // But once we do, we'd need a visitor to explain null dereferences |
173 | | // that are found via such modeling. |
174 | 293 | const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl()); |
175 | 293 | return CD && CD->getConversionType()->isBooleanType()30 ; |
176 | 293 | } |
177 | | |
178 | | bool SmartPtrModeling::evalCall(const CallEvent &Call, |
179 | 40.5k | CheckerContext &C) const { |
180 | 40.5k | ProgramStateRef State = C.getState(); |
181 | 40.5k | if (!smartptr::isStdSmartPtrCall(Call)) |
182 | 40.2k | return false; |
183 | | |
184 | 293 | if (isBoolConversionMethod(Call)) { |
185 | 30 | const MemRegion *ThisR = |
186 | 30 | cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); |
187 | | |
188 | 30 | if (ModelSmartPtrDereference) { |
189 | | // The check for the region is moved is duplicated in handleBoolOperation |
190 | | // method. |
191 | | // FIXME: Once we model std::move for smart pointers clean up this and use |
192 | | // that modeling. |
193 | 22 | handleBoolConversion(Call, C); |
194 | 22 | return true; |
195 | 8 | } else { |
196 | 8 | if (!move::isMovedFrom(State, ThisR)) { |
197 | | // TODO: Model this case as well. At least, avoid invalidation of |
198 | | // globals. |
199 | 0 | return false; |
200 | 0 | } |
201 | | |
202 | | // TODO: Add a note to bug reports describing this decision. |
203 | 8 | C.addTransition(State->BindExpr( |
204 | 8 | Call.getOriginExpr(), C.getLocationContext(), |
205 | 8 | C.getSValBuilder().makeZeroVal(Call.getResultType()))); |
206 | | |
207 | 8 | return true; |
208 | 8 | } |
209 | 30 | } |
210 | | |
211 | 263 | if (!ModelSmartPtrDereference) |
212 | 56 | return false; |
213 | | |
214 | 207 | if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { |
215 | 111 | if (CC->getDecl()->isCopyConstructor()) |
216 | 0 | return false; |
217 | | |
218 | 111 | const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion(); |
219 | 111 | if (!ThisRegion) |
220 | 0 | return false; |
221 | | |
222 | 111 | if (CC->getDecl()->isMoveConstructor()) |
223 | 12 | return handleMoveCtr(Call, C, ThisRegion); |
224 | | |
225 | 99 | if (Call.getNumArgs() == 0) { |
226 | 60 | auto NullVal = C.getSValBuilder().makeNull(); |
227 | 60 | State = State->set<TrackedRegionMap>(ThisRegion, NullVal); |
228 | | |
229 | 60 | C.addTransition( |
230 | 60 | State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, |
231 | 50 | llvm::raw_ostream &OS) { |
232 | 50 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
233 | 44 | !BR.isInteresting(ThisRegion)) |
234 | 14 | return; |
235 | 36 | OS << "Default constructed smart pointer"; |
236 | 36 | checkAndPrettyPrintRegion(OS, ThisRegion); |
237 | 36 | OS << " is null"; |
238 | 36 | })); |
239 | 39 | } else { |
240 | 39 | const auto *TrackingExpr = Call.getArgExpr(0); |
241 | 39 | assert(TrackingExpr->getType()->isPointerType() && |
242 | 39 | "Adding a non pointer value to TrackedRegionMap"); |
243 | 39 | auto ArgVal = Call.getArgSVal(0); |
244 | 39 | State = State->set<TrackedRegionMap>(ThisRegion, ArgVal); |
245 | | |
246 | 39 | C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr, |
247 | 39 | ArgVal](PathSensitiveBugReport &BR, |
248 | 31 | llvm::raw_ostream &OS) { |
249 | 31 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
250 | 25 | !BR.isInteresting(ThisRegion)) |
251 | 10 | return; |
252 | 21 | bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); |
253 | 21 | OS << "Smart pointer"; |
254 | 21 | checkAndPrettyPrintRegion(OS, ThisRegion); |
255 | 21 | if (ArgVal.isZeroConstant()) |
256 | 4 | OS << " is constructed using a null value"; |
257 | 17 | else |
258 | 17 | OS << " is constructed"; |
259 | 21 | })); |
260 | 39 | } |
261 | 99 | return true; |
262 | 99 | } |
263 | | |
264 | 96 | if (handleAssignOp(Call, C)) |
265 | 17 | return true; |
266 | | |
267 | 79 | const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call); |
268 | 79 | if (!Handler) |
269 | 28 | return false; |
270 | 51 | (this->**Handler)(Call, C); |
271 | | |
272 | 51 | return C.isDifferent(); |
273 | 51 | } |
274 | | |
275 | | void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper, |
276 | 166k | CheckerContext &C) const { |
277 | 166k | ProgramStateRef State = C.getState(); |
278 | | // Clean up dead regions from the region map. |
279 | 166k | TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); |
280 | 654 | for (auto E : TrackedRegions) { |
281 | 654 | const MemRegion *Region = E.first; |
282 | 654 | bool IsRegDead = !SymReaper.isLiveRegion(Region); |
283 | | |
284 | 654 | if (IsRegDead) |
285 | 71 | State = State->remove<TrackedRegionMap>(Region); |
286 | 654 | } |
287 | 166k | C.addTransition(State); |
288 | 166k | } |
289 | | |
290 | | void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State, |
291 | 40 | const char *NL, const char *Sep) const { |
292 | 40 | TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); |
293 | | |
294 | 40 | if (!RS.isEmpty()) { |
295 | 0 | Out << Sep << "Smart ptr regions :" << NL; |
296 | 0 | for (auto I : RS) { |
297 | 0 | I.first->dumpToStream(Out); |
298 | 0 | if (smartptr::isNullSmartPtr(State, I.first)) |
299 | 0 | Out << ": Null"; |
300 | 0 | else |
301 | 0 | Out << ": Non Null"; |
302 | 0 | Out << NL; |
303 | 0 | } |
304 | 0 | } |
305 | 40 | } |
306 | | |
307 | | ProgramStateRef SmartPtrModeling::checkRegionChanges( |
308 | | ProgramStateRef State, const InvalidatedSymbols *Invalidated, |
309 | | ArrayRef<const MemRegion *> ExplicitRegions, |
310 | | ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx, |
311 | 46.8k | const CallEvent *Call) const { |
312 | 46.8k | TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>(); |
313 | 46.8k | TrackedRegionMapTy::Factory &RegionMapFactory = |
314 | 46.8k | State->get_context<TrackedRegionMap>(); |
315 | 46.8k | for (const auto *Region : Regions) |
316 | 84.5k | RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory, |
317 | 84.5k | Region->getBaseRegion()); |
318 | 46.8k | return State->set<TrackedRegionMap>(RegionMap); |
319 | 46.8k | } |
320 | | |
321 | | void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State, |
322 | 166k | SymbolReaper &SR) const { |
323 | | // Marking tracked symbols alive |
324 | 166k | TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); |
325 | 166k | for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I654 ) { |
326 | 654 | SVal Val = I->second; |
327 | 911 | for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si257 ) { |
328 | 257 | SR.markLive(*si); |
329 | 257 | } |
330 | 654 | } |
331 | 166k | } |
332 | | |
333 | | void SmartPtrModeling::handleReset(const CallEvent &Call, |
334 | 14 | CheckerContext &C) const { |
335 | 14 | ProgramStateRef State = C.getState(); |
336 | 14 | const auto *IC = dyn_cast<CXXInstanceCall>(&Call); |
337 | 14 | if (!IC) |
338 | 0 | return; |
339 | | |
340 | 14 | const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); |
341 | 14 | if (!ThisRegion) |
342 | 0 | return; |
343 | | |
344 | 14 | assert(Call.getArgExpr(0)->getType()->isPointerType() && |
345 | 14 | "Adding a non pointer value to TrackedRegionMap"); |
346 | 14 | State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0)); |
347 | 14 | const auto *TrackingExpr = Call.getArgExpr(0); |
348 | 14 | C.addTransition( |
349 | 14 | State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR, |
350 | 10 | llvm::raw_ostream &OS) { |
351 | 10 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
352 | 5 | !BR.isInteresting(ThisRegion)) |
353 | 6 | return; |
354 | 4 | bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR); |
355 | 4 | OS << "Smart pointer"; |
356 | 4 | checkAndPrettyPrintRegion(OS, ThisRegion); |
357 | 4 | OS << " reset using a null value"; |
358 | 4 | })); |
359 | | // TODO: Make sure to ivalidate the region in the Store if we don't have |
360 | | // time to model all methods. |
361 | 14 | } |
362 | | |
363 | | void SmartPtrModeling::handleRelease(const CallEvent &Call, |
364 | 8 | CheckerContext &C) const { |
365 | 8 | ProgramStateRef State = C.getState(); |
366 | 8 | const auto *IC = dyn_cast<CXXInstanceCall>(&Call); |
367 | 8 | if (!IC) |
368 | 0 | return; |
369 | | |
370 | 8 | const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); |
371 | 8 | if (!ThisRegion) |
372 | 0 | return; |
373 | | |
374 | 8 | const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion); |
375 | | |
376 | 8 | if (InnerPointVal) { |
377 | 8 | State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), |
378 | 8 | *InnerPointVal); |
379 | 8 | } |
380 | | |
381 | 8 | auto ValueToUpdate = C.getSValBuilder().makeNull(); |
382 | 8 | State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate); |
383 | | |
384 | 8 | C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, |
385 | 7 | llvm::raw_ostream &OS) { |
386 | 7 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
387 | 3 | !BR.isInteresting(ThisRegion)) |
388 | 5 | return; |
389 | | |
390 | 2 | OS << "Smart pointer"; |
391 | 2 | checkAndPrettyPrintRegion(OS, ThisRegion); |
392 | 2 | OS << " is released and set to null"; |
393 | 2 | })); |
394 | | // TODO: Add support to enable MallocChecker to start tracking the raw |
395 | | // pointer. |
396 | 8 | } |
397 | | |
398 | | void SmartPtrModeling::handleSwap(const CallEvent &Call, |
399 | 9 | CheckerContext &C) const { |
400 | | // To model unique_ptr::swap() method. |
401 | 9 | const auto *IC = dyn_cast<CXXInstanceCall>(&Call); |
402 | 9 | if (!IC) |
403 | 0 | return; |
404 | | |
405 | 9 | const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); |
406 | 9 | if (!ThisRegion) |
407 | 0 | return; |
408 | | |
409 | 9 | const auto *ArgRegion = Call.getArgSVal(0).getAsRegion(); |
410 | 9 | if (!ArgRegion) |
411 | 0 | return; |
412 | | |
413 | 9 | auto State = C.getState(); |
414 | 9 | const auto *ThisRegionInnerPointerVal = |
415 | 9 | State->get<TrackedRegionMap>(ThisRegion); |
416 | 9 | const auto *ArgRegionInnerPointerVal = |
417 | 9 | State->get<TrackedRegionMap>(ArgRegion); |
418 | | |
419 | | // Swap the tracked region values. |
420 | 9 | State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal); |
421 | 9 | State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal); |
422 | | |
423 | 9 | C.addTransition( |
424 | 9 | State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR, |
425 | 7 | llvm::raw_ostream &OS) { |
426 | 7 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
427 | 6 | !BR.isInteresting(ThisRegion)) |
428 | 3 | return; |
429 | 4 | BR.markInteresting(ArgRegion); |
430 | 4 | OS << "Swapped null smart pointer"; |
431 | 4 | checkAndPrettyPrintRegion(OS, ArgRegion); |
432 | 4 | OS << " with smart pointer"; |
433 | 4 | checkAndPrettyPrintRegion(OS, ThisRegion); |
434 | 4 | })); |
435 | 9 | } |
436 | | |
437 | | void SmartPtrModeling::handleGet(const CallEvent &Call, |
438 | 20 | CheckerContext &C) const { |
439 | 20 | ProgramStateRef State = C.getState(); |
440 | 20 | const auto *IC = dyn_cast<CXXInstanceCall>(&Call); |
441 | 20 | if (!IC) |
442 | 0 | return; |
443 | | |
444 | 20 | const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); |
445 | 20 | if (!ThisRegion) |
446 | 0 | return; |
447 | | |
448 | 20 | SVal InnerPointerVal; |
449 | 20 | if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { |
450 | 14 | InnerPointerVal = *InnerValPtr; |
451 | 6 | } else { |
452 | 6 | const auto *CallExpr = Call.getOriginExpr(); |
453 | 6 | InnerPointerVal = C.getSValBuilder().conjureSymbolVal( |
454 | 6 | CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount()); |
455 | 6 | State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); |
456 | 6 | } |
457 | | |
458 | 20 | State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), |
459 | 20 | InnerPointerVal); |
460 | | // TODO: Add NoteTag, for how the raw pointer got using 'get' method. |
461 | 20 | C.addTransition(State); |
462 | 20 | } |
463 | | |
464 | | bool SmartPtrModeling::handleAssignOp(const CallEvent &Call, |
465 | 96 | CheckerContext &C) const { |
466 | 96 | ProgramStateRef State = C.getState(); |
467 | 96 | const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call); |
468 | 96 | if (!OC) |
469 | 51 | return false; |
470 | 45 | OverloadedOperatorKind OOK = OC->getOverloadedOperator(); |
471 | 45 | if (OOK != OO_Equal) |
472 | 28 | return false; |
473 | 17 | const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion(); |
474 | 17 | if (!ThisRegion) |
475 | 0 | return false; |
476 | | |
477 | 17 | const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion(); |
478 | | // In case of 'nullptr' or '0' assigned |
479 | 17 | if (!OtherSmartPtrRegion) { |
480 | 5 | bool AssignedNull = Call.getArgSVal(0).isZeroConstant(); |
481 | 5 | if (!AssignedNull) |
482 | 0 | return false; |
483 | 5 | auto NullVal = C.getSValBuilder().makeNull(); |
484 | 5 | State = State->set<TrackedRegionMap>(ThisRegion, NullVal); |
485 | 5 | C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR, |
486 | 5 | llvm::raw_ostream &OS) { |
487 | 5 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
488 | 5 | !BR.isInteresting(ThisRegion)) |
489 | 0 | return; |
490 | 5 | OS << "Smart pointer"; |
491 | 5 | checkAndPrettyPrintRegion(OS, ThisRegion); |
492 | 5 | OS << " is assigned to null"; |
493 | 5 | })); |
494 | 5 | return true; |
495 | 5 | } |
496 | | |
497 | 12 | return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); |
498 | 12 | } |
499 | | |
500 | | bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C, |
501 | 12 | const MemRegion *ThisRegion) const { |
502 | 12 | const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion(); |
503 | 12 | if (!OtherSmartPtrRegion) |
504 | 0 | return false; |
505 | | |
506 | 12 | return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion); |
507 | 12 | } |
508 | | |
509 | | bool SmartPtrModeling::updateMovedSmartPointers( |
510 | | CheckerContext &C, const MemRegion *ThisRegion, |
511 | 24 | const MemRegion *OtherSmartPtrRegion) const { |
512 | 24 | ProgramStateRef State = C.getState(); |
513 | 24 | const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion); |
514 | 24 | if (OtherInnerPtr) { |
515 | 15 | State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr); |
516 | 15 | auto NullVal = C.getSValBuilder().makeNull(); |
517 | 15 | State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); |
518 | 15 | bool IsArgValNull = OtherInnerPtr->isZeroConstant(); |
519 | | |
520 | 15 | C.addTransition( |
521 | 15 | State, |
522 | 15 | C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull]( |
523 | 12 | PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { |
524 | 12 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType()) |
525 | 0 | return; |
526 | 12 | if (BR.isInteresting(OtherSmartPtrRegion)) { |
527 | 6 | OS << "Smart pointer"; |
528 | 6 | checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); |
529 | 6 | OS << " is null after being moved to"; |
530 | 6 | checkAndPrettyPrintRegion(OS, ThisRegion); |
531 | 6 | } |
532 | 12 | if (BR.isInteresting(ThisRegion) && IsArgValNull5 ) { |
533 | 5 | OS << "A null pointer value is moved to"; |
534 | 5 | checkAndPrettyPrintRegion(OS, ThisRegion); |
535 | 5 | BR.markInteresting(OtherSmartPtrRegion); |
536 | 5 | } |
537 | 12 | })); |
538 | 15 | return true; |
539 | 9 | } else { |
540 | | // In case we dont know anything about value we are moving from |
541 | | // remove the entry from map for which smart pointer got moved to. |
542 | 9 | auto NullVal = C.getSValBuilder().makeNull(); |
543 | 9 | State = State->remove<TrackedRegionMap>(ThisRegion); |
544 | 9 | State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal); |
545 | 9 | C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion, |
546 | 9 | ThisRegion](PathSensitiveBugReport &BR, |
547 | 8 | llvm::raw_ostream &OS) { |
548 | 8 | if (&BR.getBugType() != smartptr::getNullDereferenceBugType() || |
549 | 4 | !BR.isInteresting(OtherSmartPtrRegion)) |
550 | 4 | return; |
551 | 4 | OS << "Smart pointer"; |
552 | 4 | checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion); |
553 | 4 | OS << " is null after; previous value moved to"; |
554 | 4 | checkAndPrettyPrintRegion(OS, ThisRegion); |
555 | 4 | })); |
556 | 9 | return true; |
557 | 9 | } |
558 | 0 | return false; |
559 | 0 | } |
560 | | |
561 | | void SmartPtrModeling::handleBoolConversion(const CallEvent &Call, |
562 | 22 | CheckerContext &C) const { |
563 | | // To model unique_ptr::operator bool |
564 | 22 | ProgramStateRef State = C.getState(); |
565 | 22 | const Expr *CallExpr = Call.getOriginExpr(); |
566 | 22 | const MemRegion *ThisRegion = |
567 | 22 | cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion(); |
568 | | |
569 | 22 | SVal InnerPointerVal; |
570 | 22 | if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) { |
571 | 15 | InnerPointerVal = *InnerValPtr; |
572 | 7 | } else { |
573 | | // In case of inner pointer SVal is not available we create |
574 | | // conjureSymbolVal for inner pointer value. |
575 | 7 | auto InnerPointerType = getInnerPointerType(Call, C); |
576 | 7 | if (InnerPointerType.isNull()) |
577 | 0 | return; |
578 | | |
579 | 7 | const LocationContext *LC = C.getLocationContext(); |
580 | 7 | InnerPointerVal = C.getSValBuilder().conjureSymbolVal( |
581 | 7 | CallExpr, LC, InnerPointerType, C.blockCount()); |
582 | 7 | State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal); |
583 | 7 | } |
584 | | |
585 | 22 | if (State->isNull(InnerPointerVal).isConstrainedTrue()) { |
586 | 5 | State = State->BindExpr(CallExpr, C.getLocationContext(), |
587 | 5 | C.getSValBuilder().makeTruthVal(false)); |
588 | | |
589 | 5 | C.addTransition(State); |
590 | 5 | return; |
591 | 17 | } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) { |
592 | 8 | State = State->BindExpr(CallExpr, C.getLocationContext(), |
593 | 8 | C.getSValBuilder().makeTruthVal(true)); |
594 | | |
595 | 8 | C.addTransition(State); |
596 | 8 | return; |
597 | 9 | } else if (move::isMovedFrom(State, ThisRegion)) { |
598 | 0 | C.addTransition( |
599 | 0 | State->BindExpr(CallExpr, C.getLocationContext(), |
600 | 0 | C.getSValBuilder().makeZeroVal(Call.getResultType()))); |
601 | 0 | return; |
602 | 9 | } else { |
603 | 9 | ProgramStateRef NotNullState, NullState; |
604 | 9 | std::tie(NotNullState, NullState) = |
605 | 9 | State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>()); |
606 | | |
607 | 9 | auto NullVal = C.getSValBuilder().makeNull(); |
608 | | // Explicitly tracking the region as null. |
609 | 9 | NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal); |
610 | | |
611 | 9 | NullState = NullState->BindExpr(CallExpr, C.getLocationContext(), |
612 | 9 | C.getSValBuilder().makeTruthVal(false)); |
613 | 9 | C.addTransition(NullState, C.getNoteTag( |
614 | 9 | [ThisRegion](PathSensitiveBugReport &BR, |
615 | 6 | llvm::raw_ostream &OS) { |
616 | 6 | OS << "Assuming smart pointer"; |
617 | 6 | checkAndPrettyPrintRegion(OS, ThisRegion); |
618 | 6 | OS << " is null"; |
619 | 6 | }, |
620 | 9 | /*IsPrunable=*/true)); |
621 | 9 | NotNullState = |
622 | 9 | NotNullState->BindExpr(CallExpr, C.getLocationContext(), |
623 | 9 | C.getSValBuilder().makeTruthVal(true)); |
624 | 9 | C.addTransition( |
625 | 9 | NotNullState, |
626 | 9 | C.getNoteTag( |
627 | 7 | [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { |
628 | 7 | OS << "Assuming smart pointer"; |
629 | 7 | checkAndPrettyPrintRegion(OS, ThisRegion); |
630 | 7 | OS << " is non-null"; |
631 | 7 | }, |
632 | 9 | /*IsPrunable=*/true)); |
633 | 9 | return; |
634 | 9 | } |
635 | 22 | } |
636 | | |
637 | 69 | void ento::registerSmartPtrModeling(CheckerManager &Mgr) { |
638 | 69 | auto *Checker = Mgr.registerChecker<SmartPtrModeling>(); |
639 | 69 | Checker->ModelSmartPtrDereference = |
640 | 69 | Mgr.getAnalyzerOptions().getCheckerBooleanOption( |
641 | 69 | Checker, "ModelSmartPtrDereference"); |
642 | 69 | } |
643 | | |
644 | 138 | bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) { |
645 | 138 | const LangOptions &LO = mgr.getLangOpts(); |
646 | 138 | return LO.CPlusPlus; |
647 | 138 | } |