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