Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
Line
Count
Source
1
//===----- UninitializedObject.h ---------------------------------*- 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 helper classes for UninitializedObjectChecker and
10
// documentation about the logic of it.
11
//
12
// The checker reports uninitialized fields in objects created after a
13
// constructor call.
14
//
15
// This checker has several options:
16
//   - "Pedantic" (boolean). If its not set or is set to false, the checker
17
//     won't emit warnings for objects that don't have at least one initialized
18
//     field. This may be set with
19
//
20
//     `-analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true`.
21
//
22
//   - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
23
//     warning for each uninitialized field, as opposed to emitting one warning
24
//     per constructor call, and listing the uninitialized fields that belongs
25
//     to it in notes. Defaults to false.
26
//
27
//     `-analyzer-config \
28
//         optin.cplusplus.UninitializedObject:NotesAsWarnings=true`.
29
//
30
//   - "CheckPointeeInitialization" (boolean). If set to false, the checker will
31
//     not analyze the pointee of pointer/reference fields, and will only check
32
//     whether the object itself is initialized. Defaults to false.
33
//
34
//     `-analyzer-config \
35
//         optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
36
//
37
//     TODO: With some clever heuristics, some pointers should be dereferenced
38
//     by default. For example, if the pointee is constructed within the
39
//     constructor call, it's reasonable to say that no external object
40
//     references it, and we wouldn't generate multiple report on the same
41
//     pointee.
42
//
43
//   - "IgnoreRecordsWithField" (string). If supplied, the checker will not
44
//     analyze structures that have a field with a name or type name that
45
//     matches the given pattern. Defaults to "".
46
//
47
//     `-analyzer-config \
48
// optin.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`.
49
//
50
//   - "IgnoreGuardedFields" (boolean). If set to true, the checker will analyze
51
//     _syntactically_ whether the found uninitialized object is used without a
52
//     preceding assert call. Defaults to false.
53
//
54
//     `-analyzer-config \
55
//         optin.cplusplus.UninitializedObject:IgnoreGuardedFields=true`.
56
//
57
// Most of the following methods as well as the checker itself is defined in
58
// UninitializedObjectChecker.cpp.
59
//
60
// Some methods are implemented in UninitializedPointee.cpp, to reduce the
61
// complexity of the main checker file.
62
//
63
//===----------------------------------------------------------------------===//
64
65
#ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
66
#define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
67
68
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
69
70
namespace clang {
71
namespace ento {
72
73
struct UninitObjCheckerOptions {
74
  bool IsPedantic = false;
75
  bool ShouldConvertNotesToWarnings = false;
76
  bool CheckPointeeInitialization = false;
77
  std::string IgnoredRecordsWithFieldPattern;
78
  bool IgnoreGuardedFields = false;
79
};
80
81
/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this
82
/// interface to store addinitional information about fields. As described
83
/// later, a list of these objects (i.e. "fieldchain") will be constructed and
84
/// used for printing note messages should an uninitialized value be found.
85
class FieldNode {
86
protected:
87
  const FieldRegion *FR;
88
89
  /// FieldNodes are never meant to be created on the heap, see
90
  /// FindUninitializedFields::addFieldToUninits().
91
  /* non-virtual */ ~FieldNode() = default;
92
93
public:
94
357
  FieldNode(const FieldRegion *FR) : FR(FR) {}
95
96
  // We'll delete all of these special member functions to force the users of
97
  // this interface to only store references to FieldNode objects in containers.
98
  FieldNode() = delete;
99
  FieldNode(const FieldNode &) = delete;
100
  FieldNode(FieldNode &&) = delete;
101
  FieldNode &operator=(const FieldNode &) = delete;
102
  FieldNode &operator=(const FieldNode &&) = delete;
103
104
455
  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(this); }
105
106
  /// Helper method for uniqueing.
107
356
  bool isSameRegion(const FieldRegion *OtherFR) const {
108
    // Special FieldNode descendants may wrap nullpointers (for example if they
109
    // describe a special relationship between two elements of the fieldchain)
110
    // -- we wouldn't like to unique these objects.
111
356
    if (FR == nullptr)
112
82
      return false;
113
114
274
    return FR == OtherFR;
115
356
  }
116
117
553
  const FieldRegion *getRegion() const { return FR; }
118
302
  const FieldDecl *getDecl() const {
119
302
    assert(FR);
120
302
    return FR->getDecl();
121
302
  }
122
123
  // When a fieldchain is printed, it will have the following format (without
124
  // newline, indices are in order of insertion, from 1 to n):
125
  //
126
  // <note_message_n>'<prefix_n><prefix_n-1>...<prefix_1>
127
  //       this-><node_1><separator_1><node_2><separator_2>...<node_n>'
128
129
  /// If this is the last element of the fieldchain, this method will print the
130
  /// note message associated with it.
131
  /// The note message should state something like "uninitialized field" or
132
  /// "uninitialized pointee" etc.
133
  virtual void printNoteMsg(llvm::raw_ostream &Out) const = 0;
134
135
  /// Print any prefixes before the fieldchain. Could contain casts, etc.
136
  virtual void printPrefix(llvm::raw_ostream &Out) const = 0;
137
138
  /// Print the node. Should contain the name of the field stored in FR.
139
  virtual void printNode(llvm::raw_ostream &Out) const = 0;
140
141
  /// Print the separator. For example, fields may be separated with '.' or
142
  /// "->".
143
  virtual void printSeparator(llvm::raw_ostream &Out) const = 0;
144
145
2
  virtual bool isBase() const { return false; }
146
};
147
148
/// Returns with Field's name. This is a helper function to get the correct name
149
/// even if Field is a captured lambda variable.
150
std::string getVariableName(const FieldDecl *Field);
151
152
/// Represents a field chain. A field chain is a list of fields where the first
153
/// element of the chain is the object under checking (not stored), and every
154
/// other element is a field, and the element that precedes it is the object
155
/// that contains it.
156
///
157
/// Note that this class is immutable (essentially a wrapper around an
158
/// ImmutableList), new FieldChainInfo objects may be created by member
159
/// functions such as add() and replaceHead().
160
class FieldChainInfo {
161
public:
162
  using FieldChain = llvm::ImmutableList<const FieldNode &>;
163
164
private:
165
  FieldChain::Factory &ChainFactory;
166
  FieldChain Chain;
167
168
  FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain)
169
26
      : FieldChainInfo(F) {
170
26
    Chain = NewChain;
171
26
  }
172
173
public:
174
  FieldChainInfo() = delete;
175
309
  FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {}
176
  FieldChainInfo(const FieldChainInfo &Other) = default;
177
178
  /// Constructs a new FieldChainInfo object with \p FN appended.
179
  template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN);
