Coverage Report

Created: 2019-07-24 05:18

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//=== InnerPointerChecker.cpp -------------------------------------*- 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 check that marks a raw pointer to a C++ container's
10
// inner buffer released when the object is destroyed. This information can
11
// be used by MallocChecker to detect use-after-free problems.
12
//
13
//===----------------------------------------------------------------------===//
14
15
#include "AllocationState.h"
16
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
17
#include "InterCheckerAPI.h"
18
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
20
#include "clang/StaticAnalyzer/Core/Checker.h"
21
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
23
24
using namespace clang;
25
using namespace ento;
26
27
// Associate container objects with a set of raw pointer symbols.
28
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef)
29
REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
30
31
32
namespace {
33
34
class InnerPointerChecker
35
    : public Checker<check::DeadSymbols, check::PostCall> {
36
37
  CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
38
      InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
39
      ShrinkToFitFn, SwapFn;
40
41
public:
42
  class InnerPointerBRVisitor : public BugReporterVisitor {
43
    SymbolRef PtrToBuf;
44
45
  public:
46
30
    InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
47
48
30
    static void *getTag() {
49
30
      static int Tag = 0;
50
30
      return &Tag;
51
30
    }
52
53
30
    void Profile(llvm::FoldingSetNodeID &ID) const override {
54
30
      ID.AddPointer(getTag());
55
30
    }
56
57
    virtual std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
58
                                                   BugReporterContext &BRC,
59
                                                   BugReport &BR) override;
60
61
    // FIXME: Scan the map once in the visitor's constructor and do a direct
62
    // lookup by region.
63
2.06k
    bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
64
2.06k
      RawPtrMapTy Map = State->get<RawPtrMap>();
65
2.06k
      for (const auto Entry : Map) {
66
1.07k
        if (Entry.second.contains(Sym))
67
934
          return true;
68
1.07k
      }
69
2.06k
      
return false1.13k
;
70
2.06k
    }
71
  };
72
73
  InnerPointerChecker()
74
      : AppendFn({"std", "basic_string", "append"}),
75
        AssignFn({"std", "basic_string", "assign"}),
76
        ClearFn({"std", "basic_string", "clear"}),
77
        CStrFn({"std", "basic_string", "c_str"}),
78
        DataFn({"std", "basic_string", "data"}),
79
        EraseFn({"std", "basic_string", "erase"}),
80
        InsertFn({"std", "basic_string", "insert"}),
81
        PopBackFn({"std", "basic_string", "pop_back"}),
82
        PushBackFn({"std", "basic_string", "push_back"}),
83
        ReplaceFn({"std", "basic_string", "replace"}),
84
        ReserveFn({"std", "basic_string", "reserve"}),
85
        ResizeFn({"std", "basic_string", "resize"}),
86
        ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}),
87
40
        SwapFn({"std", "basic_string", "swap"}) {}
88
89
  /// Check whether the called member function potentially invalidates
90
  /// pointers referring to the container object's inner buffer.
91
  bool isInvalidatingMemberFunction(const CallEvent &Call) const;
92
93
  /// Mark pointer symbols associated with the given memory region released
94
  /// in the program state.
95
  void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
96
                              const MemRegion *ObjRegion,
97
                              CheckerContext &C) const;
98
99
  /// Standard library functions that take a non-const `basic_string` argument by
100
  /// reference may invalidate its inner pointers. Check for these cases and
101
  /// mark the pointers released.
102
  void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,
103
                              CheckerContext &C) const;
104
105
  /// Record the connection between raw pointers referring to a container
106
  /// object's inner buffer and the object's memory region in the program state.
107
  /// Mark potentially invalidated pointers released.
108
  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
109
110
  /// Clean up the program state map.
111
  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
112
};
113
114
} // end anonymous namespace
115
116
bool InnerPointerChecker::isInvalidatingMemberFunction(
117
1.26k
        const CallEvent &Call) const {
118
1.26k
  if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
119
586
    OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
120
586
    if (Opc == OO_Equal || 
Opc == OO_PlusEqual562
)
121
25
      return true;
122
561
    return false;
123
561
  }
