Coverage Report

Created: 2020-02-15 09:57

/Users/buildslave/jenkins/workspace/coverage/llvm-project/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 PathDiagnosticPieceRef
58
    VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
59
              PathSensitiveBugReport &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
54
        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.61k
        const CallEvent &Call) const {
118
1.61k
  if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
119
709
    OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
120
709
    if (Opc == OO_Equal || 
Opc == OO_PlusEqual697
)
121
26
      return true;
122
683
    return false;
123
683
  }
124
903
  return (isa<CXXDestructorCall>(Call) || 
Call.isCalled(AppendFn)498
||
125
903
          
Call.isCalled(AssignFn)497
||
Call.isCalled(ClearFn)496
||
126
903
          
Call.isCalled(EraseFn)494
||
Call.isCalled(InsertFn)493
||
127
903
          
Call.isCalled(PopBackFn)492
||
Call.isCalled(PushBackFn)491
||
128
903
          
Call.isCalled(ReplaceFn)490
||
Call.isCalled(ReserveFn)489
||
129
903
          
Call.isCalled(ResizeFn)488
||
Call.isCalled(ShrinkToFitFn)487
||
130
903
          
Call.isCalled(SwapFn)486
);
131
903
}
132
133
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
134
                                                 ProgramStateRef State,
135
                                                 const MemRegion *MR,
136
481
                                                 CheckerContext &C) const {
137
481
  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
481
}
149
150
void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
151
                                                 ProgramStateRef State,
152
21.5k
                                                 CheckerContext &C) const {
153
21.5k
  if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
154
21.5k
    const FunctionDecl *FD = FC->getDecl();
155
21.5k
    if (!FD || 
!FD->isInStdNamespace()21.5k
)
156
21.4k
      return;
157
146
158
660
    
for (unsigned I = 0, E = FD->getNumParams(); 146
I != E;
++I514
) {
159
514
      QualType ParamTy = FD->getParamDecl(I)->getType();
160
514
      if (!ParamTy->isReferenceType() ||
161
514
          
ParamTy->getPointeeType().isConstQualified()104
)
162
462
        continue;
163
52
164
52
      // In case of member operator calls, `this` is counted as an
165
52
      // argument but not as a parameter.
166
52
      bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
167
52
      unsigned ArgI = isaMemberOpCall ? 
I+10
: I;
168
52
169
52
      SVal Arg = FC->getArgSVal(ArgI);
170
52
      const auto *ArgRegion =
171
52
          dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
172
52
      if (!ArgRegion)
173
15
        continue;
174
37
175
37
      markPtrSymbolsReleased(Call, State, ArgRegion, C);
176
37
    }
177
146
  }
178
21.5k
}
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
23.6k
                                        CheckerContext &C) const {
196
23.6k
  ProgramStateRef State = C.getState();
197
23.6k
198
23.6k
  if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
199
3.19k
    // TODO: Do we need these to be typed?
200
3.19k
    const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
201
3.19k
        ICall->getCXXThisVal().getAsRegion());
202
3.19k
    if (!ObjRegion)
203
1.53k
      return;
204
1.65k
205
1.65k
    if (Call.isCalled(CStrFn) || 
Call.isCalled(DataFn)1.62k
) {
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.61k
223
1.61k
    // Check [string.require] / second point.
224
1.61k
    if (isInvalidatingMemberFunction(Call)) {
225
444
      markPtrSymbolsReleased(Call, State, ObjRegion, C);
226
444
      return;
227
444
    }
228
21.5k
  }
229
21.5k
230
21.5k
  // Check [string.require] / first point.
231
21.5k
  checkFunctionArguments(Call, State, C);
232
21.5k
}
233
234
void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
235
73.4k
                                           CheckerContext &C) const {
236
73.4k
  ProgramStateRef State = C.getState();
237
73.4k
  PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
238
73.4k
  RawPtrMapTy RPM = State->get<RawPtrMap>();
239
73.4k
  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
73.4k
  C.addTransition(State);
257
73.4k
}
258
259
namespace clang {
260
namespace ento {
261
namespace allocation_state {
262
263
30
std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
264
30
  return std::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
PathDiagnosticPieceRef InnerPointerChecker::InnerPointerBRVisitor::VisitNode(
282
1.58k
    const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
283
1.58k
  if (!isSymbolTracked(N->getState(), PtrToBuf) ||
284
1.58k
      
isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)482
)
285
1.55k
    return nullptr;
286
30
287
30
  const Stmt *S = N->getStmtForDiagnostics();
288
30
  if (!S)
289
0
    return nullptr;
290
30
291
30
  const MemRegion *ObjRegion =
292
30
      allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);
293
30
  const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
294
30
  QualType ObjTy = TypedRegion->getValueType();
295
30
296
30
  SmallString<256> Buf;
297
30
  llvm::raw_svector_ostream OS(Buf);
298
30
  OS << "Pointer to inner buffer of '" << ObjTy.getAsString()
299
30
     << "' obtained here";
300
30
  PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
301
30
                             N->getLocationContext());
302
30
  return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
303
30
}
304
305
54
void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
306
54
  registerInnerPointerCheckerAux(Mgr);
307
54
  Mgr.registerChecker<InnerPointerChecker>();
308
54
}
309
310
54
bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) {
311
54
  return true;
312
54
}