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