180
181
  /// Constructs a new FieldChainInfo object with \p FN as the new head of the
182
  /// list.
183
  template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN);
184
185
  bool contains(const FieldRegion *FR) const;
186
80
  bool isEmpty() const { return Chain.isEmpty(); }
187
188
392
  const FieldNode &getHead() const { return Chain.getHead(); }
189
196
  const FieldRegion *getUninitRegion() const { return getHead().getRegion(); }
190
191
  void printNoteMsg(llvm::raw_ostream &Out) const;
192
};
193
194
using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>;
195
196
/// Searches for and stores uninitialized fields in a non-union object.
197
class FindUninitializedFields {
198
  ProgramStateRef State;
199
  const TypedValueRegion *const ObjectR;
200
201
  const UninitObjCheckerOptions Opts;
202
  bool IsAnyFieldInitialized = false;
203
204
  FieldChainInfo::FieldChain::Factory ChainFactory;
205
206
  /// A map for assigning uninitialized regions to note messages. For example,
207
  ///
208
  ///   struct A {
209
  ///     int x;
210
  ///   };
211
  ///
212
  ///   A a;
213
  ///
214
  /// After analyzing `a`, the map will contain a pair for `a.x`'s region and
215
  /// the note message "uninitialized field 'this->x'.
216
  UninitFieldMap UninitFields;
217
218
public:
219
  /// Constructs the FindUninitializedField object, searches for and stores
220
  /// uninitialized fields in R.
221
  FindUninitializedFields(ProgramStateRef State,
222
                          const TypedValueRegion *const R,
223
                          const UninitObjCheckerOptions &Opts);
224
225
  /// Returns with the modified state and a map of (uninitialized region,
226
  /// note message) pairs.
227
283
  std::pair<ProgramStateRef, const UninitFieldMap &> getResults() {
228
283
    return {State, UninitFields};
229
283
  }
230
231
  /// Returns whether the analyzed region contains at least one initialized
232
  /// field. Note that this includes subfields as well, not just direct ones,
233
  /// and will return false if an uninitialized pointee is found with
234
  /// CheckPointeeInitialization enabled.
235
109
  bool isAnyFieldInitialized() { return IsAnyFieldInitialized; }
236
237
private:
238
  // For the purposes of this checker, we'll regard the analyzed region as a
239
  // directed tree, where
240
  //   * the root is the object under checking
241
  //   * every node is an object that is
242
  //     - a union
243
  //     - a non-union record
244
  //     - dereferenceable (see isDereferencableType())
245
  //     - an array
246
  //     - of a primitive type (see isPrimitiveType())
247
  //   * the parent of each node is the object that contains it
248
  //   * every leaf is an array, a primitive object, a nullptr or an undefined
249
  //   pointer.
250
  //
251
  // Example:
252
  //
253
  //   struct A {
254
  //      struct B {
255
  //        int x, y = 0;
256
  //      };
257
  //      B b;
258
  //      int *iptr = new int;
259
  //      B* bptr;
260
  //
261
  //      A() {}
262
  //   };
263
  //
264
  // The directed tree:
265
  //
266
  //           ->x
267
  //          /
268
  //      ->b--->y
269
  //     /
270
  //    A-->iptr->(int value)
271
  //     \
272
  //      ->bptr
273
  //
274
  // From this we'll construct a vector of fieldchains, where each fieldchain
275
  // represents an uninitialized field. An uninitialized field may be a
276
  // primitive object, a pointer, a pointee or a union without a single
277
  // initialized field.
278
  // In the above example, for the default constructor call we'll end up with
279
  // these fieldchains:
280
  //
281
  //   this->b.x
282
  //   this->iptr (pointee uninit)
283
  //   this->bptr (pointer uninit)
284
  //
285
  // We'll traverse each node of the above graph with the appropriate one of
286
  // these methods:
287
288
  /// Checks the region of a union object, and returns true if no field is
289
  /// initialized within the region.
290
  bool isUnionUninit(const TypedValueRegion *R);
291
292
  /// Checks a region of a non-union object, and returns true if an
293
  /// uninitialized field is found within the region.
294
  bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain);
