clang  12.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"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/AST/Type.h"
28 
29 using namespace clang;
30 using namespace ento;
31 
32 namespace {
33 class SmartPtrModeling : public Checker<eval::Call, check::DeadSymbols> {
34 
35  bool isNullAfterMoveMethod(const CallEvent &Call) const;
36 
37 public:
38  // Whether the checker should model for null dereferences of smart pointers.
39  DefaultBool ModelSmartPtrDereference;
40  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
41  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
42  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
43 
44 private:
45  ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
46  const MemRegion *ThisValRegion) const;
47  void handleReset(const CallEvent &Call, CheckerContext &C) const;
48  void handleRelease(const CallEvent &Call, CheckerContext &C) const;
49  void handleSwap(const CallEvent &Call, CheckerContext &C) const;
50 
51  using SmartPtrMethodHandlerFn =
52  void (SmartPtrModeling::*)(const CallEvent &Call, CheckerContext &) const;
53  CallDescriptionMap<SmartPtrMethodHandlerFn> SmartPtrMethodHandlers{
54  {{"reset"}, &SmartPtrModeling::handleReset},
55  {{"release"}, &SmartPtrModeling::handleRelease},
56  {{"swap", 1}, &SmartPtrModeling::handleSwap}};
57 };
58 } // end of anonymous namespace
59 
60 REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
61 
62 // Define the inter-checker API.
63 namespace clang {
64 namespace ento {
65 namespace smartptr {
66 bool isStdSmartPtrCall(const CallEvent &Call) {
67  const auto *MethodDecl = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
68  if (!MethodDecl || !MethodDecl->getParent())
69  return false;
70 
71  const auto *RecordDecl = MethodDecl->getParent();
73  return false;
74 
76  StringRef Name = RecordDecl->getName();
77  return Name == "shared_ptr" || Name == "unique_ptr" || Name == "weak_ptr";
78  }
79  return false;
80 }
81 
82 bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion) {
83  const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);
84  return InnerPointVal && InnerPointVal->isZeroConstant();
85 }
86 } // namespace smartptr
87 } // namespace ento
88 } // namespace clang
89 
90 bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
91  // TODO: Update CallDescription to support anonymous calls?
92  // TODO: Handle other methods, such as .get() or .release().
93  // But once we do, we'd need a visitor to explain null dereferences
94  // that are found via such modeling.
95  const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
96  return CD && CD->getConversionType()->isBooleanType();
97 }
98 
99 bool SmartPtrModeling::evalCall(const CallEvent &Call,
100  CheckerContext &C) const {
101 
102  if (!smartptr::isStdSmartPtrCall(Call))
103  return false;
104 
105  if (isNullAfterMoveMethod(Call)) {
107  const MemRegion *ThisR =
108  cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
109 
110  if (!move::isMovedFrom(State, ThisR)) {
111  // TODO: Model this case as well. At least, avoid invalidation of globals.
112  return false;
113  }
114 
115  // TODO: Add a note to bug reports describing this decision.
116  C.addTransition(
117  State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
119  return true;
120  }
121 
122  if (!ModelSmartPtrDereference)
123  return false;
124 
125  if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
126  if (CC->getDecl()->isCopyOrMoveConstructor())
127  return false;
128 
129  const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
130  if (!ThisValRegion)
131  return false;
132 
133  auto State = updateTrackedRegion(Call, C, ThisValRegion);
134  C.addTransition(State);
135  return true;
136  }
137 
138  const SmartPtrMethodHandlerFn *Handler = SmartPtrMethodHandlers.lookup(Call);
139  if (!Handler)
140  return false;
141  (this->**Handler)(Call, C);
142 
143  return C.isDifferent();
144 }
145 
146 void SmartPtrModeling::checkDeadSymbols(SymbolReaper &SymReaper,
147  CheckerContext &C) const {
149  // Clean up dead regions from the region map.
150  TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
151  for (auto E : TrackedRegions) {
152  const MemRegion *Region = E.first;
153  bool IsRegDead = !SymReaper.isLiveRegion(Region);
154 
155  if (IsRegDead)
156  State = State->remove<TrackedRegionMap>(Region);
157  }
158  C.addTransition(State);
159 }
160 
161 void SmartPtrModeling::handleReset(const CallEvent &Call,
162  CheckerContext &C) const {
163  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
164  if (!IC)
165  return;
166 
167  const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
168  if (!ThisValRegion)
169  return;
170  auto State = updateTrackedRegion(Call, C, ThisValRegion);
171  C.addTransition(State);
172  // TODO: Make sure to ivalidate the the region in the Store if we don't have
173  // time to model all methods.
174 }
175 
176 void SmartPtrModeling::handleRelease(const CallEvent &Call,
177  CheckerContext &C) const {
178  const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
179  if (!IC)
180  return;
181 
182  const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
183  if (!ThisValRegion)
184  return;
185 
186  auto State = updateTrackedRegion(Call, C, ThisValRegion);
187 
188  const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
189  if (InnerPointVal) {
190  State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
191  *InnerPointVal);
192  }
193  C.addTransition(State);
194  // TODO: Add support to enable MallocChecker to start tracking the raw
195  // pointer.
196 }
197 
198 void SmartPtrModeling::handleSwap(const CallEvent &Call,
199  CheckerContext &C) const {
200  // TODO: Add support to handle swap method.
201 }
202 
204 SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
205  const MemRegion *ThisValRegion) const {
206  // TODO: Refactor and clean up handling too many things.
208  auto NumArgs = Call.getNumArgs();
209 
210  if (NumArgs == 0) {
211  auto NullSVal = C.getSValBuilder().makeNull();
212  State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal);
213  } else if (NumArgs == 1) {
214  auto ArgVal = Call.getArgSVal(0);
215  assert(Call.getArgExpr(0)->getType()->isPointerType() &&
216  "Adding a non pointer value to TrackedRegionMap");
217  State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal);
218  }
219 
220  return State;
221 }
222 
223 void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
224  auto *Checker = Mgr.registerChecker<SmartPtrModeling>();
225  Checker->ModelSmartPtrDereference =
227  Checker, "ModelSmartPtrDereference");
228 }
229 
230 bool ento::shouldRegisterSmartPtrModeling(const CheckerManager &mgr) {
231  const LangOptions &LO = mgr.getLangOpts();
232  return LO.CPlusPlus;
233 }
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:94
C Language Family Type Representation.
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const Expr * getOriginExpr() const
Returns the expression whose value will be the result of this call.
Definition: CallEvent.h:228
const AnalyzerOptions & getAnalyzerOptions() const
Defines the clang::Expr interface and subclasses for C++ expressions.
Represents a struct/union/class.
Definition: Decl.h:3770
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:272
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion)
Returns whether the smart pointer is null or not.
LineState State
virtual const Expr * getArgExpr(unsigned Index) const
Returns the expression associated with a given argument.
Definition: CallEvent.h:285
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:54
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.
DefinedOrUnknownSVal makeZeroVal(QualType type)
Construct an SVal representing &#39;0&#39; for the specified type.
Definition: SValBuilder.cpp:52
DeclContext * getDeclContext()
Definition: DeclBase.h:439
Represents a non-static C++ member function call, no matter how it is written.
Definition: CallEvent.h:663
QualType getType() const
Definition: Expr.h:142
virtual const Decl * getDecl() const
Returns the declaration of the function or method that will be called.
Definition: CallEvent.h:208
bool isIdentifier() const
Predicate functions for querying what type of name this is.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition: DeclBase.h:1816
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
bool isStdNamespace() const
Definition: DeclBase.cpp:1124
A class responsible for cleaning up unused symbols.
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option&#39;s string value as a boolean.
Dataflow Directional Tag Classes.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:143
const ProgramStateRef & getState() const
QualType getResultType() const
Returns the result type, adjusted for references.
Definition: CallEvent.cpp:70
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate.h) and friends (in DeclFriend.h).
virtual unsigned getNumArgs() const =0
Returns the number of arguments (explicit and implicit).
SValBuilder & getSValBuilder()
bool isStdSmartPtrCall(const CallEvent &Call)
Returns true if the event call is on smart pointer.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:250
bool isPointerType() const
Definition: Type.h:6650
virtual SVal getArgSVal(unsigned Index) const
Returns the value of a given argument at the time of the call.
Definition: CallEvent.cpp:381
const LangOptions & getLangOpts() const
const LocationContext * getLocationContext() const
bool isDifferent()
Check if the checker changed the state of the execution; ex: added a new transition or a bug report...