124
676
  return (isa<CXXDestructorCall>(Call) || 
Call.isCalled(AppendFn)330
||
125
676
          
Call.isCalled(AssignFn)329
||
Call.isCalled(ClearFn)328
||
126
676
          
Call.isCalled(EraseFn)326
||
Call.isCalled(InsertFn)325
||
127
676
          
Call.isCalled(PopBackFn)324
||
Call.isCalled(PushBackFn)323
||
128
676
          
Call.isCalled(ReplaceFn)322
||
Call.isCalled(ReserveFn)321
||
129
676
          
Call.isCalled(ResizeFn)320
||
Call.isCalled(ShrinkToFitFn)319
||
130
676
          
Call.isCalled(SwapFn)318
);
131
676
}
132
133
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
134
                                                 ProgramStateRef State,
135
                                                 const MemRegion *MR,
136
388
                                                 CheckerContext &C) const {
137
388
  if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
138
32
    const Expr *Origin = Call.getOriginExpr();
139
36
    for (const auto Symbol : *PS) {
140
36
      // NOTE: `Origin` may be null, and will be stored so in the symbol's
141
36
      // `RefState` in MallocChecker's `RegionState` program state map.
142
36
      State = allocation_state::markReleased(State, Symbol, Origin);
143
36
    }
144
32
    State = State->remove<RawPtrMap>(MR);
145
32
    C.addTransition(State);
146
32
    return;
147
32
  }
148
388
}
149
150
void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
151
                                                 ProgramStateRef State,
152
16.0k
                                                 CheckerContext &C) const {
153
16.0k
  if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
154
15.9k
    const FunctionDecl *FD = FC->getDecl();
155
15.9k
    if (!FD || 
!FD->isInStdNamespace()15.9k
)
156
15.9k
      return;
157
80
158
275
    
for (unsigned I = 0, E = FD->getNumParams(); 80
I != E;
++I195
) {
159
195
      QualType ParamTy = FD->getParamDecl(I)->getType();
160
195
      if (!ParamTy->isReferenceType() ||
161
195
          
ParamTy->getPointeeType().isConstQualified()53
)
162
171
        continue;
163
24
164
24
      // In case of member operator calls, `this` is counted as an
165
24
      // argument but not as a parameter.
166
24
      bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
167
24
      unsigned ArgI = isaMemberOpCall ? 
I+10
: I;
168
24
169
24
      SVal Arg = FC->getArgSVal(ArgI);
170
24
      const auto *ArgRegion =
171
24
          dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
172
24
      if (!ArgRegion)
173
20
        continue;
174
4
175
4
      markPtrSymbolsReleased(Call, State, ArgRegion, C);
176
4
    }
177
80
  }
178
16.0k
}
179
180
// [string.require]
181
//
182
// "References, pointers, and iterators referring to the elements of a
183
// basic_string sequence may be invalidated by the following uses of that
184
// basic_string object:
185
//
186
// -- As an argument to any standard library function taking a reference
187
// to non-const basic_string as an argument. For example, as an argument to
188
// non-member functions swap(), operator>>(), and getline(), or as an argument
189
// to basic_string::swap().
190
//
191
// -- Calling non-const member functions, except operator[], at, front, back,
192
// begin, rbegin, end, and rend."
193
194
void InnerPointerChecker::checkPostCall(const CallEvent &Call,
195
17.1k
                                        CheckerContext &C) const {
196
17.1k
  ProgramStateRef State = C.getState();
197
17.1k
198
17.1k
  if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
199
2.05k
    // TODO: Do we need these to be typed?
200
2.05k
    const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
201
2.05k
        ICall->getCXXThisVal().getAsRegion());
202
2.05k
    if (!ObjRegion)
203
750
      return;
