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
20using namespace clang;
21using namespace clang::ento;
22
23namespace {
24
25/// Represents a pointer or a reference field.
26class LocField final : public FieldNode {
27 /// We'll store whether the pointee or the pointer itself is uninitialited.
28 const bool IsDereferenced;
29
30public:
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(Field: 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.
57class NeedsCastLocField final : public FieldNode {
58 QualType CastBackType;
59
60public:
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(Field: getDecl()) << ')';
80 }
81
82 void printSeparator(llvm::raw_ostream &Out) const override { Out << "->"; }
83};
84
85/// Represents a Loc field that points to itself.
86class CyclicLocField final : public FieldNode {
87
88public:
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(Field: 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
111struct 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.
122static 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**, ...).
127static bool isVoidPointer(QualType T);
128
129//===----------------------------------------------------------------------===//
130// Methods for FindUninitializedFields.
131//===----------------------------------------------------------------------===//
132
133bool FindUninitializedFields::isDereferencableUninit(
134 const FieldRegion *FR, FieldChainInfo LocalChain) {
135
136 SVal V = State->getSVal(R: 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>(Val: V)) {
143 IsAnyFieldInitialized = true;
144 return false;
145 }
146
147 if (V.isUndef()) {
148 return addFieldToUninits(
149 LocalChain: LocalChain.add(FN: LocField(FR, /*IsDereferenced*/ false)), PointeeR: 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: LocalChain.add(FN: CyclicLocField(FR)), PointeeR: 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: LocalChain.add(FN: NeedsCastLocField(FR, DynT)));
177 return isNonUnionUninit(R, LocalChain: LocalChain.add(FN: LocField(FR)));
178 }
179
180 if (PointeeT->isUnionType()) {
181 if (isUnionUninit(R)) {
182 if (NeedsCastBack)
183 return addFieldToUninits(LocalChain: LocalChain.add(FN: NeedsCastLocField(FR, DynT)),
184 PointeeR: R);
185 return addFieldToUninits(LocalChain: LocalChain.add(FN: LocField(FR)), PointeeR: 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(V: PointeeV)) {
204 if (NeedsCastBack)
205 return addFieldToUninits(LocalChain: LocalChain.add(FN: NeedsCastLocField(FR, DynT)), PointeeR: R);
206 return addFieldToUninits(LocalChain: LocalChain.add(FN: LocField(FR)), PointeeR: R);
207 }
208
209 IsAnyFieldInitialized = true;
210 return false;
211}
212
213//===----------------------------------------------------------------------===//
214// Utility functions.
215//===----------------------------------------------------------------------===//
216
217static std::optional<DereferenceInfo> dereference(ProgramStateRef State,
218 const FieldRegion *FR) {
219
220 llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
221
222 SVal V = State->getSVal(R: 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(T: FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(Val: 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(Ptr: R);
237
238 // We acquire the dynamic type of R,
239 QualType DynT = R->getLocationType();
240
241 while (const MemRegion *Tmp = State->getSVal(R, T: 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(Ptr: 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(T: DynT->getPointeeType()))
255 break;
256 }
257
258 while (isa<CXXBaseObjectRegion>(Val: R)) {
259 NeedsCastBack = true;
260 const auto *SuperR = dyn_cast<TypedValueRegion>(Val: R->getSuperRegion());
261 if (!SuperR)
262 break;
263
264 R = SuperR;
265 }
266
267 return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
268}
269
270static 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