1 //===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This defines CastValueChecker which models casts of custom RTTIs.
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.
17 //===----------------------------------------------------------------------===//
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"
30 using namespace clang
;
34 class CastValueChecker
: public Checker
<check::DeadSymbols
, eval::Call
> {
35 enum class CallKind
{ Function
, Method
, InstanceOf
};
38 std::function
<void(const CastValueChecker
*, const CallEvent
&Call
,
39 DefinedOrUnknownSVal
, CheckerContext
&)>;
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.
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.
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;
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
}}};
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;
97 static bool isInfeasibleCast(const DynamicCastInfo
*CastInfo
,
102 return CastSucceeds
? CastInfo
->fails() : CastInfo
->succeeds();
105 static const NoteTag
*getNoteTag(CheckerContext
&C
,
106 const DynamicCastInfo
*CastInfo
,
107 QualType CastToTy
, const Expr
*Object
,
108 bool CastSucceeds
, bool IsKnownCast
) {
109 std::string CastToName
=
110 CastInfo
? CastInfo
->to()->getAsCXXRecordDecl()->getNameAsString()
111 : CastToTy
.getAsString();
112 Object
= Object
->IgnoreParenImpCasts();
115 [=]() -> std::string
{
116 SmallString
<128> Msg
;
117 llvm::raw_svector_ostream
Out(Msg
);
122 if (const auto *DRE
= dyn_cast
<DeclRefExpr
>(Object
)) {
123 Out
<< '\'' << DRE
->getDecl()->getDeclName() << '\'';
124 } else if (const auto *ME
= dyn_cast
<MemberExpr
>(Object
)) {
125 Out
<< (IsKnownCast
? "Field '" : "field '")
126 << ME
->getMemberDecl()->getDeclName() << '\'';
128 Out
<< (IsKnownCast
? "The object" : "the object");
131 Out
<< ' ' << (CastSucceeds
? "is a" : "is not a") << " '" << CastToName
134 return std::string(Out
.str());
136 /*IsPrunable=*/true);
139 static const NoteTag
*getNoteTag(CheckerContext
&C
,
140 SmallVector
<QualType
, 4> CastToTyVec
,
143 Object
= Object
->IgnoreParenImpCasts();
146 [=]() -> std::string
{
147 SmallString
<128> Msg
;
148 llvm::raw_svector_ostream
Out(Msg
);
153 if (const auto *DRE
= dyn_cast
<DeclRefExpr
>(Object
)) {
154 Out
<< '\'' << DRE
->getDecl()->getNameAsString() << '\'';
155 } else if (const auto *ME
= dyn_cast
<MemberExpr
>(Object
)) {
156 Out
<< (IsKnownCast
? "Field '" : "field '")
157 << ME
->getMemberDecl()->getNameAsString() << '\'';
159 Out
<< (IsKnownCast
? "The object" : "the object");
164 for (QualType CastToTy
: CastToTyVec
) {
165 std::string CastToName
=
166 CastToTy
->getAsCXXRecordDecl()
167 ? CastToTy
->getAsCXXRecordDecl()->getNameAsString()
168 : CastToTy
.getAsString();
169 Out
<< ' ' << ((CastToTyVec
.size() == 1) ? "not" :
170 (First
? "neither" : "nor")) << " a '" << CastToName
175 return std::string(Out
.str());
177 /*IsPrunable=*/true);
180 //===----------------------------------------------------------------------===//
181 // Main logic to evaluate a cast.
182 //===----------------------------------------------------------------------===//
184 static QualType
alignReferenceTypes(QualType toAlign
, QualType alignTowards
,
186 if (alignTowards
->isLValueReferenceType() &&
187 alignTowards
.isConstQualified()) {
189 return ACtx
.getLValueReferenceType(toAlign
);
190 } else if (alignTowards
->isLValueReferenceType())
191 return ACtx
.getLValueReferenceType(toAlign
);
192 else if (alignTowards
->isRValueReferenceType())
193 return ACtx
.getRValueReferenceType(toAlign
);
195 llvm_unreachable("Must align towards a reference type!");
198 static void addCastTransition(const CallEvent
&Call
, DefinedOrUnknownSVal DV
,
199 CheckerContext
&C
, bool IsNonNullParam
,
200 bool IsNonNullReturn
,
201 bool IsCheckedCast
= false) {
202 ProgramStateRef State
= C
.getState()->assume(DV
, IsNonNullParam
);
208 QualType CastToTy
= Call
.getResultType();
210 if (Call
.getNumArgs() > 0) {
211 Object
= Call
.getArgExpr(0);
212 CastFromTy
= Call
.parameters()[0]->getType();
214 Object
= cast
<CXXInstanceCall
>(&Call
)->getCXXThisExpr();
215 CastFromTy
= Object
->getType();
216 if (CastToTy
->isPointerType()) {
217 if (!CastFromTy
->isPointerType())
220 if (!CastFromTy
->isReferenceType())
223 CastFromTy
= alignReferenceTypes(CastFromTy
, CastToTy
, C
.getASTContext());
227 const MemRegion
*MR
= DV
.getAsRegion();
228 const DynamicCastInfo
*CastInfo
=
229 getDynamicCastInfo(State
, MR
, CastFromTy
, CastToTy
);
231 // We assume that every checked cast succeeds.
232 bool CastSucceeds
= IsCheckedCast
|| CastFromTy
== CastToTy
;
235 CastSucceeds
= IsNonNullReturn
&& CastInfo
->succeeds();
237 CastSucceeds
= IsNonNullReturn
;
240 // Check for infeasible casts.
241 if (isInfeasibleCast(CastInfo
, CastSucceeds
)) {
242 C
.generateSink(State
, C
.getPredecessor());
246 // Store the type and the cast information.
247 bool IsKnownCast
= CastInfo
|| IsCheckedCast
|| CastFromTy
== CastToTy
;
248 if (!IsKnownCast
|| IsCheckedCast
)
249 State
= setDynamicTypeAndCastInfo(State
, MR
, CastFromTy
, CastToTy
,
252 SVal V
= CastSucceeds
? C
.getSValBuilder().evalCast(DV
, CastToTy
, CastFromTy
)
253 : C
.getSValBuilder().makeNullWithType(CastToTy
);
255 State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(), V
, false),
256 getNoteTag(C
, CastInfo
, CastToTy
, Object
, CastSucceeds
, IsKnownCast
));
259 static void addInstanceOfTransition(const CallEvent
&Call
,
260 DefinedOrUnknownSVal DV
,
261 ProgramStateRef State
, CheckerContext
&C
,
263 const FunctionDecl
*FD
= Call
.getDecl()->getAsFunction();
264 QualType CastFromTy
= Call
.parameters()[0]->getType();
265 SmallVector
<QualType
, 4> CastToTyVec
;
266 for (unsigned idx
= 0; idx
< FD
->getTemplateSpecializationArgs()->size() - 1;
268 TemplateArgument CastToTempArg
=
269 FD
->getTemplateSpecializationArgs()->get(idx
);
270 switch (CastToTempArg
.getKind()) {
273 case TemplateArgument::Type
:
274 CastToTyVec
.push_back(CastToTempArg
.getAsType());
276 case TemplateArgument::Pack
:
277 for (TemplateArgument ArgInPack
: CastToTempArg
.pack_elements())
278 CastToTyVec
.push_back(ArgInPack
.getAsType());
283 const MemRegion
*MR
= DV
.getAsRegion();
284 if (MR
&& CastFromTy
->isReferenceType())
285 MR
= State
->getSVal(DV
.castAs
<Loc
>()).getAsRegion();
287 bool Success
= false;
288 bool IsAnyKnown
= false;
289 for (QualType CastToTy
: CastToTyVec
) {
290 if (CastFromTy
->isPointerType())
291 CastToTy
= C
.getASTContext().getPointerType(CastToTy
);
292 else if (CastFromTy
->isReferenceType())
293 CastToTy
= alignReferenceTypes(CastToTy
, CastFromTy
, C
.getASTContext());
297 const DynamicCastInfo
*CastInfo
=
298 getDynamicCastInfo(State
, MR
, CastFromTy
, CastToTy
);
302 CastSucceeds
= IsInstanceOf
&& CastInfo
->succeeds();
304 CastSucceeds
= IsInstanceOf
|| CastFromTy
== CastToTy
;
306 // Store the type and the cast information.
307 bool IsKnownCast
= CastInfo
|| CastFromTy
== CastToTy
;
308 IsAnyKnown
= IsAnyKnown
|| IsKnownCast
;
309 ProgramStateRef NewState
= State
;
311 NewState
= setDynamicTypeAndCastInfo(State
, MR
, CastFromTy
, CastToTy
,
317 NewState
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(),
318 C
.getSValBuilder().makeTruthVal(true)),
319 getNoteTag(C
, CastInfo
, CastToTy
, Call
.getArgExpr(0), true,
323 } else if (CastInfo
&& CastInfo
->succeeds()) {
324 C
.generateSink(NewState
, C
.getPredecessor());
331 State
->BindExpr(Call
.getOriginExpr(), C
.getLocationContext(),
332 C
.getSValBuilder().makeTruthVal(false)),
333 getNoteTag(C
, CastToTyVec
, Call
.getArgExpr(0), IsAnyKnown
));
337 //===----------------------------------------------------------------------===//
338 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
339 //===----------------------------------------------------------------------===//
341 static void evalNonNullParamNonNullReturn(const CallEvent
&Call
,
342 DefinedOrUnknownSVal DV
,
344 bool IsCheckedCast
= false) {
345 addCastTransition(Call
, DV
, C
, /*IsNonNullParam=*/true,
346 /*IsNonNullReturn=*/true, IsCheckedCast
);
349 static void evalNonNullParamNullReturn(const CallEvent
&Call
,
350 DefinedOrUnknownSVal DV
,
352 addCastTransition(Call
, DV
, C
, /*IsNonNullParam=*/true,
353 /*IsNonNullReturn=*/false);
356 static void evalNullParamNullReturn(const CallEvent
&Call
,
357 DefinedOrUnknownSVal DV
,
359 if (ProgramStateRef State
= C
.getState()->assume(DV
, false))
360 C
.addTransition(State
->BindExpr(Call
.getOriginExpr(),
361 C
.getLocationContext(),
362 C
.getSValBuilder().makeNullWithType(
363 Call
.getOriginExpr()->getType()),
365 C
.getNoteTag("Assuming null pointer is passed into cast",
366 /*IsPrunable=*/true));
369 void CastValueChecker::evalCast(const CallEvent
&Call
, DefinedOrUnknownSVal DV
,
370 CheckerContext
&C
) const {
371 evalNonNullParamNonNullReturn(Call
, DV
, C
, /*IsCheckedCast=*/true);
374 void CastValueChecker::evalDynCast(const CallEvent
&Call
,
375 DefinedOrUnknownSVal DV
,
376 CheckerContext
&C
) const {
377 evalNonNullParamNonNullReturn(Call
, DV
, C
);
378 evalNonNullParamNullReturn(Call
, DV
, C
);
381 void CastValueChecker::evalCastOrNull(const CallEvent
&Call
,
382 DefinedOrUnknownSVal DV
,
383 CheckerContext
&C
) const {
384 evalNonNullParamNonNullReturn(Call
, DV
, C
);
385 evalNullParamNullReturn(Call
, DV
, C
);
388 void CastValueChecker::evalDynCastOrNull(const CallEvent
&Call
,
389 DefinedOrUnknownSVal DV
,
390 CheckerContext
&C
) const {
391 evalNonNullParamNonNullReturn(Call
, DV
, C
);
392 evalNonNullParamNullReturn(Call
, DV
, C
);
393 evalNullParamNullReturn(Call
, DV
, C
);
396 //===----------------------------------------------------------------------===//
397 // Evaluating castAs, getAs.
398 //===----------------------------------------------------------------------===//
400 static void evalZeroParamNonNullReturn(const CallEvent
&Call
,
401 DefinedOrUnknownSVal DV
,
403 bool IsCheckedCast
= false) {
404 addCastTransition(Call
, DV
, C
, /*IsNonNullParam=*/true,
405 /*IsNonNullReturn=*/true, IsCheckedCast
);
408 static void evalZeroParamNullReturn(const CallEvent
&Call
,
409 DefinedOrUnknownSVal DV
,
411 addCastTransition(Call
, DV
, C
, /*IsNonNullParam=*/true,
412 /*IsNonNullReturn=*/false);
415 void CastValueChecker::evalCastAs(const CallEvent
&Call
,
416 DefinedOrUnknownSVal DV
,
417 CheckerContext
&C
) const {
418 evalZeroParamNonNullReturn(Call
, DV
, C
, /*IsCheckedCast=*/true);
421 void CastValueChecker::evalGetAs(const CallEvent
&Call
, DefinedOrUnknownSVal DV
,
422 CheckerContext
&C
) const {
423 evalZeroParamNonNullReturn(Call
, DV
, C
);
424 evalZeroParamNullReturn(Call
, DV
, C
);
427 //===----------------------------------------------------------------------===//
428 // Evaluating isa, isa_and_nonnull.
429 //===----------------------------------------------------------------------===//
431 void CastValueChecker::evalIsa(const CallEvent
&Call
, DefinedOrUnknownSVal DV
,
432 CheckerContext
&C
) const {
433 ProgramStateRef NonNullState
, NullState
;
434 std::tie(NonNullState
, NullState
) = C
.getState()->assume(DV
);
437 addInstanceOfTransition(Call
, DV
, NonNullState
, C
, /*IsInstanceOf=*/true);
438 addInstanceOfTransition(Call
, DV
, NonNullState
, C
, /*IsInstanceOf=*/false);
442 C
.generateSink(NullState
, C
.getPredecessor());
446 void CastValueChecker::evalIsaAndNonNull(const CallEvent
&Call
,
447 DefinedOrUnknownSVal DV
,
448 CheckerContext
&C
) const {
449 ProgramStateRef NonNullState
, NullState
;
450 std::tie(NonNullState
, NullState
) = C
.getState()->assume(DV
);
453 addInstanceOfTransition(Call
, DV
, NonNullState
, C
, /*IsInstanceOf=*/true);
454 addInstanceOfTransition(Call
, DV
, NonNullState
, C
, /*IsInstanceOf=*/false);
458 addInstanceOfTransition(Call
, DV
, NullState
, C
, /*IsInstanceOf=*/false);
462 //===----------------------------------------------------------------------===//
463 // Main logic to evaluate a call.
464 //===----------------------------------------------------------------------===//
466 bool CastValueChecker::evalCall(const CallEvent
&Call
,
467 CheckerContext
&C
) const {
468 const auto *Lookup
= CDM
.lookup(Call
);
472 const CastCheck
&Check
= Lookup
->first
;
473 CallKind Kind
= Lookup
->second
;
475 std::optional
<DefinedOrUnknownSVal
> DV
;
478 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 QualType ParamT
= Call
.parameters()[0]->getType();
483 QualType ResultT
= Call
.getResultType();
484 if (!(ParamT
->isPointerType() && ResultT
->isPointerType()) &&
485 !(ParamT
->isReferenceType() && ResultT
->isReferenceType())) {
489 DV
= Call
.getArgSVal(0).getAs
<DefinedOrUnknownSVal
>();
492 case CallKind::InstanceOf
: {
493 // We need to obtain the only template argument to determinte the type.
494 const FunctionDecl
*FD
= Call
.getDecl()->getAsFunction();
495 if (!FD
|| !FD
->getTemplateSpecializationArgs())
498 DV
= Call
.getArgSVal(0).getAs
<DefinedOrUnknownSVal
>();
501 case CallKind::Method
:
502 const auto *InstanceCall
= dyn_cast
<CXXInstanceCall
>(&Call
);
506 DV
= InstanceCall
->getCXXThisVal().getAs
<DefinedOrUnknownSVal
>();
513 Check(this, Call
, *DV
, C
);
517 void CastValueChecker::checkDeadSymbols(SymbolReaper
&SR
,
518 CheckerContext
&C
) const {
519 C
.addTransition(removeDeadCasts(C
.getState(), SR
));
522 void ento::registerCastValueChecker(CheckerManager
&Mgr
) {
523 Mgr
.registerChecker
<CastValueChecker
>();
526 bool ento::shouldRegisterCastValueChecker(const CheckerManager
&mgr
) {