204
1.30k
205
1.30k
    if (Call.isCalled(CStrFn) || 
Call.isCalled(DataFn)1.27k
) {
206
42
      SVal RawPtr = Call.getReturnValue();
207
42
      if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
208
42
        // Start tracking this raw pointer by adding it to the set of symbols
209
42
        // associated with this container object in the program state map.
210
42
211
42
        PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
212
42
        const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
213
42
        PtrSet Set = SetPtr ? 
*SetPtr5
:
F.getEmptySet()37
;
214
42
        assert(C.wasInlined || !Set.contains(Sym));
215
42
        Set = F.add(Set, Sym);
216
42
217
42
        State = State->set<RawPtrMap>(ObjRegion, Set);
218
42
        C.addTransition(State);
219
42
      }
220
42
      return;
221
42
    }
222
1.26k
223
1.26k
    // Check [string.require] / second point.
224
1.26k
    if (isInvalidatingMemberFunction(Call)) {
225
384
      markPtrSymbolsReleased(Call, State, ObjRegion, C);
226
384
      return;
227
384
    }
228
16.0k
  }
229
16.0k
230
16.0k
  // Check [string.require] / first point.
231
16.0k
  checkFunctionArguments(Call, State, C);
232
16.0k
}
233
234
void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
235
62.5k
                                           CheckerContext &C) const {
236
62.5k
  ProgramStateRef State = C.getState();
237
62.5k
  PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
238
62.5k
  RawPtrMapTy RPM = State->get<RawPtrMap>();
239
62.5k
  for (const auto Entry : RPM) {
240
95
    if (!SymReaper.isLiveRegion(Entry.first)) {
241
0
      // Due to incomplete destructor support, some dead regions might
242
0
      // remain in the program state map. Clean them up.
243
0
      State = State->remove<RawPtrMap>(Entry.first);
244
0
    }
245
95
    if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {
246
95
      PtrSet CleanedUpSet = *OldSet;
247
106
      for (const auto Symbol : Entry.second) {
248
106
        if (!SymReaper.isLive(Symbol))
249
11
          CleanedUpSet = F.remove(CleanedUpSet, Symbol);
250
106
      }
251
95
      State = CleanedUpSet.isEmpty()
252
95
                  ? 
State->remove<RawPtrMap>(Entry.first)9
253
95
                  : 
State->set<RawPtrMap>(Entry.first, CleanedUpSet)86
;
254
95
    }
255
95
  }
256
62.5k
  C.addTransition(State);
257
62.5k
}
258
259
namespace clang {
260
namespace ento {
261
namespace allocation_state {
262
263
30
std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
264
30
  return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
265
30
}
266
267
60
const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
268
60
  RawPtrMapTy Map = State->get<RawPtrMap>();
269
60
  for (const auto Entry : Map) {
270
60
    if (Entry.second.contains(Sym)) {
271
60
      return Entry.first;
272
60
    }
273
60
  }
274
60
  
return nullptr0
;
275
60
}
276
277
} // end namespace allocation_state
278
} // end namespace ento
279
} // end namespace clang
280
281
std::shared_ptr<PathDiagnosticPiece>
282
InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
283
                                                      BugReporterContext &BRC,
284
1.58k
                                                      BugReport &) {
285
1.58k
  if (!isSymbolTracked(N->getState(), PtrToBuf) ||
286
1.58k
      
isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)482
)
287
1.55k
    return nullptr;
288
30
289
30
  const Stmt *S = PathDiagnosticLocation::getStmt(N);
290
30
  if (!S)
291
0
    return nullptr;
292
30
293
30
  const MemRegion *ObjRegion =
294
30
      allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);
295
30
  const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
296
30
  QualType ObjTy = TypedRegion->getValueType();
297
30
298
30
  SmallString<256> Buf;
299
30
  llvm::raw_svector_ostream OS(Buf);
300
30
  OS << "Pointer to inner buffer of '" << ObjTy.getAsString()
301
30
     << "' obtained here";
302
30
  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
303
30
                             N->getLocationContext());
304
30
  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
305
30
                                                    nullptr);
306
30
}
307
308
40
void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
309
40
  registerInnerPointerCheckerAux(Mgr);
310
40
  Mgr.registerChecker<InnerPointerChecker>();
311
40
}
312
313
40
bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) {
314
40
  return true;
315
40
}