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