reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341
  342
  343
  344
  345
  346
  347
  348
  349
  350
  351
  352
  353
  354
  355
  356
//===----- UninitializedObject.h ---------------------------------*- C++ -*-==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file defines helper classes for UninitializedObjectChecker and
// documentation about the logic of it.
//
// The checker reports uninitialized fields in objects created after a
// constructor call.
//
// This checker has several options:
//   - "Pedantic" (boolean). If its not set or is set to false, the checker
//     won't emit warnings for objects that don't have at least one initialized
//     field. This may be set with
//
//     `-analyzer-config optin.cplusplus.UninitializedObject:Pedantic=true`.
//
//   - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
//     warning for each uninitialized field, as opposed to emitting one warning
//     per constructor call, and listing the uninitialized fields that belongs
//     to it in notes. Defaults to false.
//
//     `-analyzer-config \
//         optin.cplusplus.UninitializedObject:NotesAsWarnings=true`.
//
//   - "CheckPointeeInitialization" (boolean). If set to false, the checker will
//     not analyze the pointee of pointer/reference fields, and will only check
//     whether the object itself is initialized. Defaults to false.
//
//     `-analyzer-config \
//         optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
//
//     TODO: With some clever heuristics, some pointers should be dereferenced
//     by default. For example, if the pointee is constructed within the
//     constructor call, it's reasonable to say that no external object
//     references it, and we wouldn't generate multiple report on the same
//     pointee.
//
//   - "IgnoreRecordsWithField" (string). If supplied, the checker will not
//     analyze structures that have a field with a name or type name that
//     matches the given pattern. Defaults to "".
//
//     `-analyzer-config \
// optin.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`.
//
//   - "IgnoreGuardedFields" (boolean). If set to true, the checker will analyze
//     _syntactically_ whether the found uninitialized object is used without a
//     preceding assert call. Defaults to false.
//
//     `-analyzer-config \
//         optin.cplusplus.UninitializedObject:IgnoreGuardedFields=true`.
//
// Most of the following methods as well as the checker itself is defined in
// UninitializedObjectChecker.cpp.
//
// Some methods are implemented in UninitializedPointee.cpp, to reduce the
// complexity of the main checker file.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
#define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H

#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"

namespace clang {
namespace ento {

struct UninitObjCheckerOptions {
  bool IsPedantic = false;
  bool ShouldConvertNotesToWarnings = false;
  bool CheckPointeeInitialization = false;
  std::string IgnoredRecordsWithFieldPattern;
  bool IgnoreGuardedFields = false;
};

/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this
/// interface to store addinitional information about fields. As described
/// later, a list of these objects (i.e. "fieldchain") will be constructed and
/// used for printing note messages should an uninitialized value be found.
class FieldNode {
protected:
  const FieldRegion *FR;

  /// FieldNodes are never meant to be created on the heap, see
  /// FindUninitializedFields::addFieldToUninits().
  /* non-virtual */ ~FieldNode() = default;

public:
  FieldNode(const FieldRegion *FR) : FR(FR) {}

  // We'll delete all of these special member functions to force the users of
  // this interface to only store references to FieldNode objects in containers.
  FieldNode() = delete;
  FieldNode(const FieldNode &) = delete;
  FieldNode(FieldNode &&) = delete;
  FieldNode &operator=(const FieldNode &) = delete;
  FieldNode &operator=(const FieldNode &&) = delete;

  void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(this); }

  /// Helper method for uniqueing.
  bool isSameRegion(const FieldRegion *OtherFR) const {
    // Special FieldNode descendants may wrap nullpointers (for example if they
    // describe a special relationship between two elements of the fieldchain)
    // -- we wouldn't like to unique these objects.
    if (FR == nullptr)
      return false;

    return FR == OtherFR;
  }

  const FieldRegion *getRegion() const { return FR; }
  const FieldDecl *getDecl() const {
    assert(FR);
    return FR->getDecl();
  }

