clang  15.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"
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;
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,
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 
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 
114 static bool isStdSmartPtr(const CXXRecordDecl *RD) {
116 }
117 
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 {
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 
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 
144 bool isStdSmartPtr(const Expr *E) {
145  return isStdSmartPtr(E->getType()->getAsCXXRecordDecl());
146 }
147 
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
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 
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 
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,
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.
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.
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 
232 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 
243 bool isStdBasicOstream(const Expr *E) {
244  const auto *RD = E->getType()->getAsCXXRecordDecl();
246 }
247 
248 static bool isStdFunctionCall(const CallEvent &Call) {
249  return Call.getDecl() && Call.getDecl()->getDeclContext()->isStdNamespace();
250 }
251 
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;
262  if (OOK != clang::OO_LessLess)
263  return false;
264  return isStdSmartPtr(Call.getArgExpr(1)) &&
265  isStdBasicOstream(Call.getArgExpr(0));
266 }
267 
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 
275 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);
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 
437 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 
449 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;
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 =
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 
515 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 
539 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 
554 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 
571 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 
585 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 
597 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 
627 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 
663 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 
674 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 
711 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 
731 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 
769 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 
778 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 
833 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 
911 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
912  auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
913  Checker->ModelSmartPtrDereference =
914  Mgr.getAnalyzerOptions().getCheckerBooleanOption(
915  Checker, "ModelSmartPtrDereference");
916 }
917 
918 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
919  const LangOptions &LO = mgr.getLangOpts();
920  return LO.CPlusPlus;
921 }
clang::ento::smartptr::getNullDereferenceBugType
const BugType * getNullDereferenceBugType()
Definition: SmartPtrChecker.cpp:54
llvm
YAML serialization mapping.
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
CallDescription.h
SVals.h
checkAndPrettyPrintRegion
static void checkAndPrettyPrintRegion(llvm::raw_ostream &OS, const MemRegion *Region)
Definition: SmartPtrModeling.cpp:224
updateSwappedRegion
static ProgramStateRef updateSwappedRegion(ProgramStateRef State, const MemRegion *Region, const SVal *RegionInnerPointerVal)
Definition: SmartPtrModeling.cpp:171
clang::ento::DefinedOrUnknownSVal
Definition: SVals.h:217
clang::if
if(T->getSizeExpr()) TRY_TO(TraverseStmt(T -> getSizeExpr()))
Definition: RecursiveASTVisitor.h:1098
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:731
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:55
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:2652
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:133
clang::Type
The base class of the type hierarchy.
Definition: Type.h:1556
BASIC_OSTREAM_NAMES
constexpr llvm::StringLiteral BASIC_OSTREAM_NAMES[]
Definition: SmartPtrModeling.cpp:241
CallEvent.h
clang::ento::MemRegion
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:95
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:87
isStdFunctionCall
static bool isStdFunctionCall(const CallEvent &Call)
Definition: SmartPtrModeling.cpp:248
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:148
CheckerManager.h
getPointerTypeFromTemplateArg
static QualType getPointerTypeFromTemplateArg(const CallEvent &Call, CheckerContext &C)
Definition: SmartPtrModeling.cpp:200
clang::ento::OperatorKind::GetBinaryOpUnsafe
BinaryOperatorKind GetBinaryOpUnsafe() const
Definition: CheckerHelpers.h:85
Type.h
clang::DeclContext::isStdNamespace
bool isStdNamespace() const
Definition: DeclBase.cpp:1136
hasStdClassWithName
static bool hasStdClassWithName(const CXXRecordDecl *RD, ArrayRef< llvm::StringLiteral > Names)
Definition: SmartPtrModeling.cpp:102
removeTrackedSubregions
static TrackedRegionMapTy removeTrackedSubregions(TrackedRegionMapTy RegionMap, TrackedRegionMapTy::Factory &RegionMapFactory, const MemRegion *Region)
Definition: SmartPtrModeling.cpp:159
clang::StringLiteral
StringLiteral - This represents a string literal expression, e.g.
Definition: Expr.h:1767
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:1759
ExprCXX.h
clang::FunctionDecl::getOverloadedOperator
OverloadedOperatorKind getOverloadedOperator() const
getOverloadedOperator - Which C++ overloaded operator this function represents, if any.
Definition: Decl.cpp:3718
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:254
BugType.h
clang::DeclContext::getParent
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:1902
clang::DeclarationName::isIdentifier
bool isIdentifier() const
Predicate functions for querying what type of name this is.
Definition: DeclarationName.h:384
llvm::ArrayRef
Definition: LLVM.h:34
isPotentiallyComparisionOpCall
static bool isPotentiallyComparisionOpCall(const CallEvent &Call)
Definition: SmartPtrModeling.cpp:268
LLVM.h
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1126
isStdBasicOstream
bool isStdBasicOstream(const Expr *E)
Definition: SmartPtrModeling.cpp:243
isStdSmartPtr
static bool isStdSmartPtr(const CXXRecordDecl *RD)
Definition: SmartPtrModeling.cpp:114
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:78
Checker.h
DeclarationName.h
isStdOstreamOperatorCall
bool isStdOstreamOperatorCall(const CallEvent &Call)
Definition: SmartPtrModeling.cpp:252
clang
Definition: CalledOnceCheck.h:17
clang::ento::CallEvent
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:148
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:311
getInnerPointerType
static QualType getInnerPointerType(CheckerContext C, const CXXRecordDecl *RD)
Definition: SmartPtrModeling.cpp:182
clang::ento::smartptr::isStdSmartPtrCall
bool isStdSmartPtrCall(const CallEvent &Call)
Returns true if the event call is on smart pointer.
Definition: SmartPtrModeling.cpp:126
STD_PTR_NAMES
constexpr llvm::StringLiteral STD_PTR_NAMES[]
Definition: SmartPtrModeling.cpp:111
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:234
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1872
clang::RecordDecl
Represents a struct/union/class.
Definition: Decl.h:3908
clang::CallExpr
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2801
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:441
clang::NamedDecl::getName
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:274