10b57cec5SDimitry Andric //===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This defines CastValueChecker which models casts of custom RTTIs.
100b57cec5SDimitry Andric //
11a7dea167SDimitry Andric // TODO list:
12a7dea167SDimitry Andric // - It only allows one succesful cast between two types however in the wild
13a7dea167SDimitry Andric // the object could be casted to multiple types.
14a7dea167SDimitry Andric // - It needs to check the most likely type information from the dynamic type
15a7dea167SDimitry Andric // map to increase precision of dynamic casting.
16a7dea167SDimitry Andric //
170b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
180b57cec5SDimitry Andric
19a7dea167SDimitry Andric #include "clang/AST/DeclTemplate.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
23349cc55cSDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
26a7dea167SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
27bdd1243dSDimitry Andric #include <optional>
28a7dea167SDimitry Andric #include <utility>
290b57cec5SDimitry Andric
300b57cec5SDimitry Andric using namespace clang;
310b57cec5SDimitry Andric using namespace ento;
320b57cec5SDimitry Andric
330b57cec5SDimitry Andric namespace {
345ffd83dbSDimitry Andric class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {
35a7dea167SDimitry Andric enum class CallKind { Function, Method, InstanceOf };
36a7dea167SDimitry Andric
370b57cec5SDimitry Andric using CastCheck =
38a7dea167SDimitry Andric std::function<void(const CastValueChecker *, const CallEvent &Call,
390b57cec5SDimitry Andric DefinedOrUnknownSVal, CheckerContext &)>;
400b57cec5SDimitry Andric
410b57cec5SDimitry Andric public:
42a7dea167SDimitry Andric // We have five cases to evaluate a cast:
43a7dea167SDimitry Andric // 1) The parameter is non-null, the return value is non-null.
44a7dea167SDimitry Andric // 2) The parameter is non-null, the return value is null.
45a7dea167SDimitry Andric // 3) The parameter is null, the return value is null.
460b57cec5SDimitry Andric // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3.
47a7dea167SDimitry Andric //
48a7dea167SDimitry Andric // 4) castAs: Has no parameter, the return value is non-null.
49a7dea167SDimitry Andric // 5) getAs: Has no parameter, the return value is null or non-null.
50a7dea167SDimitry Andric //
51a7dea167SDimitry Andric // We have two cases to check the parameter is an instance of the given type.
52a7dea167SDimitry Andric // 1) isa: The parameter is non-null, returns boolean.
53a7dea167SDimitry Andric // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
540b57cec5SDimitry Andric bool evalCall(const CallEvent &Call, CheckerContext &C) const;
555ffd83dbSDimitry Andric void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
560b57cec5SDimitry Andric
570b57cec5SDimitry Andric private:
58a7dea167SDimitry Andric // These are known in the LLVM project. The pairs are in the following form:
59*0fca6ea1SDimitry Andric // {{match-mode, {namespace, call}, argument-count}, {callback, kind}}
60a7dea167SDimitry Andric const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
61*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"llvm", "cast"}, 1},
62a7dea167SDimitry Andric {&CastValueChecker::evalCast, CallKind::Function}},
63*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"llvm", "dyn_cast"}, 1},
64a7dea167SDimitry Andric {&CastValueChecker::evalDynCast, CallKind::Function}},
65*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"llvm", "cast_or_null"}, 1},
66a7dea167SDimitry Andric {&CastValueChecker::evalCastOrNull, CallKind::Function}},
67*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"llvm", "dyn_cast_or_null"}, 1},
68a7dea167SDimitry Andric {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
69*0fca6ea1SDimitry Andric {{CDM::CXXMethod, {"clang", "castAs"}, 0},
70a7dea167SDimitry Andric {&CastValueChecker::evalCastAs, CallKind::Method}},
71*0fca6ea1SDimitry Andric {{CDM::CXXMethod, {"clang", "getAs"}, 0},
72a7dea167SDimitry Andric {&CastValueChecker::evalGetAs, CallKind::Method}},
73*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"llvm", "isa"}, 1},
74a7dea167SDimitry Andric {&CastValueChecker::evalIsa, CallKind::InstanceOf}},
75*0fca6ea1SDimitry Andric {{CDM::SimpleFunc, {"llvm", "isa_and_nonnull"}, 1},
76a7dea167SDimitry Andric {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
770b57cec5SDimitry Andric
78a7dea167SDimitry Andric void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
790b57cec5SDimitry Andric CheckerContext &C) const;
80a7dea167SDimitry Andric void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
810b57cec5SDimitry Andric CheckerContext &C) const;
82a7dea167SDimitry Andric void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
830b57cec5SDimitry Andric CheckerContext &C) const;
84a7dea167SDimitry Andric void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
85a7dea167SDimitry Andric CheckerContext &C) const;
86a7dea167SDimitry Andric void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
87a7dea167SDimitry Andric CheckerContext &C) const;
88a7dea167SDimitry Andric void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
89a7dea167SDimitry Andric CheckerContext &C) const;
90a7dea167SDimitry Andric void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
91a7dea167SDimitry Andric CheckerContext &C) const;
92a7dea167SDimitry Andric void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
930b57cec5SDimitry Andric CheckerContext &C) const;
940b57cec5SDimitry Andric };
950b57cec5SDimitry Andric } // namespace
960b57cec5SDimitry Andric
isInfeasibleCast(const DynamicCastInfo * CastInfo,bool CastSucceeds)97a7dea167SDimitry Andric static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
98a7dea167SDimitry Andric bool CastSucceeds) {
99a7dea167SDimitry Andric if (!CastInfo)
100a7dea167SDimitry Andric return false;
101a7dea167SDimitry Andric
102a7dea167SDimitry Andric return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
1030b57cec5SDimitry Andric }
1040b57cec5SDimitry Andric
getNoteTag(CheckerContext & C,const DynamicCastInfo * CastInfo,QualType CastToTy,const Expr * Object,bool CastSucceeds,bool IsKnownCast)105a7dea167SDimitry Andric static const NoteTag *getNoteTag(CheckerContext &C,
106a7dea167SDimitry Andric const DynamicCastInfo *CastInfo,
107a7dea167SDimitry Andric QualType CastToTy, const Expr *Object,
108a7dea167SDimitry Andric bool CastSucceeds, bool IsKnownCast) {
109a7dea167SDimitry Andric std::string CastToName =
11016d6b3b3SDimitry Andric CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
11156f451bbSDimitry Andric : CastToTy.getAsString();
112a7dea167SDimitry Andric Object = Object->IgnoreParenImpCasts();
1130b57cec5SDimitry Andric
114a7dea167SDimitry Andric return C.getNoteTag(
115a7dea167SDimitry Andric [=]() -> std::string {
1160b57cec5SDimitry Andric SmallString<128> Msg;
1170b57cec5SDimitry Andric llvm::raw_svector_ostream Out(Msg);
1180b57cec5SDimitry Andric
119a7dea167SDimitry Andric if (!IsKnownCast)
120a7dea167SDimitry Andric Out << "Assuming ";
121a7dea167SDimitry Andric
122a7dea167SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
123e8d8bef9SDimitry Andric Out << '\'' << DRE->getDecl()->getDeclName() << '\'';
124a7dea167SDimitry Andric } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
125a7dea167SDimitry Andric Out << (IsKnownCast ? "Field '" : "field '")
126e8d8bef9SDimitry Andric << ME->getMemberDecl()->getDeclName() << '\'';
127a7dea167SDimitry Andric } else {
128a7dea167SDimitry Andric Out << (IsKnownCast ? "The object" : "the object");
129a7dea167SDimitry Andric }
130a7dea167SDimitry Andric
131a7dea167SDimitry Andric Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
132a7dea167SDimitry Andric << '\'';
133a7dea167SDimitry Andric
1345ffd83dbSDimitry Andric return std::string(Out.str());
1350b57cec5SDimitry Andric },
1360b57cec5SDimitry Andric /*IsPrunable=*/true);
1370b57cec5SDimitry Andric }
1380b57cec5SDimitry Andric
getNoteTag(CheckerContext & C,SmallVector<QualType,4> CastToTyVec,const Expr * Object,bool IsKnownCast)13916d6b3b3SDimitry Andric static const NoteTag *getNoteTag(CheckerContext &C,
14016d6b3b3SDimitry Andric SmallVector<QualType, 4> CastToTyVec,
14116d6b3b3SDimitry Andric const Expr *Object,
14216d6b3b3SDimitry Andric bool IsKnownCast) {
14316d6b3b3SDimitry Andric Object = Object->IgnoreParenImpCasts();
14416d6b3b3SDimitry Andric
14516d6b3b3SDimitry Andric return C.getNoteTag(
14616d6b3b3SDimitry Andric [=]() -> std::string {
14716d6b3b3SDimitry Andric SmallString<128> Msg;
14816d6b3b3SDimitry Andric llvm::raw_svector_ostream Out(Msg);
14916d6b3b3SDimitry Andric
15016d6b3b3SDimitry Andric if (!IsKnownCast)
15116d6b3b3SDimitry Andric Out << "Assuming ";
15216d6b3b3SDimitry Andric
15316d6b3b3SDimitry Andric if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
15416d6b3b3SDimitry Andric Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
15516d6b3b3SDimitry Andric } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
15616d6b3b3SDimitry Andric Out << (IsKnownCast ? "Field '" : "field '")
15716d6b3b3SDimitry Andric << ME->getMemberDecl()->getNameAsString() << '\'';
15816d6b3b3SDimitry Andric } else {
15916d6b3b3SDimitry Andric Out << (IsKnownCast ? "The object" : "the object");
16016d6b3b3SDimitry Andric }
16116d6b3b3SDimitry Andric Out << " is";
16216d6b3b3SDimitry Andric
16316d6b3b3SDimitry Andric bool First = true;
16416d6b3b3SDimitry Andric for (QualType CastToTy: CastToTyVec) {
16516d6b3b3SDimitry Andric std::string CastToName =
16656f451bbSDimitry Andric CastToTy->getAsCXXRecordDecl()
16756f451bbSDimitry Andric ? CastToTy->getAsCXXRecordDecl()->getNameAsString()
16856f451bbSDimitry Andric : CastToTy.getAsString();
16916d6b3b3SDimitry Andric Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :
17016d6b3b3SDimitry Andric (First ? "neither" : "nor")) << " a '" << CastToName
17116d6b3b3SDimitry Andric << '\'';
17216d6b3b3SDimitry Andric First = false;
17316d6b3b3SDimitry Andric }
17416d6b3b3SDimitry Andric
17516d6b3b3SDimitry Andric return std::string(Out.str());
17616d6b3b3SDimitry Andric },
17716d6b3b3SDimitry Andric /*IsPrunable=*/true);
17816d6b3b3SDimitry Andric }
17916d6b3b3SDimitry Andric
180a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
181a7dea167SDimitry Andric // Main logic to evaluate a cast.
182a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
183a7dea167SDimitry Andric
alignReferenceTypes(QualType toAlign,QualType alignTowards,ASTContext & ACtx)184a7dea167SDimitry Andric static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,
185a7dea167SDimitry Andric ASTContext &ACtx) {
186a7dea167SDimitry Andric if (alignTowards->isLValueReferenceType() &&
187a7dea167SDimitry Andric alignTowards.isConstQualified()) {
188a7dea167SDimitry Andric toAlign.addConst();
189a7dea167SDimitry Andric return ACtx.getLValueReferenceType(toAlign);
190a7dea167SDimitry Andric } else if (alignTowards->isLValueReferenceType())
191a7dea167SDimitry Andric return ACtx.getLValueReferenceType(toAlign);
192a7dea167SDimitry Andric else if (alignTowards->isRValueReferenceType())
193a7dea167SDimitry Andric return ACtx.getRValueReferenceType(toAlign);
194a7dea167SDimitry Andric
195a7dea167SDimitry Andric llvm_unreachable("Must align towards a reference type!");
196a7dea167SDimitry Andric }
197a7dea167SDimitry Andric
addCastTransition(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C,bool IsNonNullParam,bool IsNonNullReturn,bool IsCheckedCast=false)198a7dea167SDimitry Andric static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
199a7dea167SDimitry Andric CheckerContext &C, bool IsNonNullParam,
200a7dea167SDimitry Andric bool IsNonNullReturn,
201a7dea167SDimitry Andric bool IsCheckedCast = false) {
202a7dea167SDimitry Andric ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
2030b57cec5SDimitry Andric if (!State)
2040b57cec5SDimitry Andric return;
2050b57cec5SDimitry Andric
206a7dea167SDimitry Andric const Expr *Object;
207a7dea167SDimitry Andric QualType CastFromTy;
208a7dea167SDimitry Andric QualType CastToTy = Call.getResultType();
2090b57cec5SDimitry Andric
210a7dea167SDimitry Andric if (Call.getNumArgs() > 0) {
211a7dea167SDimitry Andric Object = Call.getArgExpr(0);
212a7dea167SDimitry Andric CastFromTy = Call.parameters()[0]->getType();
213a7dea167SDimitry Andric } else {
214a7dea167SDimitry Andric Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
215a7dea167SDimitry Andric CastFromTy = Object->getType();
216a7dea167SDimitry Andric if (CastToTy->isPointerType()) {
217a7dea167SDimitry Andric if (!CastFromTy->isPointerType())
218a7dea167SDimitry Andric return;
219a7dea167SDimitry Andric } else {
220a7dea167SDimitry Andric if (!CastFromTy->isReferenceType())
2210b57cec5SDimitry Andric return;
2220b57cec5SDimitry Andric
223a7dea167SDimitry Andric CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());
224a7dea167SDimitry Andric }
225a7dea167SDimitry Andric }
2260b57cec5SDimitry Andric
227a7dea167SDimitry Andric const MemRegion *MR = DV.getAsRegion();
228a7dea167SDimitry Andric const DynamicCastInfo *CastInfo =
229a7dea167SDimitry Andric getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
230a7dea167SDimitry Andric
231a7dea167SDimitry Andric // We assume that every checked cast succeeds.
232a7dea167SDimitry Andric bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
233a7dea167SDimitry Andric if (!CastSucceeds) {
234a7dea167SDimitry Andric if (CastInfo)
235a7dea167SDimitry Andric CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
236a7dea167SDimitry Andric else
237a7dea167SDimitry Andric CastSucceeds = IsNonNullReturn;
238a7dea167SDimitry Andric }
239a7dea167SDimitry Andric
240a7dea167SDimitry Andric // Check for infeasible casts.
241a7dea167SDimitry Andric if (isInfeasibleCast(CastInfo, CastSucceeds)) {
242a7dea167SDimitry Andric C.generateSink(State, C.getPredecessor());
243a7dea167SDimitry Andric return;
244a7dea167SDimitry Andric }
245a7dea167SDimitry Andric
246a7dea167SDimitry Andric // Store the type and the cast information.
247a7dea167SDimitry Andric bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
248a7dea167SDimitry Andric if (!IsKnownCast || IsCheckedCast)
249a7dea167SDimitry Andric State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
250a7dea167SDimitry Andric CastSucceeds);
251a7dea167SDimitry Andric
252a7dea167SDimitry Andric SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
25381ad6265SDimitry Andric : C.getSValBuilder().makeNullWithType(CastToTy);
254a7dea167SDimitry Andric C.addTransition(
255a7dea167SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
256a7dea167SDimitry Andric getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
257a7dea167SDimitry Andric }
258a7dea167SDimitry Andric
addInstanceOfTransition(const CallEvent & Call,DefinedOrUnknownSVal DV,ProgramStateRef State,CheckerContext & C,bool IsInstanceOf)259a7dea167SDimitry Andric static void addInstanceOfTransition(const CallEvent &Call,
260a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
261a7dea167SDimitry Andric ProgramStateRef State, CheckerContext &C,
262a7dea167SDimitry Andric bool IsInstanceOf) {
263a7dea167SDimitry Andric const FunctionDecl *FD = Call.getDecl()->getAsFunction();
264a7dea167SDimitry Andric QualType CastFromTy = Call.parameters()[0]->getType();
26516d6b3b3SDimitry Andric SmallVector<QualType, 4> CastToTyVec;
26616d6b3b3SDimitry Andric for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1;
26716d6b3b3SDimitry Andric ++idx) {
26816d6b3b3SDimitry Andric TemplateArgument CastToTempArg =
26916d6b3b3SDimitry Andric FD->getTemplateSpecializationArgs()->get(idx);
27016d6b3b3SDimitry Andric switch (CastToTempArg.getKind()) {
27116d6b3b3SDimitry Andric default:
27216d6b3b3SDimitry Andric return;
27316d6b3b3SDimitry Andric case TemplateArgument::Type:
27416d6b3b3SDimitry Andric CastToTyVec.push_back(CastToTempArg.getAsType());
27516d6b3b3SDimitry Andric break;
27616d6b3b3SDimitry Andric case TemplateArgument::Pack:
27716d6b3b3SDimitry Andric for (TemplateArgument ArgInPack: CastToTempArg.pack_elements())
27816d6b3b3SDimitry Andric CastToTyVec.push_back(ArgInPack.getAsType());
27916d6b3b3SDimitry Andric break;
28016d6b3b3SDimitry Andric }
28116d6b3b3SDimitry Andric }
28216d6b3b3SDimitry Andric
28316d6b3b3SDimitry Andric const MemRegion *MR = DV.getAsRegion();
28416d6b3b3SDimitry Andric if (MR && CastFromTy->isReferenceType())
28516d6b3b3SDimitry Andric MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();
28616d6b3b3SDimitry Andric
28716d6b3b3SDimitry Andric bool Success = false;
28816d6b3b3SDimitry Andric bool IsAnyKnown = false;
28916d6b3b3SDimitry Andric for (QualType CastToTy: CastToTyVec) {
290a7dea167SDimitry Andric if (CastFromTy->isPointerType())
291a7dea167SDimitry Andric CastToTy = C.getASTContext().getPointerType(CastToTy);
292a7dea167SDimitry Andric else if (CastFromTy->isReferenceType())
293a7dea167SDimitry Andric CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
294a7dea167SDimitry Andric else
295a7dea167SDimitry Andric return;
296a7dea167SDimitry Andric
297a7dea167SDimitry Andric const DynamicCastInfo *CastInfo =
298a7dea167SDimitry Andric getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
299a7dea167SDimitry Andric
300a7dea167SDimitry Andric bool CastSucceeds;
301a7dea167SDimitry Andric if (CastInfo)
302a7dea167SDimitry Andric CastSucceeds = IsInstanceOf && CastInfo->succeeds();
303a7dea167SDimitry Andric else
304a7dea167SDimitry Andric CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
305a7dea167SDimitry Andric
306a7dea167SDimitry Andric // Store the type and the cast information.
307a7dea167SDimitry Andric bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
30816d6b3b3SDimitry Andric IsAnyKnown = IsAnyKnown || IsKnownCast;
30916d6b3b3SDimitry Andric ProgramStateRef NewState = State;
310a7dea167SDimitry Andric if (!IsKnownCast)
31116d6b3b3SDimitry Andric NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
312a7dea167SDimitry Andric IsInstanceOf);
313a7dea167SDimitry Andric
31416d6b3b3SDimitry Andric if (CastSucceeds) {
31516d6b3b3SDimitry Andric Success = true;
31616d6b3b3SDimitry Andric C.addTransition(
31716d6b3b3SDimitry Andric NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
31816d6b3b3SDimitry Andric C.getSValBuilder().makeTruthVal(true)),
31916d6b3b3SDimitry Andric getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true,
32016d6b3b3SDimitry Andric IsKnownCast));
32116d6b3b3SDimitry Andric if (IsKnownCast)
32216d6b3b3SDimitry Andric return;
32316d6b3b3SDimitry Andric } else if (CastInfo && CastInfo->succeeds()) {
32416d6b3b3SDimitry Andric C.generateSink(NewState, C.getPredecessor());
32516d6b3b3SDimitry Andric return;
32616d6b3b3SDimitry Andric }
32716d6b3b3SDimitry Andric }
32816d6b3b3SDimitry Andric
32916d6b3b3SDimitry Andric if (!Success) {
330a7dea167SDimitry Andric C.addTransition(
331a7dea167SDimitry Andric State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
33216d6b3b3SDimitry Andric C.getSValBuilder().makeTruthVal(false)),
33316d6b3b3SDimitry Andric getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown));
33416d6b3b3SDimitry Andric }
335a7dea167SDimitry Andric }
336a7dea167SDimitry Andric
337a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
338a7dea167SDimitry Andric // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
339a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
340a7dea167SDimitry Andric
evalNonNullParamNonNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C,bool IsCheckedCast=false)341a7dea167SDimitry Andric static void evalNonNullParamNonNullReturn(const CallEvent &Call,
342a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
343a7dea167SDimitry Andric CheckerContext &C,
344a7dea167SDimitry Andric bool IsCheckedCast = false) {
345a7dea167SDimitry Andric addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
346a7dea167SDimitry Andric /*IsNonNullReturn=*/true, IsCheckedCast);
347a7dea167SDimitry Andric }
348a7dea167SDimitry Andric
evalNonNullParamNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C)349a7dea167SDimitry Andric static void evalNonNullParamNullReturn(const CallEvent &Call,
350a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
351a7dea167SDimitry Andric CheckerContext &C) {
352a7dea167SDimitry Andric addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
353a7dea167SDimitry Andric /*IsNonNullReturn=*/false);
354a7dea167SDimitry Andric }
355a7dea167SDimitry Andric
evalNullParamNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C)356a7dea167SDimitry Andric static void evalNullParamNullReturn(const CallEvent &Call,
357a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
358a7dea167SDimitry Andric CheckerContext &C) {
359a7dea167SDimitry Andric if (ProgramStateRef State = C.getState()->assume(DV, false))
360a7dea167SDimitry Andric C.addTransition(State->BindExpr(Call.getOriginExpr(),
361a7dea167SDimitry Andric C.getLocationContext(),
36281ad6265SDimitry Andric C.getSValBuilder().makeNullWithType(
36381ad6265SDimitry Andric Call.getOriginExpr()->getType()),
36481ad6265SDimitry Andric false),
3650b57cec5SDimitry Andric C.getNoteTag("Assuming null pointer is passed into cast",
366a7dea167SDimitry Andric /*IsPrunable=*/true));
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric
evalCast(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const369a7dea167SDimitry Andric void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
3700b57cec5SDimitry Andric CheckerContext &C) const {
371a7dea167SDimitry Andric evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
3720b57cec5SDimitry Andric }
3730b57cec5SDimitry Andric
evalDynCast(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const374a7dea167SDimitry Andric void CastValueChecker::evalDynCast(const CallEvent &Call,
375a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
3760b57cec5SDimitry Andric CheckerContext &C) const {
377a7dea167SDimitry Andric evalNonNullParamNonNullReturn(Call, DV, C);
378a7dea167SDimitry Andric evalNonNullParamNullReturn(Call, DV, C);
3790b57cec5SDimitry Andric }
3800b57cec5SDimitry Andric
evalCastOrNull(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const381a7dea167SDimitry Andric void CastValueChecker::evalCastOrNull(const CallEvent &Call,
382a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
3830b57cec5SDimitry Andric CheckerContext &C) const {
384a7dea167SDimitry Andric evalNonNullParamNonNullReturn(Call, DV, C);
385a7dea167SDimitry Andric evalNullParamNullReturn(Call, DV, C);
3860b57cec5SDimitry Andric }
3870b57cec5SDimitry Andric
evalDynCastOrNull(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const388a7dea167SDimitry Andric void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
389a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
3900b57cec5SDimitry Andric CheckerContext &C) const {
391a7dea167SDimitry Andric evalNonNullParamNonNullReturn(Call, DV, C);
392a7dea167SDimitry Andric evalNonNullParamNullReturn(Call, DV, C);
393a7dea167SDimitry Andric evalNullParamNullReturn(Call, DV, C);
3940b57cec5SDimitry Andric }
3950b57cec5SDimitry Andric
396a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
397a7dea167SDimitry Andric // Evaluating castAs, getAs.
398a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
399a7dea167SDimitry Andric
evalZeroParamNonNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C,bool IsCheckedCast=false)400a7dea167SDimitry Andric static void evalZeroParamNonNullReturn(const CallEvent &Call,
401a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
402a7dea167SDimitry Andric CheckerContext &C,
403a7dea167SDimitry Andric bool IsCheckedCast = false) {
404a7dea167SDimitry Andric addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
405a7dea167SDimitry Andric /*IsNonNullReturn=*/true, IsCheckedCast);
406a7dea167SDimitry Andric }
407a7dea167SDimitry Andric
evalZeroParamNullReturn(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C)408a7dea167SDimitry Andric static void evalZeroParamNullReturn(const CallEvent &Call,
409a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
410a7dea167SDimitry Andric CheckerContext &C) {
411a7dea167SDimitry Andric addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
412a7dea167SDimitry Andric /*IsNonNullReturn=*/false);
413a7dea167SDimitry Andric }
414a7dea167SDimitry Andric
evalCastAs(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const415a7dea167SDimitry Andric void CastValueChecker::evalCastAs(const CallEvent &Call,
416a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
417a7dea167SDimitry Andric CheckerContext &C) const {
418a7dea167SDimitry Andric evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
419a7dea167SDimitry Andric }
420a7dea167SDimitry Andric
evalGetAs(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const421a7dea167SDimitry Andric void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
422a7dea167SDimitry Andric CheckerContext &C) const {
423a7dea167SDimitry Andric evalZeroParamNonNullReturn(Call, DV, C);
424a7dea167SDimitry Andric evalZeroParamNullReturn(Call, DV, C);
425a7dea167SDimitry Andric }
426a7dea167SDimitry Andric
427a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
428a7dea167SDimitry Andric // Evaluating isa, isa_and_nonnull.
429a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
430a7dea167SDimitry Andric
evalIsa(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const431a7dea167SDimitry Andric void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
432a7dea167SDimitry Andric CheckerContext &C) const {
433a7dea167SDimitry Andric ProgramStateRef NonNullState, NullState;
434a7dea167SDimitry Andric std::tie(NonNullState, NullState) = C.getState()->assume(DV);
435a7dea167SDimitry Andric
436a7dea167SDimitry Andric if (NonNullState) {
437a7dea167SDimitry Andric addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
438a7dea167SDimitry Andric addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
439a7dea167SDimitry Andric }
440a7dea167SDimitry Andric
441a7dea167SDimitry Andric if (NullState) {
442a7dea167SDimitry Andric C.generateSink(NullState, C.getPredecessor());
443a7dea167SDimitry Andric }
444a7dea167SDimitry Andric }
445a7dea167SDimitry Andric
evalIsaAndNonNull(const CallEvent & Call,DefinedOrUnknownSVal DV,CheckerContext & C) const446a7dea167SDimitry Andric void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
447a7dea167SDimitry Andric DefinedOrUnknownSVal DV,
448a7dea167SDimitry Andric CheckerContext &C) const {
449a7dea167SDimitry Andric ProgramStateRef NonNullState, NullState;
450a7dea167SDimitry Andric std::tie(NonNullState, NullState) = C.getState()->assume(DV);
451a7dea167SDimitry Andric
452a7dea167SDimitry Andric if (NonNullState) {
453a7dea167SDimitry Andric addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
454a7dea167SDimitry Andric addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
455a7dea167SDimitry Andric }
456a7dea167SDimitry Andric
457a7dea167SDimitry Andric if (NullState) {
458a7dea167SDimitry Andric addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);
459a7dea167SDimitry Andric }
460a7dea167SDimitry Andric }
461a7dea167SDimitry Andric
462a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
463a7dea167SDimitry Andric // Main logic to evaluate a call.
464a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
465a7dea167SDimitry Andric
evalCall(const CallEvent & Call,CheckerContext & C) const4660b57cec5SDimitry Andric bool CastValueChecker::evalCall(const CallEvent &Call,
4670b57cec5SDimitry Andric CheckerContext &C) const {
468a7dea167SDimitry Andric const auto *Lookup = CDM.lookup(Call);
469a7dea167SDimitry Andric if (!Lookup)
4700b57cec5SDimitry Andric return false;
4710b57cec5SDimitry Andric
472a7dea167SDimitry Andric const CastCheck &Check = Lookup->first;
473a7dea167SDimitry Andric CallKind Kind = Lookup->second;
474a7dea167SDimitry Andric
475bdd1243dSDimitry Andric std::optional<DefinedOrUnknownSVal> DV;
476a7dea167SDimitry Andric
477a7dea167SDimitry Andric switch (Kind) {
478a7dea167SDimitry Andric case CallKind::Function: {
479a7dea167SDimitry Andric // We only model casts from pointers to pointers or from references
480a7dea167SDimitry Andric // to references. Other casts are most likely specialized and we
481a7dea167SDimitry Andric // cannot model them.
482a7dea167SDimitry Andric QualType ParamT = Call.parameters()[0]->getType();
483a7dea167SDimitry Andric QualType ResultT = Call.getResultType();
484a7dea167SDimitry Andric if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&
48516d6b3b3SDimitry Andric !(ParamT->isReferenceType() && ResultT->isReferenceType())) {
4860b57cec5SDimitry Andric return false;
48716d6b3b3SDimitry Andric }
4880b57cec5SDimitry Andric
489a7dea167SDimitry Andric DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
490a7dea167SDimitry Andric break;
491a7dea167SDimitry Andric }
492a7dea167SDimitry Andric case CallKind::InstanceOf: {
493a7dea167SDimitry Andric // We need to obtain the only template argument to determinte the type.
494a7dea167SDimitry Andric const FunctionDecl *FD = Call.getDecl()->getAsFunction();
495a7dea167SDimitry Andric if (!FD || !FD->getTemplateSpecializationArgs())
4960b57cec5SDimitry Andric return false;
4970b57cec5SDimitry Andric
498a7dea167SDimitry Andric DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
499a7dea167SDimitry Andric break;
500a7dea167SDimitry Andric }
501a7dea167SDimitry Andric case CallKind::Method:
502a7dea167SDimitry Andric const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
503a7dea167SDimitry Andric if (!InstanceCall)
5040b57cec5SDimitry Andric return false;
5050b57cec5SDimitry Andric
506a7dea167SDimitry Andric DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
507a7dea167SDimitry Andric break;
508a7dea167SDimitry Andric }
509a7dea167SDimitry Andric
510a7dea167SDimitry Andric if (!DV)
511a7dea167SDimitry Andric return false;
512a7dea167SDimitry Andric
513a7dea167SDimitry Andric Check(this, Call, *DV, C);
5140b57cec5SDimitry Andric return true;
5150b57cec5SDimitry Andric }
5160b57cec5SDimitry Andric
checkDeadSymbols(SymbolReaper & SR,CheckerContext & C) const5175ffd83dbSDimitry Andric void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
5185ffd83dbSDimitry Andric CheckerContext &C) const {
5195ffd83dbSDimitry Andric C.addTransition(removeDeadCasts(C.getState(), SR));
5205ffd83dbSDimitry Andric }
5215ffd83dbSDimitry Andric
registerCastValueChecker(CheckerManager & Mgr)5220b57cec5SDimitry Andric void ento::registerCastValueChecker(CheckerManager &Mgr) {
5230b57cec5SDimitry Andric Mgr.registerChecker<CastValueChecker>();
5240b57cec5SDimitry Andric }
5250b57cec5SDimitry Andric
shouldRegisterCastValueChecker(const CheckerManager & mgr)5265ffd83dbSDimitry Andric bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {
5270b57cec5SDimitry Andric return true;
5280b57cec5SDimitry Andric }
529