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