clang  14.0.0git
SmartPtrModeling.cpp
Go to the documentation of this file.
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"
19 #include "clang/AST/ExprCXX.h"
20 #include "clang/AST/Type.h"
21 #include "clang/Basic/LLVM.h"
33 #include "llvm/ADT/StringMap.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include <string>
36 
37 using namespace clang;
38 using namespace ento;
39 
40 namespace {
41 
42 class SmartPtrModeling
43  : public Checker<eval::Call, check::DeadSymbols, check::RegionChanges,
44  check::LiveSymbols> {
45 
46  bool isBoolConversionMethod(const CallEvent &Call) const;
47 
48 public:
49  // Whether the checker should model for null dereferences of smart pointers.
50  DefaultBool ModelSmartPtrDereference;
51  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
52  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
53  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
55  checkRegionChanges(ProgramStateRef State,
56  const InvalidatedSymbols *Invalidated,
57  ArrayRef<const MemRegion *> ExplicitRegions,
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) const;
74  void handleBoolConversion(const CallEvent &Call, CheckerContext &C) const;
75  bool handleComparisionOp(const CallEvent &Call, CheckerContext &C) const;
76  bool handleOstreamOperator(const CallEvent &Call, CheckerContext &C) const;
77  bool handleSwap(ProgramStateRef State, SVal First, SVal Second,
78  CheckerContext &C) const;
79  std::pair<SVal, ProgramStateRef>
80  retrieveOrConjureInnerPtrVal(ProgramStateRef State,
81  const MemRegion *ThisRegion, const Expr *E,
82  QualType Type, CheckerContext &C) const;
83 
84  using SmartPtrMethodHandlerFn =
85  void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
86  CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
87  {{"reset"}, &SmartPtrModeling::handleReset},
88  {{"release"}, &SmartPtrModeling::handleRelease},
89  {{"swap", 1}, &SmartPtrModeling::handleSwapMethod},
90  {{"get"}, &SmartPtrModeling::handleGet}};
91  const CallDescription StdSwapCall{{"std", "swap"}, 2};
92  const CallDescription StdMakeUniqueCall{{"std", "make_unique"}};
93  const CallDescription StdMakeUniqueForOverwriteCall{
94  {"std", "make_unique_for_overwrite"}};
95 };
96 } // end of anonymous namespace
97 
98 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
99 
100 // Checks if RD has name in Names and is in std namespace
101 static bool hasStdClassWithName(const CXXRecordDecl *RD,
102  ArrayRef<llvm::StringLiteral> Names) {
103  if (!RD || !RD->getDeclContext()->isStdNamespace())
104  return false;
105  if (RD->getDeclName().isIdentifier())
106  return llvm::is_contained(Names, RD->getName());
107  return false;
108 }
109 
110 constexpr llvm::StringLiteral STD_PTR_NAMES[] = {"shared_ptr", "unique_ptr",
111  "weak_ptr"};
112 
113 static bool isStdSmartPtr(const CXXRecordDecl *RD) {
115 }
116 
117 static bool isStdSmartPtr(const Expr *E) {
118  return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
119 }
120 
121 // Define the inter-checker API.
122 namespace clang {
123 namespace ento {
124 namespace smartptr {
125 bool isStdSmartPtrCall(const CallEvent &Call) {
126  const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
127  if (!MethodDecl || !MethodDecl->getParent())
128  return false;
129  return isStdSmartPtr(MethodDecl->getParent());
130 }
131 
132 bool isStdSmartPtr(const CXXRecordDecl *RD) {
133  if (!RD || !RD->getDeclContext()->isStdNamespace())
134  return false;
135 
136  if (RD->getDeclName().isIdentifier()) {
137  StringRef Name = RD->getName();
138  return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
139  }
140  return false;
141 }
142 
143 bool isStdSmartPtr(const Expr *E) {
144  return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
145 }
146 
147 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
148  const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
149  return InnerPointVal &&
150  !State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
151 }
152 } // namespace smartptr
153 } // namespace ento
154 } // namespace clang
155 
156 // If a region is removed all of the subregions need to be removed too.
157 static TrackedRegionMapTy
158 removeTrackedSubregions(TrackedRegionMapTy RegionMap,
159  TrackedRegionMapTy::Factory &RegionMapFactory,
160  const MemRegion *Region) {
161  if (!Region)
162  return RegionMap;
163  for (const auto &E : RegionMap) {
164  if (E.first->isSubRegionOf(Region))
165  RegionMap = RegionMapFactory.remove(RegionMap, E.first);
166  }
167  return RegionMap;
168 }
169 
171  const MemRegion *Region,
172  const SVal *RegionInnerPointerVal) {
173  if (RegionInnerPointerVal) {
174  State = State->set<TrackedRegionMap>(Region, *RegionInnerPointerVal);
175  } else {
176  State = State->remove<TrackedRegionMap>(Region);
177  }
178  return State;
179 }
180 
181 static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD) {
182  if (!RD || !RD->isInStdNamespace())
183  return {};
184 
185  const auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD);
186  if (!TSD)
187  return {};
188 
189  auto TemplateArgs = TSD->getTemplateArgs().asArray();
190  if (TemplateArgs.empty())
191  return {};
192  auto InnerValueType = TemplateArgs[0].getAsType();
193  return C.getASTContext().getPointerType(InnerValueType.getCanonicalType());
194 }
195 
196 // This is for use with standalone-functions like std::make_unique,
197 // std::make_unique_for_overwrite, etc. It reads the template parameter and
198 // returns the pointer type corresponding to it,
199 static QualType getPointerTypeFromTemplateArg(const CallEvent &Call,
200  CheckerContext &C) {
201  const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
202  if (!FD || !FD->isFunctionTemplateSpecialization())
203  return {};
204  const auto &TemplateArgs = FD->getTemplateSpecializationArgs()->asArray();
205  if (TemplateArgs.size() == 0)
206  return {};
207  auto ValueType = TemplateArgs[0].getAsType();
208  return C.getASTContext().getPointerType(ValueType.getCanonicalType());
209 }
210 
211 // Helper method to get the inner pointer type of specialized smart pointer
212 // Returns empty type if not found valid inner pointer type.
213 static QualType getInnerPointerType(const CallEvent &Call, CheckerContext &C) {
214  const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
215  if (!MethodDecl || !MethodDecl->getParent())
216  return {};
217 
218  const auto *RecordDecl = MethodDecl->getParent();
219  return getInnerPointerType(C, RecordDecl);
220 }
221 
222 // Helper method to pretty print region and avoid extra spacing.
223 static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS,
224  const MemRegion *Region) {
225  if (Region->canPrintPretty()) {
226  OS << " ";
227  Region->printPretty(OS);
228  }
229 }
230 
231 bool SmartPtrModeling::isBoolConversionMethod(const CallEvent &Call) const {
232  // TODO: Update CallDescription to support anonymous calls?
233  // TODO: Handle other methods, such as .get() or .release().
234  // But once we do, we'd need a visitor to explain null dereferences
235  // that are found via such modeling.
236  const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
237  return CD && CD->getConversionType()->isBooleanType();
238 }
239 
240 constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[] = {"basic_ostream"};
241 
242 bool isStdBasicOstream(const Expr *E) {
243  const auto *RD = E->getType()->getAsCXXRecordDecl();
245 }
246 
247 static bool isStdFunctionCall(const CallEvent &Call) {
248  return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
249 }
250 
251 bool isStdOstreamOperatorCall(const CallEvent &Call) {
252  if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
253  return false;
254  const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
255  if (!FC)
256  return false;
257  const FunctionDecl *FD = FC->getDecl();
258  if (!FD->isOverloadedOperator())
259  return false;
261  if (OOK != clang::OO_LessLess)
262  return false;
263  return isStdSmartPtr(Call.getArgExpr(1)) &&
264  isStdBasicOstream(Call.getArgExpr(0));
265 }
266 
267 static bool isPotentiallyComparisionOpCall(const CallEvent &Call) {
268  if (Call.getNumArgs() != 2 || !isStdFunctionCall(Call))
269  return false;
270  return smartptr::isStdSmartPtr(Call.getArgExpr(0)) ||
271  smartptr::isStdSmartPtr(Call.getArgExpr(1));
272 }
273 
274 bool SmartPtrModeling::evalCall(const CallEvent &Call,
275  CheckerContext &C) const {
276 
277  ProgramStateRef State = C.getState();
278 
279  // If any one of the arg is a unique_ptr, then
280  // we can try this function
281  if (ModelSmartPtrDereference && isPotentiallyComparisionOpCall(Call))
282  if (handleComparisionOp(Call, C))
283  return true;
284 
285  if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
286  return handleOstreamOperator(Call, C);
287 
288  if (Call.isCalled(StdSwapCall)) {
289  // Check the first arg, if it is of std::unique_ptr type.
290  assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
291  const Expr *FirstArg = Call.getArgExpr(0);
293  return false;
294  return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
295  }
296 
297  if (Call.isCalled(StdMakeUniqueCall) ||
298  Call.isCalled(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  if (CC->getDecl()->isMoveConstructor())
383  return handleMoveCtr(Call, C, ThisRegion);
384 
385  if (Call.getNumArgs() == 0) {
386  auto NullVal = C.getSValBuilder().makeNull();
387  State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
388 
389  C.addTransition(
390  State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
391  llvm::raw_ostream &OS) {
392  if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
393  !BR.isInteresting(ThisRegion))
394  return;
395  OS << "Default constructed smart pointer";
396  checkAndPrettyPrintRegion(OS, ThisRegion);
397  OS << " is null";
398  }));
399  } else {
400  const auto *TrackingExpr = Call.getArgExpr(0);
401  assert(TrackingExpr->getType()->isPointerType() &&
402  "Adding a non pointer value to TrackedRegionMap");
403  auto ArgVal = Call.getArgSVal(0);
404  State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);
405 
406  C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
407  ArgVal](PathSensitiveBugReport &BR,
408  llvm::raw_ostream &OS) {
409  if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
410  !BR.isInteresting(ThisRegion))
411  return;
412  bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
413  OS << "Smart pointer";
414  checkAndPrettyPrintRegion(OS, ThisRegion);
415  if (ArgVal.isZeroConstant())
416  OS << " is constructed using a null value";
417  else
418  OS << " is constructed";
419  }));
420  }
421  return true;
422  }
423 
424  if (handleAssignOp(Call, C))
425  return true;
426 
427  const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
428  if (!Handler)
429  return false;
430  (this->**Handler)(Call, C);
431 
432  return C.isDifferent();
433 }
434 
435 std::pair<SVal, ProgramStateRef> SmartPtrModeling::retrieveOrConjureInnerPtrVal(
436  ProgramStateRef State, const MemRegion *ThisRegion, const Expr *E,
437  QualType Type, CheckerContext &C) const {
438  const auto *Ptr = State->get<TrackedRegionMap>(ThisRegion);
439  if (Ptr)
440  return {*Ptr, State};
441  auto Val = C.getSValBuilder().conjureSymbolVal(E, C.getLocationContext(),
442  Type, C.blockCount());
443  State = State->set<TrackedRegionMap>(ThisRegion, Val);
444  return {Val, State};
445 }
446 
447 bool SmartPtrModeling::handleComparisionOp(const CallEvent &Call,
448  CheckerContext &C) const {
449  const auto *FC = dyn_cast<SimpleFunctionCall>(&Call);
450  if (!FC)
451  return false;
452  const FunctionDecl *FD = FC->getDecl();
453  if (!FD->isOverloadedOperator())
454  return false;
456  if (!(OOK == OO_EqualEqual || OOK == OO_ExclaimEqual || OOK == OO_Less ||
457  OOK == OO_LessEqual || OOK == OO_Greater || OOK == OO_GreaterEqual ||
458  OOK == OO_Spaceship))
459  return false;
460 
461  // There are some special cases about which we can infer about
462  // the resulting answer.
463  // For reference, there is a discussion at https://reviews.llvm.org/D104616.
464  // Also, the cppreference page is good to look at
465  // https://en.cppreference.com/w/cpp/memory/unique_ptr/operator_cmp.
466 
467  auto makeSValFor = [&C, this](ProgramStateRef State, const Expr *E,
468  SVal S) -> std::pair<SVal, ProgramStateRef> {
469  if (S.isZeroConstant()) {
470  return {S, State};
471  }
472  const MemRegion *Reg = S.getAsRegion();
473  assert(Reg &&
474  "this pointer of std::unique_ptr should be obtainable as MemRegion");
475  QualType Type = getInnerPointerType(C, E->getType()->getAsCXXRecordDecl());
476  return retrieveOrConjureInnerPtrVal(State, Reg, E, Type, C);
477  };
478 
479  SVal First = Call.getArgSVal(0);
480  SVal Second = Call.getArgSVal(1);
481  const auto *FirstExpr = Call.getArgExpr(0);
482  const auto *SecondExpr = Call.getArgExpr(1);
483 
484  const auto *ResultExpr = Call.getOriginExpr();
485  const auto *LCtx = C.getLocationContext();
486  auto &Bldr = C.getSValBuilder();
487  ProgramStateRef State = C.getState();
488 
489  SVal FirstPtrVal, SecondPtrVal;
490  std::tie(FirstPtrVal, State) = makeSValFor(State, FirstExpr, First);
491  std::tie(SecondPtrVal, State) = makeSValFor(State, SecondExpr, Second);
492  BinaryOperatorKind BOK =
494  auto RetVal = Bldr.evalBinOp(State, BOK, FirstPtrVal, SecondPtrVal,
495  Call.getResultType());
496 
497  if (OOK != OO_Spaceship) {
498  ProgramStateRef TrueState, FalseState;
499  std::tie(TrueState, FalseState) =
500  State->assume(*RetVal.getAs<DefinedOrUnknownSVal>());
501  if (TrueState)
502  C.addTransition(
503  TrueState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(true)));
504  if (FalseState)
505  C.addTransition(
506  FalseState->BindExpr(ResultExpr, LCtx, Bldr.makeTruthVal(false)));
507  } else {
508  C.addTransition(State->BindExpr(ResultExpr, LCtx, RetVal));
509  }
510  return true;
511 }
512 
513 bool SmartPtrModeling::handleOstreamOperator(const CallEvent &Call,
514  CheckerContext &C) const {
515  // operator<< does not modify the smart pointer.
516  // And we don't really have much of modelling of basic_ostream.
517  // So, we are better off:
518  // 1) Invalidating the mem-region of the ostream object at hand.
519  // 2) Setting the SVal of the basic_ostream as the return value.
520  // Not very satisfying, but it gets the job done, and is better
521  // than the default handling. :)
522 
523  ProgramStateRef State = C.getState();
524  const auto StreamVal = Call.getArgSVal(0);
525  const MemRegion *StreamThisRegion = StreamVal.getAsRegion();
526  if (!StreamThisRegion)
527  return false;
528  State =
529  State->invalidateRegions({StreamThisRegion}, Call.getOriginExpr(),
530  C.blockCount(), C.getLocationContext(), false);
531  State =
532  State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), StreamVal);
533  C.addTransition(State);
534  return true;
535 }
536 
537 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
538  CheckerContext &C) const {
539  ProgramStateRef State = C.getState();
540  // Clean up dead regions from the region map.
541  TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
542  for (auto E : TrackedRegions) {
543  const MemRegion *Region = E.first;
544  bool IsRegDead = !SymReaper.isLiveRegion(Region);
545 
546  if (IsRegDead)
547  State = State->remove<TrackedRegionMap>(Region);
548  }
549  C.addTransition(State);
550 }
551 
552 void SmartPtrModeling::printState(raw_ostream &Out, ProgramStateRef State,
553  const char *NL, const char *Sep) const {
554  TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
555 
556  if (!RS.isEmpty()) {
557  Out << Sep << "Smart ptr regions :" << NL;
558  for (auto I : RS) {
559  I.first->dumpToStream(Out);
560  if (smartptr::isNullSmartPtr(State, I.first))
561  Out << ": Null";
562  else
563  Out << ": Non Null";
564  Out << NL;
565  }
566  }
567 }
568 
569 ProgramStateRef SmartPtrModeling::checkRegionChanges(
570  ProgramStateRef State, const InvalidatedSymbols *Invalidated,
571  ArrayRef<const MemRegion *> ExplicitRegions,
572  ArrayRef<const MemRegion *> Regions, const LocationContext *LCtx,
573  const CallEvent *Call) const {
574  TrackedRegionMapTy RegionMap = State->get<TrackedRegionMap>();
575  TrackedRegionMapTy::Factory &RegionMapFactory =
576  State->get_context<TrackedRegionMap>();
577  for (const auto *Region : Regions)
578  RegionMap = removeTrackedSubregions(RegionMap, RegionMapFactory,
579  Region->getBaseRegion());
580  return State->set<TrackedRegionMap>(RegionMap);
581 }
582 
583 void SmartPtrModeling::checkLiveSymbols(ProgramStateRef State,
584  SymbolReaper &SR) const {
585  // Marking tracked symbols alive
586  TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
587  for (auto I = TrackedRegions.begin(), E = TrackedRegions.end(); I != E; ++I) {
588  SVal Val = I->second;
589  for (auto si = Val.symbol_begin(), se = Val.symbol_end(); si != se; ++si) {
590  SR.markLive(*si);
591  }
592  }
593 }
594 
595 void SmartPtrModeling::handleReset(const CallEvent &Call,
596  CheckerContext &C) const {
597  ProgramStateRef State = C.getState();
598  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
599  if (!IC)
600  return;
601 
602  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
603  if (!ThisRegion)
604  return;
605 
606  assert(Call.getArgExpr(0)->getType()->isPointerType() &&
607  "Adding a non pointer value to TrackedRegionMap");
608  State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
609  const auto *TrackingExpr = Call.getArgExpr(0);
610  C.addTransition(
611  State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
612  llvm::raw_ostream &OS) {
613  if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
614  !BR.isInteresting(ThisRegion))
615  return;
616  bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
617  OS << "Smart pointer";
618  checkAndPrettyPrintRegion(OS, ThisRegion);
619  OS << " reset using a null value";
620  }));
621  // TODO: Make sure to ivalidate the region in the Store if we don't have
622  // time to model all methods.
623 }
624 
625 void SmartPtrModeling::handleRelease(const CallEvent &Call,
626  CheckerContext &C) const {
627  ProgramStateRef State = C.getState();
628  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
629  if (!IC)
630  return;
631 
632  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
633  if (!ThisRegion)
634  return;
635 
636  const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
637 
638  if (InnerPointVal) {
639  State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
640  *InnerPointVal);
641  }
642 
643  auto ValueToUpdate = C.getSValBuilder().makeNull();
644  State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);
645 
646  C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
647  llvm::raw_ostream &OS) {
648  if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
649  !BR.isInteresting(ThisRegion))
650  return;
651 
652  OS << "Smart pointer";
653  checkAndPrettyPrintRegion(OS, ThisRegion);
654  OS << " is released and set to null";
655  }));
656  // TODO: Add support to enable MallocChecker to start tracking the raw
657  // pointer.
658 }
659 
660 void SmartPtrModeling::handleSwapMethod(const CallEvent &Call,
661  CheckerContext &C) const {
662  // To model unique_ptr::swap() method.
663  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
664  if (!IC)
665  return;
666 
667  auto State = C.getState();
668  handleSwap(State, IC->getCXXThisVal(), Call.getArgSVal(0), C);
669 }
670 
671 bool SmartPtrModeling::handleSwap(ProgramStateRef State, SVal First,
672  SVal Second, CheckerContext &C) const {
673  const MemRegion *FirstThisRegion = First.getAsRegion();
674  if (!FirstThisRegion)
675  return false;
676  const MemRegion *SecondThisRegion = Second.getAsRegion();
677  if (!SecondThisRegion)
678  return false;
679 
680  const auto *FirstInnerPtrVal = State->get<TrackedRegionMap>(FirstThisRegion);
681  const auto *SecondInnerPtrVal =
682  State->get<TrackedRegionMap>(SecondThisRegion);
683 
684  State = updateSwappedRegion(State, FirstThisRegion, SecondInnerPtrVal);
685  State = updateSwappedRegion(State, SecondThisRegion, FirstInnerPtrVal);
686 
687  C.addTransition(State, C.getNoteTag([FirstThisRegion, SecondThisRegion](
688  PathSensitiveBugReport &BR,
689  llvm::raw_ostream &OS) {
690  if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
691  return;
692  if (BR.isInteresting(FirstThisRegion) &&
693  !BR.isInteresting(SecondThisRegion)) {
694  BR.markInteresting(SecondThisRegion);
695  BR.markNotInteresting(FirstThisRegion);
696  }
697  if (BR.isInteresting(SecondThisRegion) &&
698  !BR.isInteresting(FirstThisRegion)) {
699  BR.markInteresting(FirstThisRegion);
700  BR.markNotInteresting(SecondThisRegion);
701  }
702  // TODO: We need to emit some note here probably!!
703  }));
704 
705  return true;
706 }
707 
708 void SmartPtrModeling::handleGet(const CallEvent &Call,
709  CheckerContext &C) const {
710  ProgramStateRef State = C.getState();
711  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
712  if (!IC)
713  return;
714 
715  const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
716  if (!ThisRegion)
717  return;
718 
719  SVal InnerPointerVal;
720  std::tie(InnerPointerVal, State) = retrieveOrConjureInnerPtrVal(
721  State, ThisRegion, Call.getOriginExpr(), Call.getResultType(), C);
722  State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
723  InnerPointerVal);
724  // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
725  C.addTransition(State);
726 }
727 
728 bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
729  CheckerContext &C) const {
730  ProgramStateRef State = C.getState();
731  const auto *OC = dyn_cast<CXXMemberOperatorCall>(&Call);
732  if (!OC)
733  return false;
734  OverloadedOperatorKind OOK = OC->getOverloadedOperator();
735  if (OOK != OO_Equal)
736  return false;
737  const MemRegion *ThisRegion = OC->getCXXThisVal().getAsRegion();
738  if (!ThisRegion)
739  return false;
740 
741  const MemRegion *OtherSmartPtrRegion = OC->getArgSVal(0).getAsRegion();
742  // In case of 'nullptr' or '0' assigned
743  if (!OtherSmartPtrRegion) {
744  bool AssignedNull = Call.getArgSVal(0).isZeroConstant();
745  if (!AssignedNull)
746  return false;
747  auto NullVal = C.getSValBuilder().makeNull();
748  State = State->set<TrackedRegionMap>(ThisRegion, NullVal);
749  C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
750  llvm::raw_ostream &OS) {
751  if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
752  !BR.isInteresting(ThisRegion))
753  return;
754  OS << "Smart pointer";
755  checkAndPrettyPrintRegion(OS, ThisRegion);
756  OS << " is assigned to null";
757  }));
758  return true;
759  }
760 
761  return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
762 }
763 
764 bool SmartPtrModeling::handleMoveCtr(const CallEvent &Call, CheckerContext &C,
765  const MemRegion *ThisRegion) const {
766  const auto *OtherSmartPtrRegion = Call.getArgSVal(0).getAsRegion();
767  if (!OtherSmartPtrRegion)
768  return false;
769 
770  return updateMovedSmartPointers(C, ThisRegion, OtherSmartPtrRegion);
771 }
772 
773 bool SmartPtrModeling::updateMovedSmartPointers(
774  CheckerContext &C, const MemRegion *ThisRegion,
775  const MemRegion *OtherSmartPtrRegion) const {
776  ProgramStateRef State = C.getState();
777  const auto *OtherInnerPtr = State->get<TrackedRegionMap>(OtherSmartPtrRegion);
778  if (OtherInnerPtr) {
779  State = State->set<TrackedRegionMap>(ThisRegion, *OtherInnerPtr);
780  auto NullVal = C.getSValBuilder().makeNull();
781  State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
782  bool IsArgValNull = OtherInnerPtr->isZeroConstant();
783 
784  C.addTransition(
785  State,
786  C.getNoteTag([ThisRegion, OtherSmartPtrRegion, IsArgValNull](
787  PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
788  if (&BR.getBugType() != smartptr::getNullDereferenceBugType())
789  return;
790  if (BR.isInteresting(OtherSmartPtrRegion)) {
791  OS << "Smart pointer";
792  checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
793  OS << " is null after being moved to";
794  checkAndPrettyPrintRegion(OS, ThisRegion);
795  }
796  if (BR.isInteresting(ThisRegion) && IsArgValNull) {
797  OS << "A null pointer value is moved to";
798  checkAndPrettyPrintRegion(OS, ThisRegion);
799  BR.markInteresting(OtherSmartPtrRegion);
800  }
801  }));
802  return true;
803  } else {
804  // In case we dont know anything about value we are moving from
805  // remove the entry from map for which smart pointer got moved to.
806  auto NullVal = C.getSValBuilder().makeNull();
807  State = State->remove<TrackedRegionMap>(ThisRegion);
808  State = State->set<TrackedRegionMap>(OtherSmartPtrRegion, NullVal);
809  C.addTransition(State, C.getNoteTag([OtherSmartPtrRegion,
810  ThisRegion](PathSensitiveBugReport &BR,
811  llvm::raw_ostream &OS) {
812  if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
813  !BR.isInteresting(OtherSmartPtrRegion))
814  return;
815  OS << "Smart pointer";
816  checkAndPrettyPrintRegion(OS, OtherSmartPtrRegion);
817  OS << " is null after; previous value moved to";
818  checkAndPrettyPrintRegion(OS, ThisRegion);
819  }));
820  return true;
821  }
822  return false;
823 }
824 
825 void SmartPtrModeling::handleBoolConversion(const CallEvent &Call,
826  CheckerContext &C) const {
827  // To model unique_ptr::operator bool
828  ProgramStateRef State = C.getState();
829  const Expr *CallExpr = Call.getOriginExpr();
830  const MemRegion *ThisRegion =
831  cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
832 
833  SVal InnerPointerVal;
834  if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
835  InnerPointerVal = *InnerValPtr;
836  } else {
837  // In case of inner pointer SVal is not available we create
838  // conjureSymbolVal for inner pointer value.
839  auto InnerPointerType = getInnerPointerType(Call, C);
840  if (InnerPointerType.isNull())
841  return;
842 
843  const LocationContext *LC = C.getLocationContext();
844  InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
845  CallExpr, LC, InnerPointerType, C.blockCount());
846  State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
847  }
848 
849  if (State->isNull(InnerPointerVal).isConstrainedTrue()) {
850  State = State->BindExpr(CallExpr, C.getLocationContext(),
851  C.getSValBuilder().makeTruthVal(false));
852 
853  C.addTransition(State);
854  return;
855  } else if (State->isNonNull(InnerPointerVal).isConstrainedTrue()) {
856  State = State->BindExpr(CallExpr, C.getLocationContext(),
857  C.getSValBuilder().makeTruthVal(true));
858 
859  C.addTransition(State);
860  return;
861  } else if (move::isMovedFrom(State, ThisRegion)) {
862  C.addTransition(
863  State->BindExpr(CallExpr, C.getLocationContext(),
864  C.getSValBuilder().makeZeroVal(Call.getResultType())));
865  return;
866  } else {
867  ProgramStateRef NotNullState, NullState;
868  std::tie(NotNullState, NullState) =
869  State->assume(InnerPointerVal.castAs<DefinedOrUnknownSVal>());
870 
871  auto NullVal = C.getSValBuilder().makeNull();
872  // Explicitly tracking the region as null.
873  NullState = NullState->set<TrackedRegionMap>(ThisRegion, NullVal);
874 
875  NullState = NullState->BindExpr(CallExpr, C.getLocationContext(),
876  C.getSValBuilder().makeTruthVal(false));
877  C.addTransition(NullState, C.getNoteTag(
878  [ThisRegion](PathSensitiveBugReport &BR,
879  llvm::raw_ostream &OS) {
880  OS << "Assuming smart pointer";
881  checkAndPrettyPrintRegion(OS, ThisRegion);
882  OS << " is null";
883  },
884  /*IsPrunable=*/true));
885  NotNullState =
886  NotNullState->BindExpr(CallExpr, C.getLocationContext(),
887  C.getSValBuilder().makeTruthVal(true));
888  C.addTransition(
889  NotNullState,
890  C.getNoteTag(
891  [ThisRegion](PathSensitiveBugReport &BR, llvm::raw_ostream &OS) {
892  OS << "Assuming smart pointer";
893  checkAndPrettyPrintRegion(OS, ThisRegion);
894  OS << " is non-null";
895  },
896  /*IsPrunable=*/true));
897  return;
898  }
899 }
900 
901 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
902  auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
903  Checker->ModelSmartPtrDereference =
904  Mgr.getAnalyzerOptions().getCheckerBooleanOption(
905  Checker, "ModelSmartPtrDereference");
906 }
907 
908 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
909  const LangOptions &LO = mgr.getLangOpts();
910  return LO.CPlusPlus;
911 }
clang::ento::smartptr::getNullDereferenceBugType
const BugType * getNullDereferenceBugType()
Definition: SmartPtrChecker.cpp:54
llvm
Definition: Dominators.h:30
clang::LocationContext
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Definition: AnalysisDeclContext.h:215
SVals.h
checkAndPrettyPrintRegion
static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, const MemRegion *Region)
Definition: SmartPtrModeling.cpp:223
updateSwappedRegion
static ProgramStateRef updateSwappedRegion(ProgramStateRef State, const MemRegion *Region, const SVal *RegionInnerPointerVal)
Definition: SmartPtrModeling.cpp:170
clang::ento::DefinedOrUnknownSVal
Definition: SVals.h:236
clang::if
if(T->getSizeExpr()) TRY_TO(TraverseStmt(T -> getSizeExpr()))
Definition: RecursiveASTVisitor.h:1002
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:673
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:54
Move.h
DeclCXX.h
SymbolManager.h
clang::FunctionDecl::isOverloadedOperator
bool isOverloadedOperator() const
Whether this function declaration represents an C++ overloaded operator, e.g., "operator+".
Definition: Decl.h:2625
llvm::Optional
Definition: LLVM.h:40
clang::ComparisonCategoryType::First
@ First
clang::index::SymbolRole::Call
@ Call
clang::ento::smartptr::isStdSmartPtr
bool isStdSmartPtr(const CXXRecordDecl *RD)
Definition: SmartPtrModeling.cpp:132
clang::Type
The base class of the type hierarchy.
Definition: Type.h:1490
BASIC_OSTREAM_NAMES
constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[]
Definition: SmartPtrModeling.cpp:240
CallEvent.h
clang::ento::MemRegion
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:94
REGISTER_MAP_WITH_PROGRAMSTATE
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Definition: ProgramStateTrait.h:84
isStdFunctionCall
static bool isStdFunctionCall(const CallEvent &Call)
Definition: SmartPtrModeling.cpp:247
SymExpr.h
BuiltinCheckerRegistration.h
clang::Decl::isInStdNamespace
bool isInStdNamespace() const
Definition: DeclBase.cpp:394
clang::ento::smartptr::isNullSmartPtr
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion)
Returns whether the smart pointer is null or not.
Definition: SmartPtrModeling.cpp:147
CheckerManager.h
getPointerTypeFromTemplateArg
static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, CheckerContext &C)
Definition: SmartPtrModeling.cpp:199
clang::ento::OperatorKind::GetBinaryOpUnsafe
BinaryOperatorKind GetBinaryOpUnsafe() const
Definition: CheckerHelpers.h:86
Type.h
clang::DeclContext::isStdNamespace
bool isStdNamespace() const
Definition: DeclBase.cpp:1126
hasStdClassWithName
static bool hasStdClassWithName(const CXXRecordDecl *RD, ArrayRef< llvm::StringLiteral > Names)
Definition: SmartPtrModeling.cpp:101
removeTrackedSubregions
static TrackedRegionMapTy removeTrackedSubregions(TrackedRegionMapTy RegionMap, TrackedRegionMapTy::Factory &RegionMapFactory, const MemRegion *Region)
Definition: SmartPtrModeling.cpp:158
clang::StringLiteral
StringLiteral - This represents a string literal expression, e.g.
Definition: Expr.h:1761
clang::Type::getAsCXXRecordDecl
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1753
ExprCXX.h
clang::FunctionDecl::getOverloadedOperator
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
Definition: Decl.cpp:3653
clang::OverloadedOperatorKind
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
Definition: OperatorKinds.h:21
SmartPtr.h
clang::ento::operationKindFromOverloadedOperator
OperatorKind operationKindFromOverloadedOperator(OverloadedOperatorKind OOK, bool IsBinary)
Definition: CheckerHelpers.cpp:151
clang::CXXRecordDecl
Represents a C++ struct/union/class.
Definition: DeclCXX.h:255
BugType.h
clang::DeclContext::getParent
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:1860
clang::DeclarationName::isIdentifier
bool isIdentifier() const
Predicate functions for querying what type of name this is.
Definition: DeclarationName.h:375
llvm::ArrayRef
Definition: LLVM.h:34
isPotentiallyComparisionOpCall
static bool isPotentiallyComparisionOpCall(const CallEvent &Call)
Definition: SmartPtrModeling.cpp:267
LLVM.h
State
LineState State
Definition: UnwrappedLineFormatter.cpp:986
isStdBasicOstream
bool isStdBasicOstream(const Expr *E)
Definition: SmartPtrModeling.cpp:242
isStdSmartPtr
static bool isStdSmartPtr(const CXXRecordDecl *RD)
Definition: SmartPtrModeling.cpp:113
clang::BinaryOperatorKind
BinaryOperatorKind
Definition: OperationKinds.h:25
CheckerContext.h
clang::LangOptions
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:58
Checker.h
DeclarationName.h
isStdOstreamOperatorCall
bool isStdOstreamOperatorCall(const CallEvent &Call)
Definition: SmartPtrModeling.cpp:251
clang
Definition: CalledOnceCheck.h:17
clang::ento::CallEvent
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:143
MemRegion.h
CheckerHelpers.h
clang::Expr::getType
QualType getType() const
Definition: Expr.h:141
clang::NamedDecl::getDeclName
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:313
getInnerPointerType
static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD)
Definition: SmartPtrModeling.cpp:181
clang::ento::smartptr::isStdSmartPtrCall
bool isStdSmartPtrCall(const CallEvent &Call)
Returns true if the event call is on smart pointer.
Definition: SmartPtrModeling.cpp:125
STD_PTR_NAMES
constexpr llvm::StringLiteral STD_PTR_NAMES[]
Definition: SmartPtrModeling.cpp:110
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::ento::InvalidatedSymbols
llvm::DenseSet< SymbolRef > InvalidatedSymbols
Definition: Store.h:51
clang::ento::move::isMovedFrom
bool isMovedFrom(ProgramStateRef State, const MemRegion *Region)
Returns true if the object is known to have been recently std::moved.
Definition: MoveChecker.cpp:235
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1856
clang::RecordDecl
Represents a struct/union/class.
Definition: Decl.h:3859
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2795
llvm::IntrusiveRefCntPtr< const ProgramState >
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...
clang::Decl::getDeclContext
DeclContext * getDeclContext()
Definition: DeclBase.h:439
clang::NamedDecl::getName
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:276