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