295
296
  /// Checks a region of a pointer or reference object, and returns true if the
297
  /// ptr/ref object itself or any field within the pointee's region is
298
  /// uninitialized.
299
  bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain);
300
301
  /// Returns true if the value of a primitive object is uninitialized.
302
  bool isPrimitiveUninit(const SVal &V);
303
304
  // Note that we don't have a method for arrays -- the elements of an array are
305
  // often left uninitialized intentionally even when it is of a C++ record
306
  // type, so we'll assume that an array is always initialized.
307
  // TODO: Add a support for nonloc::LocAsInteger.
308
309
  /// Processes LocalChain and attempts to insert it into UninitFields. Returns
310
  /// true on success. Also adds the head of the list and \p PointeeR (if
311
  /// supplied) to the GDM as already analyzed objects.
312
  ///
313
  /// Since this class analyzes regions with recursion, we'll only store
314
  /// references to temporary FieldNode objects created on the stack. This means
315
  /// that after analyzing a leaf of the directed tree described above, the
316
  /// elements LocalChain references will be destructed, so we can't store it
317
  /// directly.
318
  bool addFieldToUninits(FieldChainInfo LocalChain,
319
                         const MemRegion *PointeeR = nullptr);
320
};
321
322
/// Returns true if T is a primitive type. An object of a primitive type only
323
/// needs to be analyzed as much as checking whether their value is undefined.
324
500
inline bool isPrimitiveType(const QualType &T) {
325
500
  return T->isBuiltinType() || 
T->isEnumeralType()52
||
326
500
         
T->isFunctionType()32
||
T->isAtomicType()32
||
327
500
         
T->isVectorType()30
||
T->isScalarType()26
;
328
500
}
329
330
942
inline bool isDereferencableType(const QualType &T) {
331
942
  return T->isAnyPointerType() || 
T->isReferenceType()704
;
332
942
}
333
334
// Template method definitions.
335
336
template <class FieldNodeT>
337
357
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
338
357
  assert(!contains(FN.getRegion()) &&
339
357
         "Can't add a field that is already a part of the "
340
357
         "fieldchain! Is this a cyclic reference?");
