1 //===----- UninitializedPointee.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 functions and methods for handling pointers and references
10 // to reduce the size and complexity of UninitializedObjectChecker.cpp.
11 //
12 // To read about command line options and documentation about how the checker
13 // works, refer to UninitializedObjectChecker.h.
14 //
15 //===----------------------------------------------------------------------===//
16 
17 #include "UninitializedObject.h"
18 #include <optional>
19 
20 using namespace clang;
21 using namespace clang::ento;
22 
23 namespace {
24 
25 /// Represents a pointer or a reference field.
26 class LocField final : public FieldNode {
27   /// We'll store whether the pointee or the pointer itself is uninitialited.
28   const bool IsDereferenced;
29 
30 public:
31   LocField(const FieldRegion *FR, const bool IsDereferenced = true)
32       : FieldNode(FR), IsDereferenced(IsDereferenced) {}
33 
34   void printNoteMsg(llvm::raw_ostream &Out) const override {
35     if (IsDereferenced)
36       Out << "uninitialized pointee ";
37     else
38       Out << "uninitialized pointer ";
39   }
40 
41   void printPrefix(llvm::raw_ostream &Out) const override {}
42 
43   void printNode(llvm::raw_ostream &Out) const override {
44     Out << getVariableName(getDecl());
45   }
46 
47   void printSeparator(llvm::raw_ostream &Out) const override {
48     if (getDecl()->getType()->isPointerType())
49       Out << "->";
50     else
51       Out << '.';
52   }
53 };
54 
55 /// Represents a nonloc::LocAsInteger or void* field, that point to objects, but
56 /// needs to be casted back to its dynamic type for a correct note message.
57 class NeedsCastLocField final : public FieldNode {
58   QualType CastBackType;
59 
60 public:
61   NeedsCastLocField(const FieldRegion *FR, const QualType &T)
62       : FieldNode(FR), CastBackType(T) {}
63 
64   void printNoteMsg(llvm::raw_ostream &Out) const override {
65     Out << "uninitialized pointee ";
66   }
67 
68   void printPrefix(llvm::raw_ostream &Out) const override {
69     // If this object is a nonloc::LocAsInteger.
70     if (getDecl()->getType()->isIntegerType())
71       Out << "reinterpret_cast";
72     // If this pointer's dynamic type is different then it's static type.
73     else
74       Out << "static_cast";
75     Out << '<' << CastBackType.getAsString() << ">(";
76   }
77 
78   void printNode(llvm::raw_ostream &Out) const override {
79     Out << getVariableName(getDecl()) << ')';
80   }
81 
82   void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; }
83 };
84 
85 /// Represents a Loc field that points to itself.
86 class CyclicLocField final : public FieldNode {
87 
88 public:
89   CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
90 
91   void printNoteMsg(llvm::raw_ostream &Out) const override {
92     Out << "object references itself ";
93   }
94 
95   void printPrefix(llvm::raw_ostream &Out) const override {}
96 
97   void printNode(llvm::raw_ostream &Out) const override {
98     Out << getVariableName(getDecl());
99   }
100 
101   void printSeparator(llvm::raw_ostream &Out) const override {
102     llvm_unreachable("CyclicLocField objects must be the last node of the "
103                      "fieldchain!");
104   }
105 };
106 
107 } // end of anonymous namespace
108 
109 // Utility function declarations.
110 
111 struct DereferenceInfo {
112   const TypedValueRegion *R;
113   const bool NeedsCastBack;
114   const bool IsCyclic;
115   DereferenceInfo(const TypedValueRegion *R, bool NCB, bool IC)
116       : R(R), NeedsCastBack(NCB), IsCyclic(IC) {}
117 };
118 
119 /// Dereferences \p FR and returns with the pointee's region, and whether it
120 /// needs to be casted back to it's location type. If for whatever reason
121 /// dereferencing fails, returns std::nullopt.
122 static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
123                                                   const FieldRegion *FR);
124 
125 /// Returns whether \p T can be (transitively) dereferenced to a void pointer
126 /// type (void*, void**, ...).
127 static bool isVoidPointer(QualType T);
128 
129 //===----------------------------------------------------------------------===//
130 //                   Methods for FindUninitializedFields.
131 //===----------------------------------------------------------------------===//
132 
133 bool FindUninitializedFields::isDereferencableUninit(
134     const FieldRegion *FR, FieldChainInfo LocalChain) {
135 
136   SVal V = State->getSVal(FR);
137 
138   assert((isDereferencableType(FR->getDecl()->getType()) ||
139           isa<nonloc::LocAsInteger>(V)) &&
140          "This method only checks dereferenceable objects!");
141 
142   if (V.isUnknown() || isa<loc::ConcreteInt>(V)) {
143     IsAnyFieldInitialized = true;
144     return false;
145   }
146 
147   if (V.isUndef()) {
148     return addFieldToUninits(
149         LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR);
150   }
151 
152   if (!Opts.CheckPointeeInitialization) {
153     IsAnyFieldInitialized = true;
154     return false;
155   }
156 
157   // At this point the pointer itself is initialized and points to a valid
158   // location, we'll now check the pointee.
159   std::optional<DereferenceInfo> DerefInfo = dereference(State, FR);
160   if (!DerefInfo) {
161     IsAnyFieldInitialized = true;
162     return false;
163   }
164 
165   if (DerefInfo->IsCyclic)
166     return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR);
167 
168   const TypedValueRegion *R = DerefInfo->R;
169   const bool NeedsCastBack = DerefInfo->NeedsCastBack;
170 
171   QualType DynT = R->getLocationType();
172   QualType PointeeT = DynT->getPointeeType();
173 
174   if (PointeeT->isStructureOrClassType()) {
175     if (NeedsCastBack)
176       return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT)));
177     return isNonUnionUninit(R, LocalChain.add(LocField(FR)));
178   }
179 
180   if (PointeeT->isUnionType()) {
181     if (isUnionUninit(R)) {
182       if (NeedsCastBack)
183         return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)),
184                                  R);
185       return addFieldToUninits(LocalChain.add(LocField(FR)), R);
186     } else {
187       IsAnyFieldInitialized = true;
188       return false;
189     }
190   }
191 
192   if (PointeeT->isArrayType()) {
193     IsAnyFieldInitialized = true;
194     return false;
195   }
196 
197   assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) &&
198          "At this point FR must either have a primitive dynamic type, or it "
199          "must be a null, undefined, unknown or concrete pointer!");
200 
201   SVal PointeeV = State->getSVal(R);
202 
203   if (isPrimitiveUninit(PointeeV)) {
204     if (NeedsCastBack)
205       return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R);
206     return addFieldToUninits(LocalChain.add(LocField(FR)), R);
207   }
208 
209   IsAnyFieldInitialized = true;
210   return false;
211 }
212 
213 //===----------------------------------------------------------------------===//
214 //                           Utility functions.
215 //===----------------------------------------------------------------------===//
216 
217 static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
218                                                   const FieldRegion *FR) {
219 
220   llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
221 
222   SVal V = State->getSVal(FR);
223   assert(V.getAsRegion() && "V must have an underlying region!");
224 
225   // If the static type of the field is a void pointer, or it is a
226   // nonloc::LocAsInteger, we need to cast it back to the dynamic type before
227   // dereferencing.
228   bool NeedsCastBack =
229       isVoidPointer(FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(V);
230 
231   // The region we'd like to acquire.
232   const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
233   if (!R)
234     return std::nullopt;
235 
236   VisitedRegions.insert(R);
237 
238   // We acquire the dynamic type of R,
239   QualType DynT = R->getLocationType();
240 
241   while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) {
242 
243     R = Tmp->getAs<TypedValueRegion>();
244     if (!R)
245       return std::nullopt;
246 
247     // We found a cyclic pointer, like int *ptr = (int *)&ptr.
248     if (!VisitedRegions.insert(R).second)
249       return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true};
250 
251     DynT = R->getLocationType();
252     // In order to ensure that this loop terminates, we're also checking the
253     // dynamic type of R, since type hierarchy is finite.
254     if (isDereferencableType(DynT->getPointeeType()))
255       break;
256   }
257 
258   while (isa<CXXBaseObjectRegion>(R)) {
259     NeedsCastBack = true;
260     const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion());
261     if (!SuperR)
262       break;
263 
264     R = SuperR;
265   }
266 
267   return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
268 }
269 
270 static bool isVoidPointer(QualType T) {
271   while (!T.isNull()) {
272     if (T->isVoidPointerType())
273       return true;
274     T = T->getPointeeType();
275   }
276   return false;
277 }
278