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
|