Coverage Report

Created: 2020-09-19 12:23

/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
1.07k
      for (const auto &Entry : Map) {
66
1.07k
        if (Entry.second.contains(Sym))
67
934
          return true;
68
1.07k
      }
69
1.13k
      return false;
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
60
        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
2.25k
        const CallEvent &Call) const {
118
2.25k
  if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
119
1.28k
    OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
120
1.28k
    if (Opc == OO_Equal || 
Opc == OO_PlusEqual1.27k
)
121
167
      return true;
122
1.11k
    return false;
123
1.11k
  }
124
974
  return (isa<CXXDestructorCall>(Call) || 
Call.isCalled(AppendFn)559
||
125
558
          Call.isCalled(AssignFn) || 
Call.isCalled(ClearFn)557
||
126
555
          Call.isCalled(EraseFn) || 
Call.isCalled(InsertFn)554
||
127
553
          Call.isCalled(PopBackFn) || 
Call.isCalled(PushBackFn)552
||
128
551
          Call.isCalled(ReplaceFn) || 
Call.isCalled(ReserveFn)550
||
129
549
          Call.isCalled(ResizeFn) || 
Call.isCalled(ShrinkToFitFn)548
||
130
547
          Call.isCalled(SwapFn));
131
974
}
132
133
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
134
                                                 ProgramStateRef State,
135
                                                 const MemRegion *MR,
136
778
                                                 CheckerContext &C) const {
137
778
  if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
138
32
    const Expr *Origin = Call.getOriginExpr();
139
36
    for (const auto Symbol : *PS) {
140
      // NOTE: `Origin` may be null, and will be stored so in the symbol's
141
      // `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
778
}
149
150
void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
151
                                                 ProgramStateRef State,
152
28.9k
                                                 CheckerContext &C) const {
153
28.9k
  if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
154
28.9k
    const FunctionDecl *FD = FC->getDecl();
155
28.9k
    if (!FD || 
!FD->isInStdNamespace()28.9k
)
156
28.4k
      return;
157
429
158
1.52k
    
for (unsigned I = 0, E = FD->getNumParams(); 429
I != E;
++I1.09k
) {
159
1.09k
      QualType ParamTy = FD->getParamDecl(I)->getType();
160
1.09k
      if (!ParamTy->isReferenceType() ||
161
285
          ParamTy->getPointeeType().isConstQualified())
162
881
        continue;
163
210
164
      // In case of member operator calls, `this` is counted as an
165
      // argument but not as a parameter.
166
210
      bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
167
210
      unsigned ArgI = isaMemberOpCall ? 
I+10
: I;
168
210
169
210
      SVal Arg = FC->getArgSVal(ArgI);
170
210
      const auto *ArgRegion =
171
210
          dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
172
210
      if (!ArgRegion)
173
27
        continue;
174
183
175
183
      markPtrSymbolsReleased(Call, State, ArgRegion, C);
176
183
    }
177
429
  }
178
28.9k
}
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
32.6k
                                        CheckerContext &C) const {
196
32.6k
  ProgramStateRef State = C.getState();
197
32.6k
198
32.6k
  if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
199
    // TODO: Do we need these to be typed?
200
5.36k
    const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
201
5.36k
        ICall->getCXXThisVal().getAsRegion());
202
5.36k
    if (!ObjRegion)
203
3.06k
      return;
204
2.29k
205
2.29k
    if (Call.isCalled(CStrFn) || 
Call.isCalled(DataFn)2.27k
) {
206
42
      SVal RawPtr = Call.getReturnValue();
207
42
      if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
208
        // Start tracking this raw pointer by adding it to the set of symbols
209
        // 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
37
        PtrSet Set = SetPtr ? 
*SetPtr5
: F.getEmptySet();
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
2.25k
223
    // Check [string.require] / second point.
224
2.25k
    if (isInvalidatingMemberFunction(Call)) {
225
595
      markPtrSymbolsReleased(Call, State, ObjRegion, C);
226
595
      return;
227
595
    }
228
28.9k
  }
229
28.9k
230
  // Check [string.require] / first point.
231
28.9k
  checkFunctionArguments(Call, State, C);
232
28.9k
}
233
234
void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
235
96.8k
                                           CheckerContext &C) const {
236
96.8k
  ProgramStateRef State = C.getState();
237
96.8k
  PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
238
96.8k
  RawPtrMapTy RPM = State->get<RawPtrMap>();
239
95
  for (const auto &Entry : RPM) {
240
95
    if (!SymReaper.isLiveRegion(Entry.first)) {
241
      // Due to incomplete destructor support, some dead regions might
242
      // 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
9
                  ? State->remove<RawPtrMap>(Entry.first)
253
86
                  : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
254
95
    }
255
95
  }
256
96.8k
  C.addTransition(State);
257
96.8k
}
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
0
  return nullptr;
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
482
      isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))
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
60
void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
306
60
  registerInnerPointerCheckerAux(Mgr);
307
60
  Mgr.registerChecker<InnerPointerChecker>();
308
60
}
309
310
120
bool ento::shouldRegisterInnerPointerChecker(const CheckerManager &mgr) {
311
120
  return true;
312
120
}