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: |
35 | LocField(const FieldRegion *FR, const bool IsDereferenced = true) |
36 | : FieldNode(FR), IsDereferenced(IsDereferenced) {} |
37 | |
38 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
39 | if (IsDereferenced) |
40 | Out << "uninitialized pointee " ; |
41 | else |
42 | Out << "uninitialized pointer " ; |
43 | } |
44 | |
45 | void printPrefix(llvm::raw_ostream &Out) const override {} |
46 | |
47 | void printNode(llvm::raw_ostream &Out) const override { |
48 | Out << getVariableName(Field: getDecl()); |
49 | } |
50 | |
51 | 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: |
65 | NeedsCastLocField(const FieldRegion *FR, const QualType &T) |
66 | : FieldNode(FR), CastBackType(T) {} |
67 | |
68 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
69 | Out << "uninitialized pointee " ; |
70 | } |
71 | |
72 | 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 | |
82 | void printNode(llvm::raw_ostream &Out) const override { |
83 | Out << getVariableName(Field: getDecl()) << ')'; |
84 | } |
85 | |
86 | 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: |
93 | CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {} |
94 | |
95 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
96 | Out << "object references itself " ; |
97 | } |
98 | |
99 | void printPrefix(llvm::raw_ostream &Out) const override {} |
100 | |
101 | void printNode(llvm::raw_ostream &Out) const override { |
102 | Out << getVariableName(Field: getDecl()); |
103 | } |
104 | |
105 | 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; |
119 | 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 | |
137 | bool FindUninitializedFields::isDereferencableUninit( |
138 | const FieldRegion *FR, FieldChainInfo LocalChain) { |
139 | |
140 | SVal V = State->getSVal(R: 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>(Val: V)) { |
147 | IsAnyFieldInitialized = true; |
148 | return false; |
149 | } |
150 | |
151 | if (V.isUndef()) { |
152 | return addFieldToUninits( |
153 | LocalChain: LocalChain.add(FN: LocField(FR, /*IsDereferenced*/ false)), PointeeR: 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: LocalChain.add(FN: CyclicLocField(FR)), PointeeR: 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: LocalChain.add(FN: NeedsCastLocField(FR, DynT))); |
181 | return isNonUnionUninit(R, LocalChain: LocalChain.add(FN: LocField(FR))); |
182 | } |
183 | |
184 | if (PointeeT->isUnionType()) { |
185 | if (isUnionUninit(R)) { |
186 | if (NeedsCastBack) |
187 | return addFieldToUninits(LocalChain: LocalChain.add(FN: NeedsCastLocField(FR, DynT)), |
188 | PointeeR: R); |
189 | return addFieldToUninits(LocalChain: LocalChain.add(FN: LocField(FR)), PointeeR: 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(V: PointeeV)) { |
208 | if (NeedsCastBack) |
209 | return addFieldToUninits(LocalChain: LocalChain.add(FN: NeedsCastLocField(FR, DynT)), PointeeR: R); |
210 | return addFieldToUninits(LocalChain: LocalChain.add(FN: LocField(FR)), PointeeR: R); |
211 | } |
212 | |
213 | IsAnyFieldInitialized = true; |
214 | return false; |
215 | } |
216 | |
217 | //===----------------------------------------------------------------------===// |
218 | // Utility functions. |
219 | //===----------------------------------------------------------------------===// |
220 | |
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(R: 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(T: FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(Val: 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(Ptr: R); |
241 | |
242 | // We acquire the dynamic type of R, |
243 | QualType DynT = R->getLocationType(); |
244 | |
245 | while (const MemRegion *Tmp = State->getSVal(R, T: 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(Ptr: 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(T: DynT->getPointeeType())) |
259 | break; |
260 | } |
261 | |
262 | while (isa<CXXBaseObjectRegion>(Val: R)) { |
263 | NeedsCastBack = true; |
264 | const auto *SuperR = dyn_cast<TypedValueRegion>(Val: R->getSuperRegion()); |
265 | if (!SuperR) |
266 | break; |
267 | |
268 | R = SuperR; |
269 | } |
270 | |
271 | return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false}; |
272 | } |
273 | |
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 | |