/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- CastValueChecker - Model implementation of custom RTTIs --*- 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 defines CastValueChecker which models casts of custom RTTIs. |
10 | | // |
11 | | // TODO list: |
12 | | // - It only allows one succesful cast between two types however in the wild |
13 | | // the object could be casted to multiple types. |
14 | | // - It needs to check the most likely type information from the dynamic type |
15 | | // map to increase precision of dynamic casting. |
16 | | // |
17 | | //===----------------------------------------------------------------------===// |
18 | | |
19 | | #include "clang/AST/DeclTemplate.h" |
20 | | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
21 | | #include "clang/StaticAnalyzer/Core/Checker.h" |
22 | | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
23 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h" |
24 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
25 | | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
26 | | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
27 | | #include <optional> |
28 | | #include <utility> |
29 | | |
30 | | using namespace clang; |
31 | | using namespace ento; |
32 | | |
33 | | namespace { |
34 | | class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> { |
35 | | enum class CallKind { Function, Method, InstanceOf }; |
36 | | |
37 | | using CastCheck = |
38 | | std::function<void(const CastValueChecker *, const CallEvent &Call, |
39 | | DefinedOrUnknownSVal, CheckerContext &)>; |
40 | | |
41 | | public: |
42 | | // We have five cases to evaluate a cast: |
43 | | // 1) The parameter is non-null, the return value is non-null. |
44 | | // 2) The parameter is non-null, the return value is null. |
45 | | // 3) The parameter is null, the return value is null. |
46 | | // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. |
47 | | // |
48 | | // 4) castAs: Has no parameter, the return value is non-null. |
49 | | // 5) getAs: Has no parameter, the return value is null or non-null. |
50 | | // |
51 | | // We have two cases to check the parameter is an instance of the given type. |
52 | | // 1) isa: The parameter is non-null, returns boolean. |
53 | | // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean. |
54 | | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
55 | | void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; |
56 | | |
57 | | private: |
58 | | // These are known in the LLVM project. The pairs are in the following form: |
59 | | // {{{namespace, call}, argument-count}, {callback, kind}} |
60 | | const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = { |
61 | | {{{"llvm", "cast"}, 1}, |
62 | | {&CastValueChecker::evalCast, CallKind::Function}}, |
63 | | {{{"llvm", "dyn_cast"}, 1}, |
64 | | {&CastValueChecker::evalDynCast, CallKind::Function}}, |
65 | | {{{"llvm", "cast_or_null"}, 1}, |
66 | | {&CastValueChecker::evalCastOrNull, CallKind::Function}}, |
67 | | {{{"llvm", "dyn_cast_or_null"}, 1}, |
68 | | {&CastValueChecker::evalDynCastOrNull, CallKind::Function}}, |
69 | | {{{"clang", "castAs"}, 0}, |
70 | | {&CastValueChecker::evalCastAs, CallKind::Method}}, |
71 | | {{{"clang", "getAs"}, 0}, |
72 | | {&CastValueChecker::evalGetAs, CallKind::Method}}, |
73 | | {{{"llvm", "isa"}, 1}, |
74 | | {&CastValueChecker::evalIsa, CallKind::InstanceOf}}, |
75 | | {{{"llvm", "isa_and_nonnull"}, 1}, |
76 | | {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}}; |
77 | | |
78 | | void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, |
79 | | CheckerContext &C) const; |
80 | | void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV, |
81 | | CheckerContext &C) const; |
82 | | void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, |
83 | | CheckerContext &C) const; |
84 | | void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV, |
85 | | CheckerContext &C) const; |
86 | | void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV, |
87 | | CheckerContext &C) const; |
88 | | void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, |
89 | | CheckerContext &C) const; |
90 | | void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, |
91 | | CheckerContext &C) const; |
92 | | void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV, |
93 | | CheckerContext &C) const; |
94 | | }; |
95 | | } // namespace |
96 | | |
97 | | static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, |
98 | 372 | bool CastSucceeds) { |
99 | 372 | if (!CastInfo) |
100 | 336 | return false; |
101 | | |
102 | 36 | return CastSucceeds ? CastInfo->fails()18 : CastInfo->succeeds()18 ; |
103 | 372 | } |
104 | | |
105 | | static const NoteTag *getNoteTag(CheckerContext &C, |
106 | | const DynamicCastInfo *CastInfo, |
107 | | QualType CastToTy, const Expr *Object, |
108 | 398 | bool CastSucceeds, bool IsKnownCast) { |
109 | 398 | std::string CastToName = |
110 | 398 | CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()36 |
111 | 398 | : CastToTy.getAsString()362 ; |
112 | 398 | Object = Object->IgnoreParenImpCasts(); |
113 | | |
114 | 398 | return C.getNoteTag( |
115 | 398 | [=]() -> std::string { |
116 | 184 | SmallString<128> Msg; |
117 | 184 | llvm::raw_svector_ostream Out(Msg); |
118 | | |
119 | 184 | if (!IsKnownCast) |
120 | 135 | Out << "Assuming "; |
121 | | |
122 | 184 | if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { |
123 | 184 | Out << '\'' << DRE->getDecl()->getDeclName() << '\''; |
124 | 184 | } else if (const auto *0 ME0 = dyn_cast<MemberExpr>(Object)) { |
125 | 0 | Out << (IsKnownCast ? "Field '" : "field '") |
126 | 0 | << ME->getMemberDecl()->getDeclName() << '\''; |
127 | 0 | } else { |
128 | 0 | Out << (IsKnownCast ? "The object" : "the object"); |
129 | 0 | } |
130 | | |
131 | 184 | Out << ' ' << (CastSucceeds ? "is a"95 : "is not a"89 ) << " '" << CastToName |
132 | 184 | << '\''; |
133 | | |
134 | 184 | return std::string(Out.str()); |
135 | 184 | }, |
136 | 398 | /*IsPrunable=*/true); |
137 | 398 | } |
138 | | |
139 | | static const NoteTag *getNoteTag(CheckerContext &C, |
140 | | SmallVector<QualType, 4> CastToTyVec, |
141 | | const Expr *Object, |
142 | 126 | bool IsKnownCast) { |
143 | 126 | Object = Object->IgnoreParenImpCasts(); |
144 | | |
145 | 126 | return C.getNoteTag( |
146 | 126 | [=]() -> std::string { |
147 | 58 | SmallString<128> Msg; |
148 | 58 | llvm::raw_svector_ostream Out(Msg); |
149 | | |
150 | 58 | if (!IsKnownCast) |
151 | 4 | Out << "Assuming "; |
152 | | |
153 | 58 | if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) { |
154 | 58 | Out << '\'' << DRE->getDecl()->getNameAsString() << '\''; |
155 | 58 | } else if (const auto *0 ME0 = dyn_cast<MemberExpr>(Object)) { |
156 | 0 | Out << (IsKnownCast ? "Field '" : "field '") |
157 | 0 | << ME->getMemberDecl()->getNameAsString() << '\''; |
158 | 0 | } else { |
159 | 0 | Out << (IsKnownCast ? "The object" : "the object"); |
160 | 0 | } |
161 | 58 | Out << " is"; |
162 | | |
163 | 58 | bool First = true; |
164 | 116 | for (QualType CastToTy: CastToTyVec) { |
165 | 116 | std::string CastToName = |
166 | 116 | CastToTy->getAsCXXRecordDecl() |
167 | 116 | ? CastToTy->getAsCXXRecordDecl()->getNameAsString() |
168 | 116 | : CastToTy.getAsString()0 ; |
169 | 116 | Out << ' ' << ((CastToTyVec.size() == 1) ? "not"20 : |
170 | 116 | (96 First96 ? "neither"38 : "nor"58 )) << " a '" << CastToName |
171 | 116 | << '\''; |
172 | 116 | First = false; |
173 | 116 | } |
174 | | |
175 | 58 | return std::string(Out.str()); |
176 | 58 | }, |
177 | 126 | /*IsPrunable=*/true); |
178 | 126 | } |
179 | | |
180 | | //===----------------------------------------------------------------------===// |
181 | | // Main logic to evaluate a cast. |
182 | | //===----------------------------------------------------------------------===// |
183 | | |
184 | | static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards, |
185 | 304 | ASTContext &ACtx) { |
186 | 304 | if (alignTowards->isLValueReferenceType() && |
187 | 304 | alignTowards.isConstQualified()) { |
188 | 0 | toAlign.addConst(); |
189 | 0 | return ACtx.getLValueReferenceType(toAlign); |
190 | 304 | } else if (alignTowards->isLValueReferenceType()) |
191 | 304 | return ACtx.getLValueReferenceType(toAlign); |
192 | 0 | else if (alignTowards->isRValueReferenceType()) |
193 | 0 | return ACtx.getRValueReferenceType(toAlign); |
194 | | |
195 | 0 | llvm_unreachable("Must align towards a reference type!"); |
196 | 0 | } |
197 | | |
198 | | static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, |
199 | | CheckerContext &C, bool IsNonNullParam, |
200 | | bool IsNonNullReturn, |
201 | 401 | bool IsCheckedCast = false) { |
202 | 401 | ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam); |
203 | 401 | if (!State) |
204 | 20 | return; |
205 | | |
206 | 381 | const Expr *Object; |
207 | 381 | QualType CastFromTy; |
208 | 381 | QualType CastToTy = Call.getResultType(); |
209 | | |
210 | 381 | if (Call.getNumArgs() > 0) { |
211 | 342 | Object = Call.getArgExpr(0); |
212 | 342 | CastFromTy = Call.parameters()[0]->getType(); |
213 | 342 | } else { |
214 | 39 | Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr(); |
215 | 39 | CastFromTy = Object->getType(); |
216 | 39 | if (CastToTy->isPointerType()) { |
217 | 39 | if (!CastFromTy->isPointerType()) |
218 | 9 | return; |
219 | 39 | } else { |
220 | 0 | if (!CastFromTy->isReferenceType()) |
221 | 0 | return; |
222 | | |
223 | 0 | CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext()); |
224 | 0 | } |
225 | 39 | } |
226 | | |
227 | 372 | const MemRegion *MR = DV.getAsRegion(); |
228 | 372 | const DynamicCastInfo *CastInfo = |
229 | 372 | getDynamicCastInfo(State, MR, CastFromTy, CastToTy); |
230 | | |
231 | | // We assume that every checked cast succeeds. |
232 | 372 | bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy351 ; |
233 | 372 | if (!CastSucceeds) { |
234 | 351 | if (CastInfo) |
235 | 36 | CastSucceeds = IsNonNullReturn && CastInfo->succeeds()18 ; |
236 | 315 | else |
237 | 315 | CastSucceeds = IsNonNullReturn; |
238 | 351 | } |
239 | | |
240 | | // Check for infeasible casts. |
241 | 372 | if (isInfeasibleCast(CastInfo, CastSucceeds)) { |
242 | 18 | C.generateSink(State, C.getPredecessor()); |
243 | 18 | return; |
244 | 18 | } |
245 | | |
246 | | // Store the type and the cast information. |
247 | 354 | bool IsKnownCast = CastInfo || IsCheckedCast336 || CastFromTy == CastToTy315 ; |
248 | 354 | if (!IsKnownCast || IsCheckedCast39 ) |
249 | 336 | State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, |
250 | 336 | CastSucceeds); |
251 | | |
252 | 354 | SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)197 |
253 | 354 | : C.getSValBuilder().makeNullWithType(CastToTy)157 ; |
254 | 354 | C.addTransition( |
255 | 354 | State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false), |
256 | 354 | getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); |
257 | 354 | } |
258 | | |
259 | | static void addInstanceOfTransition(const CallEvent &Call, |
260 | | DefinedOrUnknownSVal DV, |
261 | | ProgramStateRef State, CheckerContext &C, |
262 | 180 | bool IsInstanceOf) { |
263 | 180 | const FunctionDecl *FD = Call.getDecl()->getAsFunction(); |
264 | 180 | QualType CastFromTy = Call.parameters()[0]->getType(); |
265 | 180 | SmallVector<QualType, 4> CastToTyVec; |
266 | 584 | for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1; |
267 | 404 | ++idx) { |
268 | 404 | TemplateArgument CastToTempArg = |
269 | 404 | FD->getTemplateSpecializationArgs()->get(idx); |
270 | 404 | switch (CastToTempArg.getKind()) { |
271 | 0 | default: |
272 | 0 | return; |
273 | 284 | case TemplateArgument::Type: |
274 | 284 | CastToTyVec.push_back(CastToTempArg.getAsType()); |
275 | 284 | break; |
276 | 120 | case TemplateArgument::Pack: |
277 | 120 | for (TemplateArgument ArgInPack: CastToTempArg.pack_elements()) |
278 | 92 | CastToTyVec.push_back(ArgInPack.getAsType()); |
279 | 120 | break; |
280 | 404 | } |
281 | 404 | } |
282 | | |
283 | 180 | const MemRegion *MR = DV.getAsRegion(); |
284 | 180 | if (MR && CastFromTy->isReferenceType()) |
285 | 180 | MR = State->getSVal(DV.castAs<Loc>()).getAsRegion(); |
286 | | |
287 | 180 | bool Success = false; |
288 | 180 | bool IsAnyKnown = false; |
289 | 304 | for (QualType CastToTy: CastToTyVec) { |
290 | 304 | if (CastFromTy->isPointerType()) |
291 | 0 | CastToTy = C.getASTContext().getPointerType(CastToTy); |
292 | 304 | else if (CastFromTy->isReferenceType()) |
293 | 304 | CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext()); |
294 | 0 | else |
295 | 0 | return; |
296 | | |
297 | 304 | const DynamicCastInfo *CastInfo = |
298 | 304 | getDynamicCastInfo(State, MR, CastFromTy, CastToTy); |
299 | | |
300 | 304 | bool CastSucceeds; |
301 | 304 | if (CastInfo) |
302 | 252 | CastSucceeds = IsInstanceOf && CastInfo->succeeds()126 ; |
303 | 52 | else |
304 | 52 | CastSucceeds = IsInstanceOf || CastFromTy == CastToTy26 ; |
305 | | |
306 | | // Store the type and the cast information. |
307 | 304 | bool IsKnownCast = CastInfo || CastFromTy == CastToTy52 ; |
308 | 304 | IsAnyKnown = IsAnyKnown || IsKnownCast196 ; |
309 | 304 | ProgramStateRef NewState = State; |
310 | 304 | if (!IsKnownCast) |
311 | 52 | NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, |
312 | 52 | IsInstanceOf); |
313 | | |
314 | 304 | if (CastSucceeds) { |
315 | 44 | Success = true; |
316 | 44 | C.addTransition( |
317 | 44 | NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(), |
318 | 44 | C.getSValBuilder().makeTruthVal(true)), |
319 | 44 | getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true, |
320 | 44 | IsKnownCast)); |
321 | 44 | if (IsKnownCast) |
322 | 18 | return; |
323 | 260 | } else if (CastInfo && CastInfo->succeeds()234 ) { |
324 | 18 | C.generateSink(NewState, C.getPredecessor()); |
325 | 18 | return; |
326 | 18 | } |
327 | 304 | } |
328 | | |
329 | 144 | if (!Success) { |
330 | 126 | C.addTransition( |
331 | 126 | State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), |
332 | 126 | C.getSValBuilder().makeTruthVal(false)), |
333 | 126 | getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown)); |
334 | 126 | } |
335 | 144 | } |
336 | | |
337 | | //===----------------------------------------------------------------------===// |
338 | | // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. |
339 | | //===----------------------------------------------------------------------===// |
340 | | |
341 | | static void evalNonNullParamNonNullReturn(const CallEvent &Call, |
342 | | DefinedOrUnknownSVal DV, |
343 | | CheckerContext &C, |
344 | 187 | bool IsCheckedCast = false) { |
345 | 187 | addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, |
346 | 187 | /*IsNonNullReturn=*/true, IsCheckedCast); |
347 | 187 | } |
348 | | |
349 | | static void evalNonNullParamNullReturn(const CallEvent &Call, |
350 | | DefinedOrUnknownSVal DV, |
351 | 175 | CheckerContext &C) { |
352 | 175 | addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, |
353 | 175 | /*IsNonNullReturn=*/false); |
354 | 175 | } |
355 | | |
356 | | static void evalNullParamNullReturn(const CallEvent &Call, |
357 | | DefinedOrUnknownSVal DV, |
358 | 159 | CheckerContext &C) { |
359 | 159 | if (ProgramStateRef State = C.getState()->assume(DV, false)) |
360 | 40 | C.addTransition(State->BindExpr(Call.getOriginExpr(), |
361 | 40 | C.getLocationContext(), |
362 | 40 | C.getSValBuilder().makeNullWithType( |
363 | 40 | Call.getOriginExpr()->getType()), |
364 | 40 | false), |
365 | 40 | C.getNoteTag("Assuming null pointer is passed into cast", |
366 | 40 | /*IsPrunable=*/true)); |
367 | 159 | } |
368 | | |
369 | | void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, |
370 | 11 | CheckerContext &C) const { |
371 | 11 | evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); |
372 | 11 | } |
373 | | |
374 | | void CastValueChecker::evalDynCast(const CallEvent &Call, |
375 | | DefinedOrUnknownSVal DV, |
376 | 17 | CheckerContext &C) const { |
377 | 17 | evalNonNullParamNonNullReturn(Call, DV, C); |
378 | 17 | evalNonNullParamNullReturn(Call, DV, C); |
379 | 17 | } |
380 | | |
381 | | void CastValueChecker::evalCastOrNull(const CallEvent &Call, |
382 | | DefinedOrUnknownSVal DV, |
383 | 1 | CheckerContext &C) const { |
384 | 1 | evalNonNullParamNonNullReturn(Call, DV, C); |
385 | 1 | evalNullParamNullReturn(Call, DV, C); |
386 | 1 | } |
387 | | |
388 | | void CastValueChecker::evalDynCastOrNull(const CallEvent &Call, |
389 | | DefinedOrUnknownSVal DV, |
390 | 158 | CheckerContext &C) const { |
391 | 158 | evalNonNullParamNonNullReturn(Call, DV, C); |
392 | 158 | evalNonNullParamNullReturn(Call, DV, C); |
393 | 158 | evalNullParamNullReturn(Call, DV, C); |
394 | 158 | } |
395 | | |
396 | | //===----------------------------------------------------------------------===// |
397 | | // Evaluating castAs, getAs. |
398 | | //===----------------------------------------------------------------------===// |
399 | | |
400 | | static void evalZeroParamNonNullReturn(const CallEvent &Call, |
401 | | DefinedOrUnknownSVal DV, |
402 | | CheckerContext &C, |
403 | 29 | bool IsCheckedCast = false) { |
404 | 29 | addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, |
405 | 29 | /*IsNonNullReturn=*/true, IsCheckedCast); |
406 | 29 | } |
407 | | |
408 | | static void evalZeroParamNullReturn(const CallEvent &Call, |
409 | | DefinedOrUnknownSVal DV, |
410 | 10 | CheckerContext &C) { |
411 | 10 | addCastTransition(Call, DV, C, /*IsNonNullParam=*/true, |
412 | 10 | /*IsNonNullReturn=*/false); |
413 | 10 | } |
414 | | |
415 | | void CastValueChecker::evalCastAs(const CallEvent &Call, |
416 | | DefinedOrUnknownSVal DV, |
417 | 19 | CheckerContext &C) const { |
418 | 19 | evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true); |
419 | 19 | } |
420 | | |
421 | | void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, |
422 | 10 | CheckerContext &C) const { |
423 | 10 | evalZeroParamNonNullReturn(Call, DV, C); |
424 | 10 | evalZeroParamNullReturn(Call, DV, C); |
425 | 10 | } |
426 | | |
427 | | //===----------------------------------------------------------------------===// |
428 | | // Evaluating isa, isa_and_nonnull. |
429 | | //===----------------------------------------------------------------------===// |
430 | | |
431 | | void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, |
432 | 86 | CheckerContext &C) const { |
433 | 86 | ProgramStateRef NonNullState, NullState; |
434 | 86 | std::tie(NonNullState, NullState) = C.getState()->assume(DV); |
435 | | |
436 | 86 | if (NonNullState) { |
437 | 86 | addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); |
438 | 86 | addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); |
439 | 86 | } |
440 | | |
441 | 86 | if (NullState) { |
442 | 0 | C.generateSink(NullState, C.getPredecessor()); |
443 | 0 | } |
444 | 86 | } |
445 | | |
446 | | void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call, |
447 | | DefinedOrUnknownSVal DV, |
448 | 4 | CheckerContext &C) const { |
449 | 4 | ProgramStateRef NonNullState, NullState; |
450 | 4 | std::tie(NonNullState, NullState) = C.getState()->assume(DV); |
451 | | |
452 | 4 | if (NonNullState) { |
453 | 4 | addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); |
454 | 4 | addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); |
455 | 4 | } |
456 | | |
457 | 4 | if (NullState) { |
458 | 0 | addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false); |
459 | 0 | } |
460 | 4 | } |
461 | | |
462 | | //===----------------------------------------------------------------------===// |
463 | | // Main logic to evaluate a call. |
464 | | //===----------------------------------------------------------------------===// |
465 | | |
466 | | bool CastValueChecker::evalCall(const CallEvent &Call, |
467 | 586 | CheckerContext &C) const { |
468 | 586 | const auto *Lookup = CDM.lookup(Call); |
469 | 586 | if (!Lookup) |
470 | 269 | return false; |
471 | | |
472 | 317 | const CastCheck &Check = Lookup->first; |
473 | 317 | CallKind Kind = Lookup->second; |
474 | | |
475 | 317 | std::optional<DefinedOrUnknownSVal> DV; |
476 | | |
477 | 317 | switch (Kind) { |
478 | 198 | case CallKind::Function: { |
479 | | // We only model casts from pointers to pointers or from references |
480 | | // to references. Other casts are most likely specialized and we |
481 | | // cannot model them. |
482 | 198 | QualType ParamT = Call.parameters()[0]->getType(); |
483 | 198 | QualType ResultT = Call.getResultType(); |
484 | 198 | if (!(ParamT->isPointerType() && ResultT->isPointerType()173 ) && |
485 | 198 | !(25 ParamT->isReferenceType()25 && ResultT->isReferenceType()24 )) { |
486 | 11 | return false; |
487 | 11 | } |
488 | | |
489 | 187 | DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); |
490 | 187 | break; |
491 | 198 | } |
492 | 90 | case CallKind::InstanceOf: { |
493 | | // We need to obtain the only template argument to determinte the type. |
494 | 90 | const FunctionDecl *FD = Call.getDecl()->getAsFunction(); |
495 | 90 | if (!FD || !FD->getTemplateSpecializationArgs()) |
496 | 0 | return false; |
497 | | |
498 | 90 | DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); |
499 | 90 | break; |
500 | 90 | } |
501 | 29 | case CallKind::Method: |
502 | 29 | const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); |
503 | 29 | if (!InstanceCall) |
504 | 0 | return false; |
505 | | |
506 | 29 | DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>(); |
507 | 29 | break; |
508 | 317 | } |
509 | | |
510 | 306 | if (!DV) |
511 | 0 | return false; |
512 | | |
513 | 306 | Check(this, Call, *DV, C); |
514 | 306 | return true; |
515 | 306 | } |
516 | | |
517 | | void CastValueChecker::checkDeadSymbols(SymbolReaper &SR, |
518 | 4.26k | CheckerContext &C) const { |
519 | 4.26k | C.addTransition(removeDeadCasts(C.getState(), SR)); |
520 | 4.26k | } |
521 | | |
522 | 52 | void ento::registerCastValueChecker(CheckerManager &Mgr) { |
523 | 52 | Mgr.registerChecker<CastValueChecker>(); |
524 | 52 | } |
525 | | |
526 | 108 | bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) { |
527 | 108 | return true; |
528 | 108 | } |