  // When a fieldchain is printed, it will have the following format (without
  // newline, indices are in order of insertion, from 1 to n):
  //
  // <note_message_n>'<prefix_n><prefix_n-1>...<prefix_1>
  //       this-><node_1><separator_1><node_2><separator_2>...<node_n>'

  /// If this is the last element of the fieldchain, this method will print the
  /// note message associated with it.
  /// The note message should state something like "uninitialized field" or
  /// "uninitialized pointee" etc.
  virtual void printNoteMsg(llvm::raw_ostream &Out) const = 0;

  /// Print any prefixes before the fieldchain. Could contain casts, etc.
  virtual void printPrefix(llvm::raw_ostream &Out) const = 0;

  /// Print the node. Should contain the name of the field stored in FR.
  virtual void printNode(llvm::raw_ostream &Out) const = 0;

  /// Print the separator. For example, fields may be separated with '.' or
  /// "->".
  virtual void printSeparator(llvm::raw_ostream &Out) const = 0;

  virtual bool isBase() const { return false; }
};

/// Returns with Field's name. This is a helper function to get the correct name
/// even if Field is a captured lambda variable.
std::string getVariableName(const FieldDecl *Field);

/// Represents a field chain. A field chain is a list of fields where the first
/// element of the chain is the object under checking (not stored), and every
/// other element is a field, and the element that precedes it is the object
/// that contains it.
///
/// Note that this class is immutable (essentially a wrapper around an
/// ImmutableList), new FieldChainInfo objects may be created by member
/// functions such as add() and replaceHead().
class FieldChainInfo {
public:
  using FieldChain = llvm::ImmutableList<const FieldNode &>;

private:
  FieldChain::Factory &ChainFactory;
  FieldChain Chain;

  FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain)
      : FieldChainInfo(F) {
    Chain = NewChain;
  }

public:
  FieldChainInfo() = delete;
  FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {}
  FieldChainInfo(const FieldChainInfo &Other) = default;

  /// Constructs a new FieldChainInfo object with \p FN appended.
  template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN);

  /// Constructs a new FieldChainInfo object with \p FN as the new head of the
  /// list.
  template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN);

  bool contains(const FieldRegion *FR) const;
  bool isEmpty() const { return Chain.isEmpty(); }

  const FieldNode &getHead() const { return Chain.getHead(); }
  const FieldRegion *getUninitRegion() const { return getHead().getRegion(); }

  void printNoteMsg(llvm::raw_ostream &Out) const;
};

using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>;

/// Searches for and stores uninitialized fields in a non-union object.
class FindUninitializedFields {
  ProgramStateRef State;
  const TypedValueRegion *const ObjectR;

  const UninitObjCheckerOptions Opts;
  bool IsAnyFieldInitialized = false;

  FieldChainInfo::FieldChain::Factory ChainFactory;

  /// A map for assigning uninitialized regions to note messages. For example,
  ///
  ///   struct A {
  ///     int x;
  ///   };
  ///
  ///   A a;
  ///
  /// After analyzing `a`, the map will contain a pair for `a.x`'s region and
  /// the note message "uninitialized field 'this->x'.
  UninitFieldMap UninitFields;

public:
  /// Constructs the FindUninitializedField object, searches for and stores
  /// uninitialized fields in R.
  FindUninitializedFields(ProgramStateRef State,
                          const TypedValueRegion *const R,
                          const UninitObjCheckerOptions &Opts);

  /// Returns with the modified state and a map of (uninitialized region,
  /// note message) pairs.
  std::pair<ProgramStateRef, const UninitFieldMap &> getResults() {
    return {State, UninitFields};
  }

