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