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(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. |
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(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. |
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(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 | |
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(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 | |
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(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 | |
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 | |