/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 76 | : FieldNode(FR), IsDereferenced(IsDereferenced) {} |
37 | | |
38 | 28 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
39 | 28 | if (IsDereferenced) |
40 | 18 | Out << "uninitialized pointee "; |
41 | 10 | else |
42 | 10 | Out << "uninitialized pointer "; |
43 | 28 | } |
44 | | |
45 | 62 | void printPrefix(llvm::raw_ostream &Out) const override {} |
46 | | |
47 | 62 | void printNode(llvm::raw_ostream &Out) const override { |
48 | 62 | Out << getVariableName(getDecl()); |
49 | 62 | } |
50 | | |
51 | 34 | void printSeparator(llvm::raw_ostream &Out) const override { |
52 | 34 | if (getDecl()->getType()->isPointerType()) |
53 | 14 | Out << "->"; |
54 | 20 | else |
55 | 20 | Out << '.'; |
56 | 34 | } |
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 | 15 | : FieldNode(FR), CastBackType(T) {} |
67 | | |
68 | 6 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
69 | 6 | Out << "uninitialized pointee "; |
70 | 6 | } |
71 | | |
72 | 21 | void printPrefix(llvm::raw_ostream &Out) const override { |
73 | | // If this object is a nonloc::LocAsInteger. |
74 | 21 | if (getDecl()->getType()->isIntegerType()) |
75 | 2 | Out << "reinterpret_cast"; |
76 | | // If this pointer's dynamic type is different then it's static type. |
77 | 19 | else |
78 | 19 | Out << "static_cast"; |
79 | 21 | Out << '<' << CastBackType.getAsString() << ">("; |
80 | 21 | } |
81 | | |
82 | 21 | void printNode(llvm::raw_ostream &Out) const override { |
83 | 21 | Out << getVariableName(getDecl()) << ')'; |
84 | 21 | } |
85 | | |
86 | 15 | 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 | 6 | CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {} |
94 | | |
95 | 6 | void printNoteMsg(llvm::raw_ostream &Out) const override { |
96 | 6 | Out << "object references itself "; |
97 | 6 | } |
98 | | |
99 | 6 | void printPrefix(llvm::raw_ostream &Out) const override {} |
100 | | |
101 | 6 | void printNode(llvm::raw_ostream &Out) const override { |
102 | 6 | Out << getVariableName(getDecl()); |
103 | 6 | } |
104 | | |
105 | 0 | void printSeparator(llvm::raw_ostream &Out) const override { |
106 | 0 | llvm_unreachable("CyclicLocField objects must be the last node of the " |
107 | 0 | "fieldchain!"); |
108 | 0 | } |
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 | 113 | : 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 | 163 | const FieldRegion *FR, FieldChainInfo LocalChain) { |
139 | | |
140 | 163 | SVal V = State->getSVal(FR); |
141 | | |
142 | 163 | assert((isDereferencableType(FR->getDecl()->getType()) || |
143 | 163 | isa<nonloc::LocAsInteger>(V)) && |
144 | 163 | "This method only checks dereferenceable objects!"); |
145 | | |
146 | 163 | if (V.isUnknown() || isa<loc::ConcreteInt>(V)159 ) { |
147 | 17 | IsAnyFieldInitialized = true; |
148 | 17 | return false; |
149 | 17 | } |
150 | | |
151 | 146 | if (V.isUndef()) { |
152 | 10 | return addFieldToUninits( |
153 | 10 | LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR); |
154 | 10 | } |
155 | | |
156 | 136 | if (!Opts.CheckPointeeInitialization) { |
157 | 1 | IsAnyFieldInitialized = true; |
158 | 1 | return false; |
159 | 1 | } |
160 | | |
161 | | // At this point the pointer itself is initialized and points to a valid |
162 | | // location, we'll now check the pointee. |
163 | 135 | std::optional<DereferenceInfo> DerefInfo = dereference(State, FR); |
164 | 135 | if (!DerefInfo) { |
165 | 22 | IsAnyFieldInitialized = true; |
166 | 22 | return false; |
167 | 22 | } |
168 | | |
169 | 113 | if (DerefInfo->IsCyclic) |
170 | 6 | return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR); |
171 | | |
172 | 107 | const TypedValueRegion *R = DerefInfo->R; |
173 | 107 | const bool NeedsCastBack = DerefInfo->NeedsCastBack; |
174 | | |
175 | 107 | QualType DynT = R->getLocationType(); |
176 | 107 | QualType PointeeT = DynT->getPointeeType(); |
177 | | |
178 | 107 | if (PointeeT->isStructureOrClassType()) { |
179 | 53 | if (NeedsCastBack) |
180 | 9 | return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT))); |
181 | 44 | return isNonUnionUninit(R, LocalChain.add(LocField(FR))); |
182 | 53 | } |
183 | | |
184 | 54 | if (PointeeT->isUnionType()) { |
185 | 4 | if (isUnionUninit(R)) { |
186 | 0 | if (NeedsCastBack) |
187 | 0 | return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), |
188 | 0 | R); |
189 | 0 | return addFieldToUninits(LocalChain.add(LocField(FR)), R); |
190 | 4 | } else { |
191 | 4 | IsAnyFieldInitialized = true; |
192 | 4 | return false; |
193 | 4 | } |
194 | 4 | } |
195 | | |
196 | 50 | if (PointeeT->isArrayType()) { |
197 | 2 | IsAnyFieldInitialized = true; |
198 | 2 | return false; |
199 | 2 | } |
200 | | |
201 | 48 | assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) && |
202 | 48 | "At this point FR must either have a primitive dynamic type, or it " |
203 | 48 | "must be a null, undefined, unknown or concrete pointer!"); |
204 | | |
205 | 48 | SVal PointeeV = State->getSVal(R); |
206 | | |
207 | 48 | if (isPrimitiveUninit(PointeeV)) { |
208 | 28 | if (NeedsCastBack) |
209 | 6 | return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R); |
210 | 22 | return addFieldToUninits(LocalChain.add(LocField(FR)), R); |
211 | 28 | } |
212 | | |
213 | 20 | IsAnyFieldInitialized = true; |
214 | 20 | return false; |
215 | 48 | } |
216 | | |
217 | | //===----------------------------------------------------------------------===// |
218 | | // Utility functions. |
219 | | //===----------------------------------------------------------------------===// |
220 | | |
221 | | static std::optional<DereferenceInfo> dereference(ProgramStateRef State, |
222 | 135 | const FieldRegion *FR) { |
223 | | |
224 | 135 | llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions; |
225 | | |
226 | 135 | SVal V = State->getSVal(FR); |
227 | 135 | 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 | 135 | bool NeedsCastBack = |
233 | 135 | isVoidPointer(FR->getDecl()->getType()) || isa<nonloc::LocAsInteger>(V)111 ; |
234 | | |
235 | | // The region we'd like to acquire. |
236 | 135 | const auto *R = V.getAsRegion()->getAs<TypedValueRegion>(); |
237 | 135 | if (!R) |
238 | 22 | return std::nullopt; |
239 | | |
240 | 113 | VisitedRegions.insert(R); |
241 | | |
242 | | // We acquire the dynamic type of R, |
243 | 113 | QualType DynT = R->getLocationType(); |
244 | | |
245 | 117 | while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) { |
246 | | |
247 | 18 | R = Tmp->getAs<TypedValueRegion>(); |
248 | 18 | if (!R) |
249 | 0 | return std::nullopt; |
250 | | |
251 | | // We found a cyclic pointer, like int *ptr = (int *)&ptr. |
252 | 18 | if (!VisitedRegions.insert(R).second) |
253 | 6 | return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true}; |
254 | | |
255 | 12 | 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 | 12 | if (isDereferencableType(DynT->getPointeeType())) |
259 | 8 | break; |
260 | 12 | } |
261 | | |
262 | 109 | while (107 isa<CXXBaseObjectRegion>(R)) { |
263 | 3 | NeedsCastBack = true; |
264 | 3 | const auto *SuperR = dyn_cast<TypedValueRegion>(R->getSuperRegion()); |
265 | 3 | if (!SuperR) |
266 | 1 | break; |
267 | | |
268 | 2 | R = SuperR; |
269 | 2 | } |
270 | | |
271 | 107 | return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false}; |
272 | 113 | } |
273 | | |
274 | 135 | static bool isVoidPointer(QualType T) { |
275 | 375 | while (!T.isNull()) { |
276 | 264 | if (T->isVoidPointerType()) |
277 | 24 | return true; |
278 | 240 | T = T->getPointeeType(); |
279 | 240 | } |
280 | 111 | return false; |
281 | 135 | } |