clang  7.0.0
InnerPointerChecker.cpp
Go to the documentation of this file.
1 //=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file defines a check that marks a raw pointer to a C++ container's
11 // inner buffer released when the object is destroyed. This information can
12 // be used by MallocChecker to detect use-after-free problems.
13 //
14 //===----------------------------------------------------------------------===//
15 
16 #include "AllocationState.h"
17 #include "ClangSACheckers.h"
23 
24 using namespace clang;
25 using namespace ento;
26 
27 using PtrSet = llvm::ImmutableSet<SymbolRef>;
28 
29 // Associate container objects with a set of raw pointer symbols.
31 
32 // This is a trick to gain access to PtrSet's Factory.
33 namespace clang {
34 namespace ento {
35 template <>
37  static void *GDMIndex() {
38  static int Index = 0;
39  return &Index;
40  }
41 };
42 } // end namespace ento
43 } // end namespace clang
44 
45 namespace {
46 
47 class InnerPointerChecker
48  : public Checker<check::DeadSymbols, check::PostCall> {
49 
50  CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
51  InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
52  ShrinkToFitFn, SwapFn;
53 
54 public:
55  class InnerPointerBRVisitor : public BugReporterVisitor {
56  SymbolRef PtrToBuf;
57 
58  public:
59  InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
60 
61  static void *getTag() {
62  static int Tag = 0;
63  return &Tag;
64  }
65 
66  void Profile(llvm::FoldingSetNodeID &ID) const override {
67  ID.AddPointer(getTag());
68  }
69 
70  std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
71  const ExplodedNode *PrevN,
72  BugReporterContext &BRC,
73  BugReport &BR) override;
74 
75  // FIXME: Scan the map once in the visitor's constructor and do a direct
76  // lookup by region.
77  bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
78  RawPtrMapTy Map = State->get<RawPtrMap>();
79  for (const auto Entry : Map) {
80  if (Entry.second.contains(Sym))
81  return true;
82  }
83  return false;
84  }
85  };
86 
87  InnerPointerChecker()
88  : AppendFn("append"), AssignFn("assign"), ClearFn("clear"),
89  CStrFn("c_str"), DataFn("data"), EraseFn("erase"), InsertFn("insert"),
90  PopBackFn("pop_back"), PushBackFn("push_back"), ReplaceFn("replace"),
91  ReserveFn("reserve"), ResizeFn("resize"),
92  ShrinkToFitFn("shrink_to_fit"), SwapFn("swap") {}
93 
94  /// Check if the object of this member function call is a `basic_string`.
95  bool isCalledOnStringObject(const CXXInstanceCall *ICall) const;
96 
97  /// Check whether the called member function potentially invalidates
98  /// pointers referring to the container object's inner buffer.
99  bool isInvalidatingMemberFunction(const CallEvent &Call) const;
100 
101  /// Mark pointer symbols associated with the given memory region released
102  /// in the program state.
103  void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
104  const MemRegion *ObjRegion,
105  CheckerContext &C) const;
106 
107  /// Standard library functions that take a non-const `basic_string` argument by
108  /// reference may invalidate its inner pointers. Check for these cases and
109  /// mark the pointers released.
110  void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,
111  CheckerContext &C) const;
112 
113  /// Record the connection between raw pointers referring to a container
114  /// object's inner buffer and the object's memory region in the program state.
115  /// Mark potentially invalidated pointers released.
116  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
117 
118  /// Clean up the program state map.
119  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
120 };
121 
122 } // end anonymous namespace
123 
124 bool InnerPointerChecker::isCalledOnStringObject(
125  const CXXInstanceCall *ICall) const {
126  const auto *ObjRegion =
127  dyn_cast_or_null<TypedValueRegion>(ICall->getCXXThisVal().getAsRegion());
128  if (!ObjRegion)
129  return false;
130 
131  QualType ObjTy = ObjRegion->getValueType();
132  if (ObjTy.isNull() ||
133  ObjTy->getAsCXXRecordDecl()->getName() != "basic_string")
134  return false;
135 
136  return true;
137 }
138 
139 bool InnerPointerChecker::isInvalidatingMemberFunction(
140  const CallEvent &Call) const {
141  if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
142  OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
143  if (Opc == OO_Equal || Opc == OO_PlusEqual)
144  return true;
145  return false;
146  }
147  return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
148  Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
149  Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
150  Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
151  Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
152  Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
153  Call.isCalled(SwapFn));
154 }
155 
156 void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
158  const MemRegion *MR,
159  CheckerContext &C) const {
160  if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
161  const Expr *Origin = Call.getOriginExpr();
162  for (const auto Symbol : *PS) {
163  // NOTE: `Origin` may be null, and will be stored so in the symbol's
164  // `RefState` in MallocChecker's `RegionState` program state map.
165  State = allocation_state::markReleased(State, Symbol, Origin);
166  }
167  State = State->remove<RawPtrMap>(MR);
168  C.addTransition(State);
169  return;
170  }
171 }
172 
173 void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
174  ProgramStateRef State,
175  CheckerContext &C) const {
176  if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
177  const FunctionDecl *FD = FC->getDecl();
178  if (!FD || !FD->isInStdNamespace())
179  return;
180 
181  for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
182  QualType ParamTy = FD->getParamDecl(I)->getType();
183  if (!ParamTy->isReferenceType() ||
184  ParamTy->getPointeeType().isConstQualified())
185  continue;
186 
187  // In case of member operator calls, `this` is counted as an
188  // argument but not as a parameter.
189  bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
190  unsigned ArgI = isaMemberOpCall ? I+1 : I;
191 
192  SVal Arg = FC->getArgSVal(ArgI);
193  const auto *ArgRegion =
194  dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
195  if (!ArgRegion)
196  continue;
197 
198  markPtrSymbolsReleased(Call, State, ArgRegion, C);
199  }
200  }
201 }
202 
203 // [string.require]
204 //
205 // "References, pointers, and iterators referring to the elements of a
206 // basic_string sequence may be invalidated by the following uses of that
207 // basic_string object:
208 //
209 // -- As an argument to any standard library function taking a reference
210 // to non-const basic_string as an argument. For example, as an argument to
211 // non-member functions swap(), operator>>(), and getline(), or as an argument
212 // to basic_string::swap().
213 //
214 // -- Calling non-const member functions, except operator[], at, front, back,
215 // begin, rbegin, end, and rend."
216 
217 void InnerPointerChecker::checkPostCall(const CallEvent &Call,
218  CheckerContext &C) const {
219  ProgramStateRef State = C.getState();
220 
221  if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
222  if (isCalledOnStringObject(ICall)) {
223  const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
224  ICall->getCXXThisVal().getAsRegion());
225 
226  if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
227  SVal RawPtr = Call.getReturnValue();
228  if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
229  // Start tracking this raw pointer by adding it to the set of symbols
230  // associated with this container object in the program state map.
231 
232  PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
233  const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
234  PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
235  assert(C.wasInlined || !Set.contains(Sym));
236  Set = F.add(Set, Sym);
237 
238  State = State->set<RawPtrMap>(ObjRegion, Set);
239  C.addTransition(State);
240  }
241  return;
242  }
243 
244  // Check [string.require] / second point.
245  if (isInvalidatingMemberFunction(Call)) {
246  markPtrSymbolsReleased(Call, State, ObjRegion, C);
247  return;
248  }
249  }
250  }
251 
252  // Check [string.require] / first point.
253  checkFunctionArguments(Call, State, C);
254 }
255 
256 void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
257  CheckerContext &C) const {
258  ProgramStateRef State = C.getState();
259  PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
260  RawPtrMapTy RPM = State->get<RawPtrMap>();
261  for (const auto Entry : RPM) {
262  if (!SymReaper.isLiveRegion(Entry.first)) {
263  // Due to incomplete destructor support, some dead regions might
264  // remain in the program state map. Clean them up.
265  State = State->remove<RawPtrMap>(Entry.first);
266  }
267  if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {
268  PtrSet CleanedUpSet = *OldSet;
269  for (const auto Symbol : Entry.second) {
270  if (!SymReaper.isLive(Symbol))
271  CleanedUpSet = F.remove(CleanedUpSet, Symbol);
272  }
273  State = CleanedUpSet.isEmpty()
274  ? State->remove<RawPtrMap>(Entry.first)
275  : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
276  }
277  }
278  C.addTransition(State);
279 }
280 
281 std::shared_ptr<PathDiagnosticPiece>
282 InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
283  const ExplodedNode *PrevN,
284  BugReporterContext &BRC,
285  BugReport &BR) {
286  if (!isSymbolTracked(N->getState(), PtrToBuf) ||
287  isSymbolTracked(PrevN->getState(), PtrToBuf))
288  return nullptr;
289 
291  if (!S)
292  return nullptr;
293 
294  SmallString<256> Buf;
295  llvm::raw_svector_ostream OS(Buf);
296  OS << "Dangling inner pointer obtained here";
298  N->getLocationContext());
299  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
300  nullptr);
301 }
302 
303 namespace clang {
304 namespace ento {
305 namespace allocation_state {
306 
307 std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
308  return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
309 }
310 
311 } // end namespace allocation_state
312 } // end namespace ento
313 } // end namespace clang
314 
315 void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
316  registerNewDeleteChecker(Mgr);
317  Mgr.registerChecker<InnerPointerChecker>();
318 }
Represents a function declaration or definition.
Definition: Decl.h:1716
A (possibly-)qualified type.
Definition: Type.h:655
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:94
Stmt - This represents one statement.
Definition: Stmt.h:66
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
Definition: Type.cpp:497
llvm::ImmutableSet< SymbolRef > PtrSet
ExplodedNode * addTransition(ProgramStateRef State=nullptr, const ProgramPointTag *Tag=nullptr)
Generates a new transition in the program state graph (ExplodedGraph).
const ProgramStateRef & getState() const
const Expr * getOriginExpr() const
Returns the expression whose value will be the result of this call.
Definition: CallEvent.h:249
const bool wasInlined
If we are post visiting a call, this flag will be set if the call was inlined.
Symbolic value.
Definition: SymExpr.h:30
LineState State
bool isReferenceType() const
Definition: Type.h:6125
std::unique_ptr< BugReporterVisitor > getInnerPointerBRVisitor(SymbolRef Sym)
This function provides an additional visitor that augments the bug report with information relevant t...
bool isLiveRegion(const MemRegion *region)
const LocationContext * getLocationContext() const
SVal getReturnValue() const
Returns the return value of the call.
Definition: CallEvent.cpp:345
SymbolRef getAsSymbol(bool IncludeBaseRegions=false) const
If this SVal wraps a symbol return that SymbolRef.
Definition: SVals.cpp:127
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition: Type.cpp:1627
Represents a non-static C++ member function call, no matter how it is written.
Definition: CallEvent.h:662
#define REGISTER_MAP_WITH_PROGRAMSTATE(Name, Key, Value)
Declares an immutable map of type NameTy, suitable for placement into the ProgramState.
Expr - This represents one expression.
Definition: Expr.h:106
virtual SVal getCXXThisVal() const
Returns the value of the implicit &#39;this&#39; object.
Definition: CallEvent.cpp:619
bool isNull() const
Return true if this QualType doesn&#39;t point to a type yet.
Definition: Type.h:720
CHECKER * registerChecker(AT... Args)
Used to register checkers.
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition: Type.h:5948
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
This class represents a description of a function call using the number of arguments and the name of ...
Definition: CallEvent.h:78
const MemRegion * getAsRegion() const
Definition: SVals.cpp:151
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2254
SVal - This represents a symbolic expression, which can be either an L-value or an R-value...
Definition: SVals.h:76
A class responsible for cleaning up unused symbols.
Dataflow Directional Tag Classes.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
Definition: OperatorKinds.h:22
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:165
const ProgramStateRef & getState() const
ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin)
bool isCalled(const CallDescription &CD) const
Returns true if the CallEvent is a call to a function that matches the CallDescription.
Definition: CallEvent.cpp:316
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:275
static llvm::ImmutableListFactory< const FieldRegion * > Factory
QualType getType() const
Definition: Decl.h:648
This class provides an interface through which checkers can create individual bug reports...
Definition: BugReporter.h:76
bool isInStdNamespace() const
Definition: DeclBase.cpp:357
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:2971
SourceManager & getSourceManager()
Definition: BugReporter.h:585
bool isLive(SymbolRef sym)