341
342
357
  FieldChainInfo NewChain = *this;
343
357
  NewChain.Chain = ChainFactory.add(FN, Chain);
344
357
  return NewChain;
345
357
}
UninitializedObjectChecker.cpp:clang::ento::FieldChainInfo clang::ento::FieldChainInfo::add<(anonymous namespace)::RegularField>((anonymous namespace)::RegularField const&)
Line
Count
Source
337
180
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
338
180
  assert(!contains(FN.getRegion()) &&
339
180
         "Can't add a field that is already a part of the "
340
180
         "fieldchain! Is this a cyclic reference?");
341
342
180
  FieldChainInfo NewChain = *this;
343
180
  NewChain.Chain = ChainFactory.add(FN, Chain);
344
180
  return NewChain;
345
180
}
UninitializedObjectChecker.cpp:clang::ento::FieldChainInfo clang::ento::FieldChainInfo::add<(anonymous namespace)::BaseClass>((anonymous namespace)::BaseClass const&)
Line
Count
Source
337
80
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
338
80
  assert(!contains(FN.getRegion()) &&
339
80
         "Can't add a field that is already a part of the "
340
80
         "fieldchain! Is this a cyclic reference?");
341
342
80
  FieldChainInfo NewChain = *this;
343
80
  NewChain.Chain = ChainFactory.add(FN, Chain);
344
80
  return NewChain;
345
80
}
UninitializedPointee.cpp:clang::ento::FieldChainInfo clang::ento::FieldChainInfo::add<(anonymous namespace)::LocField>((anonymous namespace)::LocField const&)
Line
Count
Source
337
76
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
338
76
  assert(!contains(FN.getRegion()) &&
339
76
         "Can't add a field that is already a part of the "
340
76
         "fieldchain! Is this a cyclic reference?");
341
342
76
  FieldChainInfo NewChain = *this;
343
76
  NewChain.Chain = ChainFactory.add(FN, Chain);
344
76
  return NewChain;
345
76
}
UninitializedPointee.cpp:clang::ento::FieldChainInfo clang::ento::FieldChainInfo::add<(anonymous namespace)::CyclicLocField>((anonymous namespace)::CyclicLocField const&)
Line
Count
Source
337
6
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
338
6
  assert(!contains(FN.getRegion()) &&
339
6
         "Can't add a field that is already a part of the "
340
6
         "fieldchain! Is this a cyclic reference?");
341
342
6
  FieldChainInfo NewChain = *this;
343
6
  NewChain.Chain = ChainFactory.add(FN, Chain);
344
6
  return NewChain;
345
6
}
UninitializedPointee.cpp:clang::ento::FieldChainInfo clang::ento::FieldChainInfo::add<(anonymous namespace)::NeedsCastLocField>((anonymous namespace)::NeedsCastLocField const&)
Line
Count
Source
337
15
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
338
15
  assert(!contains(FN.getRegion()) &&
339
15
         "Can't add a field that is already a part of the "
340
15
         "fieldchain! Is this a cyclic reference?");
341
342
15
  FieldChainInfo NewChain = *this;
343
15
  NewChain.Chain = ChainFactory.add(FN, Chain);
344
15
  return NewChain;
345
15
}
346
347
template <class FieldNodeT>
348
26
inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) {
349
26
  FieldChainInfo NewChain(ChainFactory, Chain.getTail());
350
26
  return NewChain.add(FN);
351
26
}
352
353
} // end of namespace ento
354
} // end of namespace clang
355
356
#endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H