clang: lib/StaticAnalyzer/Checkers/CastValueChecker.cpp Source File (original) (raw)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
27#include
28#include
29
30using namespace clang;
31using namespace ento;
32
33namespace {
34class 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,
40
41public:
42
43
44
45
46
47
48
49
50
51
52
53
56
57private:
58
59
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
94};
95}
96
98 bool CastSucceeds) {
99 if (!CastInfo)
100 return false;
101
102 return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
103}
104
108 bool CastSucceeds, bool IsKnownCast) {
109 std::string CastToName =
112 Object = Object->IgnoreParenImpCasts();
113
114 return C.getNoteTag(
115 [=]() -> std::string {
117 llvm::raw_svector_ostream Out(Msg);
118
119 if (!IsKnownCast)
120 Out << "Assuming ";
121
122 if (const auto *DRE = dyn_cast(Object)) {
123 Out << '\'' << DRE->getDecl()->getDeclName() << '\'';
124 } else if (const auto *ME = dyn_cast(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 true);
137}
138
141 const Expr *Object,
142 bool IsKnownCast) {
143 Object = Object->IgnoreParenImpCasts();
144
145 return C.getNoteTag(
146 [=]() -> std::string {
148 llvm::raw_svector_ostream Out(Msg);
149
150 if (!IsKnownCast)
151 Out << "Assuming ";
152
153 if (const auto *DRE = dyn_cast(Object)) {
154 Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
155 } else if (const auto *ME = dyn_cast(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 << '\'';
173 }
174
175 return std::string(Out.str());
176 },
177 true);
178}
179
180
181
182
183
194
195 llvm_unreachable("Must align towards a reference type!");
196}
197
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;
209
210 if (Call.getNumArgs() > 0) {
211 Object = Call.getArgExpr(0);
212 CastFromTy = Call.parameters()[0]->getType();
213 } else {
214 Object = cast(&Call)->getCXXThisExpr();
215 CastFromTy = Object->getType();
218 return;
219 } else {
221 return;
222
224 }
225 }
226
230
231
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
242 C.generateSink(State, C.getPredecessor());
243 return;
244 }
245
246
247 bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
248 if (!IsKnownCast || IsCheckedCast)
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
262 bool IsInstanceOf) {
264 QualType CastFromTy = Call.parameters()[0]->getType();
267 ++idx) {
270 switch (CastToTempArg.getKind()) {
271 default:
272 return;
274 CastToTyVec.push_back(CastToTempArg.getAsType());
275 break;
278 CastToTyVec.push_back(ArgInPack.getAsType());
279 break;
280 }
281 }
282
285 MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();
286
288 bool IsAnyKnown = false;
289 for (QualType CastToTy: CastToTyVec) {
291 CastToTy = C.getASTContext().getPointerType(CastToTy);
294 else
295 return;
296
299
300 bool CastSucceeds;
301 if (CastInfo)
302 CastSucceeds = IsInstanceOf && CastInfo->succeeds();
303 else
304 CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
305
306
307 bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
308 IsAnyKnown = IsAnyKnown || IsKnownCast;
310 if (!IsKnownCast)
312 IsInstanceOf);
313
314 if (CastSucceeds) {
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
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
339
340
344 bool IsCheckedCast = false) {
346 true, IsCheckedCast);
347}
348
353 false);
354}
355
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 true));
367}
368
372}
373
374void CastValueChecker::evalDynCast(const CallEvent &Call,
379}
380
381void CastValueChecker::evalCastOrNull(const CallEvent &Call,
386}
387
388void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
394}
395
396
397
398
399
403 bool IsCheckedCast = false) {
405 true, IsCheckedCast);
406}
407
412 false);
413}
414
415void CastValueChecker::evalCastAs(const CallEvent &Call,
419}
420
425}
426
427
428
429
430
434 std::tie(NonNullState, NullState) = C.getState()->assume(DV);
435
436 if (NonNullState) {
439 }
440
441 if (NullState) {
442 C.generateSink(NullState, C.getPredecessor());
443 }
444}
445
446void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
450 std::tie(NonNullState, NullState) = C.getState()->assume(DV);
451
452 if (NonNullState) {
455 }
456
457 if (NullState) {
459 }
460}
461
462
463
464
465
466bool CastValueChecker::evalCall(const CallEvent &Call,
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 DV;
476
477 switch (Kind) {
478 case CallKind::Function: {
479
480
481
482 QualType ParamT = Call.parameters()[0]->getType();
486 return false;
487 }
488
490 break;
491 }
492 case CallKind::InstanceOf: {
493
496 return false;
497
499 break;
500 }
501 case CallKind::Method:
502 const auto *InstanceCall = dyn_cast(&Call);
503 if (!InstanceCall)
504 return false;
505
507 break;
508 }
509
510 if (!DV)
511 return false;
512
513 Check(this, Call, *DV, C);
514 return true;
515}
516
517void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
520}
521
522void ento::registerCastValueChecker(CheckerManager &Mgr) {
524}
525
526bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {
527 return true;
528}
static bool isInfeasibleCast(const DynamicCastInfo *CastInfo, bool CastSucceeds)
static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C, bool IsNonNullParam, bool IsNonNullReturn, bool IsCheckedCast=false)
static const NoteTag * getNoteTag(CheckerContext &C, const DynamicCastInfo *CastInfo, QualType CastToTy, const Expr *Object, bool CastSucceeds, bool IsKnownCast)
static void evalNonNullParamNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C)
static void addInstanceOfTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, ProgramStateRef State, CheckerContext &C, bool IsInstanceOf)
static void evalZeroParamNonNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C, bool IsCheckedCast=false)
static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards, ASTContext &ACtx)
static void evalNullParamNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C)
static void evalZeroParamNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C)
static void evalNonNullParamNonNullReturn(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C, bool IsCheckedCast=false)
Defines the C++ template declaration subclasses.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
QualType getRValueReferenceType(QualType T) const
Return the uniqued reference to the type for an rvalue reference to the specified type.
QualType getLValueReferenceType(QualType T, bool SpelledAsLValue=true) const
Return the uniqued reference to the type for an lvalue reference to the specified type.
This represents one expression.
Represents a function declaration or definition.
const TemplateArgumentList * getTemplateSpecializationArgs() const
Retrieve the template arguments used to produce this function template specialization from the primar...
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
A (possibly-)qualified type.
void addConst()
Add the const type qualifier to this QualType.
bool isConstQualified() const
Determine whether this type is const-qualified.
static std::string getAsString(SplitQualType split, const PrintingPolicy &Policy)
const TemplateArgument & get(unsigned Idx) const
Retrieve the template argument at a given index.
Represents a template argument.
QualType getAsType() const
Retrieve the type for a type template argument.
ArrayRef< TemplateArgument > pack_elements() const
Iterator range referencing all of the elements of a template argument pack.
@ Pack
The template argument is actually a parameter pack.
@ Type
The template argument is a type.
ArgKind getKind() const
Return the kind of stored template argument.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
bool isRValueReferenceType() const
bool isPointerType() const
bool isReferenceType() const
bool isLValueReferenceType() const
An immutable map from CallDescriptions to arbitrary data.
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
MemRegion - The root abstract class for all memory regions.
The tag upon which the TagVisitor reacts.
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
const MemRegion * getAsRegion() const
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
A class responsible for cleaning up unused symbols.
const DynamicCastInfo * getDynamicCastInfo(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy, QualType CastToTy)
Get dynamic cast information from CastFromTy to CastToTy of MR.
ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR)
Removes the dead cast informations from State.
ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy, QualType CastToTy, bool IsCastSucceeds)
Set dynamic type and cast information of the region; return the new state.
The JSON file list parser is used to communicate input to InstallAPI.
@ Success
Template argument deduction was successful.