blob: 1580f7f31cee2f6f0d56ddc923c91b9ba98be8a4 [file] [log] [blame]
Csaba Dabis693936a2019-07-10 00:20:03 +00001//===- 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//
Csaba Dabis0202c352019-08-22 00:20:36 +00009// 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.
Csaba Dabis693936a2019-07-10 00:20:03 +000016//
17//===----------------------------------------------------------------------===//
18
19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
20#include "clang/StaticAnalyzer/Core/Checker.h"
21#include "clang/StaticAnalyzer/Core/CheckerManager.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Csaba Dabis0202c352019-08-22 00:20:36 +000024#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
Csaba Dabis693936a2019-07-10 00:20:03 +000025#include "llvm/ADT/Optional.h"
Csaba Dabiscf229d52019-08-09 02:24:42 +000026#include <utility>
Csaba Dabis693936a2019-07-10 00:20:03 +000027
28using namespace clang;
29using namespace ento;
30
31namespace {
32class CastValueChecker : public Checker<eval::Call> {
Csaba Dabis0202c352019-08-22 00:20:36 +000033 enum class CallKind { Function, Method };
Csaba Dabiscf229d52019-08-09 02:24:42 +000034
Csaba Dabis693936a2019-07-10 00:20:03 +000035 using CastCheck =
Csaba Dabis0202c352019-08-22 00:20:36 +000036 std::function<void(const CastValueChecker *, const CallEvent &Call,
Csaba Dabis693936a2019-07-10 00:20:03 +000037 DefinedOrUnknownSVal, CheckerContext &)>;
38
39public:
Csaba Dabiscf229d52019-08-09 02:24:42 +000040 // We have five cases to evaluate a cast:
Csaba Dabis0202c352019-08-22 00:20:36 +000041 // 1) The parameter is non-null, the return value is non-null.
42 // 2) The parameter is non-null, the return value is null.
43 // 3) The parameter is null, the return value is null.
Csaba Dabis693936a2019-07-10 00:20:03 +000044 // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3.
Csaba Dabiscf229d52019-08-09 02:24:42 +000045 //
Csaba Dabis0202c352019-08-22 00:20:36 +000046 // 4) castAs: Has no parameter, the return value is non-null.
47 // 5) getAs: Has no parameter, the return value is null or non-null.
Csaba Dabis693936a2019-07-10 00:20:03 +000048 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
Csaba Dabis0202c352019-08-22 00:20:36 +000049 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
Csaba Dabis693936a2019-07-10 00:20:03 +000050
51private:
Csaba Dabiscf229d52019-08-09 02:24:42 +000052 // These are known in the LLVM project. The pairs are in the following form:
53 // {{{namespace, call}, argument-count}, {callback, kind}}
Csaba Dabis0202c352019-08-22 00:20:36 +000054 const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
Csaba Dabiscf229d52019-08-09 02:24:42 +000055 {{{"llvm", "cast"}, 1},
Csaba Dabis0202c352019-08-22 00:20:36 +000056 {&CastValueChecker::evalCast, CallKind::Function}},
Csaba Dabiscf229d52019-08-09 02:24:42 +000057 {{{"llvm", "dyn_cast"}, 1},
Csaba Dabis0202c352019-08-22 00:20:36 +000058 {&CastValueChecker::evalDynCast, CallKind::Function}},
Csaba Dabiscf229d52019-08-09 02:24:42 +000059 {{{"llvm", "cast_or_null"}, 1},
Csaba Dabis0202c352019-08-22 00:20:36 +000060 {&CastValueChecker::evalCastOrNull, CallKind::Function}},
Csaba Dabis693936a2019-07-10 00:20:03 +000061 {{{"llvm", "dyn_cast_or_null"}, 1},
Csaba Dabis0202c352019-08-22 00:20:36 +000062 {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
Csaba Dabiscf229d52019-08-09 02:24:42 +000063 {{{"clang", "castAs"}, 0},
Csaba Dabis0202c352019-08-22 00:20:36 +000064 {&CastValueChecker::evalCastAs, CallKind::Method}},
Csaba Dabiscf229d52019-08-09 02:24:42 +000065 {{{"clang", "getAs"}, 0},
Csaba Dabis0202c352019-08-22 00:20:36 +000066 {&CastValueChecker::evalGetAs, CallKind::Method}}};
Csaba Dabis693936a2019-07-10 00:20:03 +000067
Csaba Dabis0202c352019-08-22 00:20:36 +000068 void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000069 CheckerContext &C) const;
Csaba Dabis0202c352019-08-22 00:20:36 +000070 void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000071 CheckerContext &C) const;
Csaba Dabis0202c352019-08-22 00:20:36 +000072 void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000073 CheckerContext &C) const;
Csaba Dabis0202c352019-08-22 00:20:36 +000074 void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000075 CheckerContext &C) const;
Csaba Dabis0202c352019-08-22 00:20:36 +000076 void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabiscf229d52019-08-09 02:24:42 +000077 CheckerContext &C) const;
Csaba Dabis0202c352019-08-22 00:20:36 +000078 void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabiscf229d52019-08-09 02:24:42 +000079 CheckerContext &C) const;
Csaba Dabis693936a2019-07-10 00:20:03 +000080};
81} // namespace
82
Csaba Dabis0202c352019-08-22 00:20:36 +000083static QualType getRecordType(QualType Ty) {
84 Ty = Ty.getCanonicalType();
Csaba Dabiscf229d52019-08-09 02:24:42 +000085
Csaba Dabis0202c352019-08-22 00:20:36 +000086 if (Ty->isPointerType())
87 Ty = Ty->getPointeeType();
88
89 if (Ty->isReferenceType())
90 Ty = Ty.getNonReferenceType();
91
92 return Ty.getUnqualifiedType();
Csaba Dabis693936a2019-07-10 00:20:03 +000093}
94
Csaba Dabis0202c352019-08-22 00:20:36 +000095static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
96 bool CastSucceeds) {
97 if (!CastInfo)
98 return false;
99
100 return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
101}
102
103static const NoteTag *getNoteTag(CheckerContext &C,
104 const DynamicCastInfo *CastInfo,
105 QualType CastToTy, const Expr *Object,
106 bool CastSucceeds, bool IsKnownCast) {
107 std::string CastToName =
108 CastInfo ? CastInfo->to()->getAsCXXRecordDecl()->getNameAsString()
109 : CastToTy->getAsCXXRecordDecl()->getNameAsString();
110 Object = Object->IgnoreParenImpCasts();
Csaba Dabis693936a2019-07-10 00:20:03 +0000111
Csaba Dabiscf229d52019-08-09 02:24:42 +0000112 return C.getNoteTag(
Csaba Dabis22dc44f2019-08-22 01:41:06 +0000113 [=]() -> std::string {
Csaba Dabis693936a2019-07-10 00:20:03 +0000114 SmallString<128> Msg;
115 llvm::raw_svector_ostream Out(Msg);
116
Csaba Dabis0202c352019-08-22 00:20:36 +0000117 if (!IsKnownCast)
118 Out << "Assuming ";
Csaba Dabiscf229d52019-08-09 02:24:42 +0000119
Csaba Dabis0202c352019-08-22 00:20:36 +0000120 if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
121 Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
122 } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
123 Out << (IsKnownCast ? "Field '" : "field '")
124 << ME->getMemberDecl()->getNameAsString() << '\'';
125 } else {
126 Out << (IsKnownCast ? "The object" : "the object");
127 }
128
129 Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
130 << '\'';
Csaba Dabiscf229d52019-08-09 02:24:42 +0000131
Csaba Dabis693936a2019-07-10 00:20:03 +0000132 return Out.str();
133 },
134 /*IsPrunable=*/true);
Csaba Dabiscf229d52019-08-09 02:24:42 +0000135}
Csaba Dabis693936a2019-07-10 00:20:03 +0000136
Csaba Dabis0202c352019-08-22 00:20:36 +0000137//===----------------------------------------------------------------------===//
138// Main logic to evaluate a cast.
139//===----------------------------------------------------------------------===//
140
141static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
142 CheckerContext &C, bool IsNonNullParam,
143 bool IsNonNullReturn,
144 bool IsCheckedCast = false) {
145 ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
146 if (!State)
147 return;
148
149 const Expr *Object;
150 QualType CastFromTy;
151 QualType CastToTy = getRecordType(Call.getResultType());
152
153 if (Call.getNumArgs() > 0) {
154 Object = Call.getArgExpr(0);
155 CastFromTy = getRecordType(Call.parameters()[0]->getType());
156 } else {
157 Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
158 CastFromTy = getRecordType(Object->getType());
159 }
160
161 const MemRegion *MR = DV.getAsRegion();
162 const DynamicCastInfo *CastInfo =
163 getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
164
165 // We assume that every checked cast succeeds.
166 bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
167 if (!CastSucceeds) {
168 if (CastInfo)
169 CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
170 else
171 CastSucceeds = IsNonNullReturn;
172 }
173
174 // Check for infeasible casts.
175 if (isInfeasibleCast(CastInfo, CastSucceeds)) {
176 C.generateSink(State, C.getPredecessor());
177 return;
178 }
179
180 // Store the type and the cast information.
181 bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
182 if (!IsKnownCast || IsCheckedCast)
183 State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
184 Call.getResultType(), CastSucceeds);
185
186 SVal V = CastSucceeds ? DV : C.getSValBuilder().makeNull();
187 C.addTransition(
188 State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
189 getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
Csaba Dabiscf229d52019-08-09 02:24:42 +0000190}
191
192//===----------------------------------------------------------------------===//
193// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
194//===----------------------------------------------------------------------===//
195
Csaba Dabis0202c352019-08-22 00:20:36 +0000196static void evalNonNullParamNonNullReturn(const CallEvent &Call,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000197 DefinedOrUnknownSVal DV,
198 CheckerContext &C,
199 bool IsCheckedCast = false) {
Csaba Dabis0202c352019-08-22 00:20:36 +0000200 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
201 /*IsNonNullReturn=*/true, IsCheckedCast);
Csaba Dabis693936a2019-07-10 00:20:03 +0000202}
203
Csaba Dabis0202c352019-08-22 00:20:36 +0000204static void evalNonNullParamNullReturn(const CallEvent &Call,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000205 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000206 CheckerContext &C) {
Csaba Dabis0202c352019-08-22 00:20:36 +0000207 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
208 /*IsNonNullReturn=*/false);
Csaba Dabis693936a2019-07-10 00:20:03 +0000209}
210
Csaba Dabis0202c352019-08-22 00:20:36 +0000211static void evalNullParamNullReturn(const CallEvent &Call,
212 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000213 CheckerContext &C) {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000214 if (ProgramStateRef State = C.getState()->assume(DV, false))
Csaba Dabis0202c352019-08-22 00:20:36 +0000215 C.addTransition(State->BindExpr(Call.getOriginExpr(),
216 C.getLocationContext(),
217 C.getSValBuilder().makeNull(), false),
Csaba Dabiscf229d52019-08-09 02:24:42 +0000218 C.getNoteTag("Assuming null pointer is passed into cast",
219 /*IsPrunable=*/true));
Csaba Dabis693936a2019-07-10 00:20:03 +0000220}
221
Csaba Dabis0202c352019-08-22 00:20:36 +0000222void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000223 CheckerContext &C) const {
Csaba Dabis0202c352019-08-22 00:20:36 +0000224 evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
Csaba Dabis693936a2019-07-10 00:20:03 +0000225}
226
Csaba Dabis0202c352019-08-22 00:20:36 +0000227void CastValueChecker::evalDynCast(const CallEvent &Call,
228 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000229 CheckerContext &C) const {
Csaba Dabis0202c352019-08-22 00:20:36 +0000230 evalNonNullParamNonNullReturn(Call, DV, C);
231 evalNonNullParamNullReturn(Call, DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000232}
233
Csaba Dabis0202c352019-08-22 00:20:36 +0000234void CastValueChecker::evalCastOrNull(const CallEvent &Call,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000235 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000236 CheckerContext &C) const {
Csaba Dabis0202c352019-08-22 00:20:36 +0000237 evalNonNullParamNonNullReturn(Call, DV, C);
238 evalNullParamNullReturn(Call, DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000239}
240
Csaba Dabis0202c352019-08-22 00:20:36 +0000241void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000242 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000243 CheckerContext &C) const {
Csaba Dabis0202c352019-08-22 00:20:36 +0000244 evalNonNullParamNonNullReturn(Call, DV, C);
245 evalNonNullParamNullReturn(Call, DV, C);
246 evalNullParamNullReturn(Call, DV, C);
Csaba Dabiscf229d52019-08-09 02:24:42 +0000247}
248
249//===----------------------------------------------------------------------===//
250// Evaluating castAs, getAs.
251//===----------------------------------------------------------------------===//
252
Csaba Dabis0202c352019-08-22 00:20:36 +0000253static void evalZeroParamNonNullReturn(const CallEvent &Call,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000254 DefinedOrUnknownSVal DV,
255 CheckerContext &C,
256 bool IsCheckedCast = false) {
Csaba Dabis0202c352019-08-22 00:20:36 +0000257 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
258 /*IsNonNullReturn=*/true, IsCheckedCast);
Csaba Dabiscf229d52019-08-09 02:24:42 +0000259}
260
Csaba Dabis0202c352019-08-22 00:20:36 +0000261static void evalZeroParamNullReturn(const CallEvent &Call,
262 DefinedOrUnknownSVal DV,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000263 CheckerContext &C) {
Csaba Dabis0202c352019-08-22 00:20:36 +0000264 addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
265 /*IsNonNullReturn=*/false);
Csaba Dabiscf229d52019-08-09 02:24:42 +0000266}
267
Csaba Dabis0202c352019-08-22 00:20:36 +0000268void CastValueChecker::evalCastAs(const CallEvent &Call,
269 DefinedOrUnknownSVal DV,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000270 CheckerContext &C) const {
Csaba Dabis0202c352019-08-22 00:20:36 +0000271 evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
Csaba Dabiscf229d52019-08-09 02:24:42 +0000272}
273
Csaba Dabis0202c352019-08-22 00:20:36 +0000274void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000275 CheckerContext &C) const {
Csaba Dabis0202c352019-08-22 00:20:36 +0000276 evalZeroParamNonNullReturn(Call, DV, C);
277 evalZeroParamNullReturn(Call, DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000278}
279
Csaba Dabis0202c352019-08-22 00:20:36 +0000280//===----------------------------------------------------------------------===//
281// Main logic to evaluate a call.
282//===----------------------------------------------------------------------===//
283
Csaba Dabis693936a2019-07-10 00:20:03 +0000284bool CastValueChecker::evalCall(const CallEvent &Call,
285 CheckerContext &C) const {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000286 const auto *Lookup = CDM.lookup(Call);
287 if (!Lookup)
Csaba Dabis693936a2019-07-10 00:20:03 +0000288 return false;
289
Csaba Dabis0202c352019-08-22 00:20:36 +0000290 // We need to obtain the record type of the call's result to model it.
291 if (!getRecordType(Call.getResultType())->isRecordType())
Csaba Dabiscf229d52019-08-09 02:24:42 +0000292 return false;
293
294 const CastCheck &Check = Lookup->first;
Csaba Dabis0202c352019-08-22 00:20:36 +0000295 CallKind Kind = Lookup->second;
Csaba Dabiscf229d52019-08-09 02:24:42 +0000296 Optional<DefinedOrUnknownSVal> DV;
297
298 switch (Kind) {
Csaba Dabis0202c352019-08-22 00:20:36 +0000299 case CallKind::Function: {
300 // We need to obtain the record type of the call's parameter to model it.
301 if (!getRecordType(Call.parameters()[0]->getType())->isRecordType())
Csaba Dabiscf229d52019-08-09 02:24:42 +0000302 return false;
303
304 DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
305 break;
306 }
Csaba Dabis0202c352019-08-22 00:20:36 +0000307 case CallKind::Method:
Csaba Dabiscf229d52019-08-09 02:24:42 +0000308 const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
309 if (!InstanceCall)
310 return false;
311
312 DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
313 break;
314 }
315
316 if (!DV)
Csaba Dabis693936a2019-07-10 00:20:03 +0000317 return false;
318
Csaba Dabis0202c352019-08-22 00:20:36 +0000319 Check(this, Call, *DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000320 return true;
321}
322
Csaba Dabis0202c352019-08-22 00:20:36 +0000323void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
324 CheckerContext &C) const {
325 C.addTransition(removeDeadCasts(C.getState(), SR));
326}
327
Csaba Dabis693936a2019-07-10 00:20:03 +0000328void ento::registerCastValueChecker(CheckerManager &Mgr) {
329 Mgr.registerChecker<CastValueChecker>();
330}
331
332bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
333 return true;
334}