  /// Returns whether the analyzed region contains at least one initialized
  /// field. Note that this includes subfields as well, not just direct ones,
  /// and will return false if an uninitialized pointee is found with
  /// CheckPointeeInitialization enabled.
  bool isAnyFieldInitialized() { return IsAnyFieldInitialized; }

private:
  // For the purposes of this checker, we'll regard the analyzed region as a
  // directed tree, where
  //   * the root is the object under checking
  //   * every node is an object that is
  //     - a union
  //     - a non-union record
  //     - dereferenceable (see isDereferencableType())
  //     - an array
  //     - of a primitive type (see isPrimitiveType())
  //   * the parent of each node is the object that contains it
  //   * every leaf is an array, a primitive object, a nullptr or an undefined
  //   pointer.
  //
  // Example:
  //
  //   struct A {
  //      struct B {
  //        int x, y = 0;
  //      };
  //      B b;
  //      int *iptr = new int;
  //      B* bptr;
  //
  //      A() {}
  //   };
  //
  // The directed tree:
  //
  //           ->x
  //          /
  //      ->b--->y
  //     /
  //    A-->iptr->(int value)
  //     \
  //      ->bptr
  //
  // From this we'll construct a vector of fieldchains, where each fieldchain
  // represents an uninitialized field. An uninitialized field may be a
  // primitive object, a pointer, a pointee or a union without a single
  // initialized field.
  // In the above example, for the default constructor call we'll end up with
  // these fieldchains:
  //
  //   this->b.x
  //   this->iptr (pointee uninit)
  //   this->bptr (pointer uninit)
  //
  // We'll traverse each node of the above graph with the appropriate one of
  // these methods:

  /// Checks the region of a union object, and returns true if no field is
  /// initialized within the region.
  bool isUnionUninit(const TypedValueRegion *R);

  /// Checks a region of a non-union object, and returns true if an
  /// uninitialized field is found within the region.
  bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain);

  /// Checks a region of a pointer or reference object, and returns true if the
  /// ptr/ref object itself or any field within the pointee's region is
  /// uninitialized.
  bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain);

  /// Returns true if the value of a primitive object is uninitialized.
  bool isPrimitiveUninit(const SVal &V);

  // Note that we don't have a method for arrays -- the elements of an array are
  // often left uninitialized intentionally even when it is of a C++ record
  // type, so we'll assume that an array is always initialized.
  // TODO: Add a support for nonloc::LocAsInteger.

  /// Processes LocalChain and attempts to insert it into UninitFields. Returns
  /// true on success. Also adds the head of the list and \p PointeeR (if
  /// supplied) to the GDM as already analyzed objects.
  ///
  /// Since this class analyzes regions with recursion, we'll only store
  /// references to temporary FieldNode objects created on the stack. This means
  /// that after analyzing a leaf of the directed tree described above, the
  /// elements LocalChain references will be destructed, so we can't store it
  /// directly.
  bool addFieldToUninits(FieldChainInfo LocalChain,
                         const MemRegion *PointeeR = nullptr);
};

/// Returns true if T is a primitive type. An object of a primitive type only
/// needs to be analyzed as much as checking whether their value is undefined.
inline bool isPrimitiveType(const QualType &T) {
  return T->isBuiltinType() || T->isEnumeralType() ||
         T->isFunctionType() || T->isAtomicType() ||
         T->isVectorType() || T->isScalarType();
}

inline bool isDereferencableType(const QualType &T) {
  return T->isAnyPointerType() || T->isReferenceType();
}

// Template method definitions.

template <class FieldNodeT>
inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
  assert(!contains(FN.getRegion()) &&
         "Can't add a field that is already a part of the "
         "fieldchain! Is this a cyclic reference?");

  FieldChainInfo NewChain = *this;
  NewChain.Chain = ChainFactory.add(FN, Chain);
  return NewChain;
}

template <class FieldNodeT>
inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) {
  FieldChainInfo NewChain(ChainFactory, Chain.getTail());
  return NewChain.add(FN);
}

} // end of namespace ento
} // end of namespace clang

#endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H