]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
Merge ^/head r363583 through r364040.
[FreeBSD/FreeBSD.git] / contrib / llvm-project / clang / lib / StaticAnalyzer / Checkers / CastValueChecker.cpp
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/CallEvent.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
25 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
26 #include "llvm/ADT/Optional.h"
27 #include <utility>
28
29 using namespace clang;
30 using namespace ento;
31
32 namespace {
33 class CastValueChecker : public Checker<check::DeadSymbols, eval::Call> {
34   enum class CallKind { Function, Method, InstanceOf };
35
36   using CastCheck =
37       std::function<void(const CastValueChecker *, const CallEvent &Call,
38                          DefinedOrUnknownSVal, CheckerContext &)>;
39
40 public:
41   // We have five cases to evaluate a cast:
42   // 1) The parameter is non-null, the return value is non-null.
43   // 2) The parameter is non-null, the return value is null.
44   // 3) The parameter is null, the return value is null.
45   // cast: 1;  dyn_cast: 1, 2;  cast_or_null: 1, 3;  dyn_cast_or_null: 1, 2, 3.
46   //
47   // 4) castAs: Has no parameter, the return value is non-null.
48   // 5) getAs:  Has no parameter, the return value is null or non-null.
49   //
50   // We have two cases to check the parameter is an instance of the given type.
51   // 1) isa:             The parameter is non-null, returns boolean.
52   // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
53   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
54   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
55
56 private:
57   // These are known in the LLVM project. The pairs are in the following form:
58   // {{{namespace, call}, argument-count}, {callback, kind}}
59   const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
60       {{{"llvm", "cast"}, 1},
61        {&CastValueChecker::evalCast, CallKind::Function}},
62       {{{"llvm", "dyn_cast"}, 1},
63        {&CastValueChecker::evalDynCast, CallKind::Function}},
64       {{{"llvm", "cast_or_null"}, 1},
65        {&CastValueChecker::evalCastOrNull, CallKind::Function}},
66       {{{"llvm", "dyn_cast_or_null"}, 1},
67        {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
68       {{{"clang", "castAs"}, 0},
69        {&CastValueChecker::evalCastAs, CallKind::Method}},
70       {{{"clang", "getAs"}, 0},
71        {&CastValueChecker::evalGetAs, CallKind::Method}},
72       {{{"llvm", "isa"}, 1},
73        {&CastValueChecker::evalIsa, CallKind::InstanceOf}},
74       {{{"llvm", "isa_and_nonnull"}, 1},
75        {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
76
77   void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
78                 CheckerContext &C) const;
79   void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
80                    CheckerContext &C) const;
81   void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
82                       CheckerContext &C) const;
83   void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
84                          CheckerContext &C) const;
85   void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
86                   CheckerContext &C) const;
87   void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
88                  CheckerContext &C) const;
89   void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
90                CheckerContext &C) const;
91   void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
92                          CheckerContext &C) const;
93 };
94 } // namespace
95
96 static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
97                              bool CastSucceeds) {
98   if (!CastInfo)
99     return false;
100
101   return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
102 }
103
104 static const NoteTag *getNoteTag(CheckerContext &C,
105                                  const DynamicCastInfo *CastInfo,
106                                  QualType CastToTy, const Expr *Object,
107                                  bool CastSucceeds, bool IsKnownCast) {
108   std::string CastToName =
109       CastInfo ? CastInfo->to()->getPointeeCXXRecordDecl()->getNameAsString()
110                : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
111   Object = Object->IgnoreParenImpCasts();
112
113   return C.getNoteTag(
114       [=]() -> std::string {
115         SmallString<128> Msg;
116         llvm::raw_svector_ostream Out(Msg);
117
118         if (!IsKnownCast)
119           Out << "Assuming ";
120
121         if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
122           Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
123         } else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
124           Out << (IsKnownCast ? "Field '" : "field '")
125               << ME->getMemberDecl()->getNameAsString() << '\'';
126         } else {
127           Out << (IsKnownCast ? "The object" : "the object");
128         }
129
130         Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
131             << '\'';
132
133         return std::string(Out.str());
134       },
135       /*IsPrunable=*/true);
136 }
137
138 //===----------------------------------------------------------------------===//
139 // Main logic to evaluate a cast.
140 //===----------------------------------------------------------------------===//
141
142 static QualType alignReferenceTypes(QualType toAlign, QualType alignTowards,
143                                     ASTContext &ACtx) {
144   if (alignTowards->isLValueReferenceType() &&
145       alignTowards.isConstQualified()) {
146     toAlign.addConst();
147     return ACtx.getLValueReferenceType(toAlign);
148   } else if (alignTowards->isLValueReferenceType())
149     return ACtx.getLValueReferenceType(toAlign);
150   else if (alignTowards->isRValueReferenceType())
151     return ACtx.getRValueReferenceType(toAlign);
152
153   llvm_unreachable("Must align towards a reference type!");
154 }
155
156 static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
157                               CheckerContext &C, bool IsNonNullParam,
158                               bool IsNonNullReturn,
159                               bool IsCheckedCast = false) {
160   ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
161   if (!State)
162     return;
163
164   const Expr *Object;
165   QualType CastFromTy;
166   QualType CastToTy = Call.getResultType();
167
168   if (Call.getNumArgs() > 0) {
169     Object = Call.getArgExpr(0);
170     CastFromTy = Call.parameters()[0]->getType();
171   } else {
172     Object = cast<CXXInstanceCall>(&Call)->getCXXThisExpr();
173     CastFromTy = Object->getType();
174     if (CastToTy->isPointerType()) {
175       if (!CastFromTy->isPointerType())
176         return;
177     } else {
178       if (!CastFromTy->isReferenceType())
179         return;
180
181       CastFromTy = alignReferenceTypes(CastFromTy, CastToTy, C.getASTContext());
182     }
183   }
184
185   const MemRegion *MR = DV.getAsRegion();
186   const DynamicCastInfo *CastInfo =
187       getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
188
189   // We assume that every checked cast succeeds.
190   bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
191   if (!CastSucceeds) {
192     if (CastInfo)
193       CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
194     else
195       CastSucceeds = IsNonNullReturn;
196   }
197
198   // Check for infeasible casts.
199   if (isInfeasibleCast(CastInfo, CastSucceeds)) {
200     C.generateSink(State, C.getPredecessor());
201     return;
202   }
203
204   // Store the type and the cast information.
205   bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
206   if (!IsKnownCast || IsCheckedCast)
207     State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
208                                       CastSucceeds);
209
210   SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
211                         : C.getSValBuilder().makeNull();
212   C.addTransition(
213       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
214       getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
215 }
216
217 static void addInstanceOfTransition(const CallEvent &Call,
218                                     DefinedOrUnknownSVal DV,
219                                     ProgramStateRef State, CheckerContext &C,
220                                     bool IsInstanceOf) {
221   const FunctionDecl *FD = Call.getDecl()->getAsFunction();
222   QualType CastFromTy = Call.parameters()[0]->getType();
223   QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType();
224   if (CastFromTy->isPointerType())
225     CastToTy = C.getASTContext().getPointerType(CastToTy);
226   else if (CastFromTy->isReferenceType())
227     CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
228   else
229     return;
230
231   const MemRegion *MR = DV.getAsRegion();
232   const DynamicCastInfo *CastInfo =
233       getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
234
235   bool CastSucceeds;
236   if (CastInfo)
237     CastSucceeds = IsInstanceOf && CastInfo->succeeds();
238   else
239     CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
240
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 || CastFromTy == CastToTy;
248   if (!IsKnownCast)
249     State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
250                                       IsInstanceOf);
251
252   C.addTransition(
253       State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
254                       C.getSValBuilder().makeTruthVal(CastSucceeds)),
255       getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds,
256                  IsKnownCast));
257 }
258
259 //===----------------------------------------------------------------------===//
260 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
261 //===----------------------------------------------------------------------===//
262
263 static void evalNonNullParamNonNullReturn(const CallEvent &Call,
264                                           DefinedOrUnknownSVal DV,
265                                           CheckerContext &C,
266                                           bool IsCheckedCast = false) {
267   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
268                     /*IsNonNullReturn=*/true, IsCheckedCast);
269 }
270
271 static void evalNonNullParamNullReturn(const CallEvent &Call,
272                                        DefinedOrUnknownSVal DV,
273                                        CheckerContext &C) {
274   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
275                     /*IsNonNullReturn=*/false);
276 }
277
278 static void evalNullParamNullReturn(const CallEvent &Call,
279                                     DefinedOrUnknownSVal DV,
280                                     CheckerContext &C) {
281   if (ProgramStateRef State = C.getState()->assume(DV, false))
282     C.addTransition(State->BindExpr(Call.getOriginExpr(),
283                                     C.getLocationContext(),
284                                     C.getSValBuilder().makeNull(), false),
285                     C.getNoteTag("Assuming null pointer is passed into cast",
286                                  /*IsPrunable=*/true));
287 }
288
289 void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
290                                 CheckerContext &C) const {
291   evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
292 }
293
294 void CastValueChecker::evalDynCast(const CallEvent &Call,
295                                    DefinedOrUnknownSVal DV,
296                                    CheckerContext &C) const {
297   evalNonNullParamNonNullReturn(Call, DV, C);
298   evalNonNullParamNullReturn(Call, DV, C);
299 }
300
301 void CastValueChecker::evalCastOrNull(const CallEvent &Call,
302                                       DefinedOrUnknownSVal DV,
303                                       CheckerContext &C) const {
304   evalNonNullParamNonNullReturn(Call, DV, C);
305   evalNullParamNullReturn(Call, DV, C);
306 }
307
308 void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
309                                          DefinedOrUnknownSVal DV,
310                                          CheckerContext &C) const {
311   evalNonNullParamNonNullReturn(Call, DV, C);
312   evalNonNullParamNullReturn(Call, DV, C);
313   evalNullParamNullReturn(Call, DV, C);
314 }
315
316 //===----------------------------------------------------------------------===//
317 // Evaluating castAs, getAs.
318 //===----------------------------------------------------------------------===//
319
320 static void evalZeroParamNonNullReturn(const CallEvent &Call,
321                                        DefinedOrUnknownSVal DV,
322                                        CheckerContext &C,
323                                        bool IsCheckedCast = false) {
324   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
325                     /*IsNonNullReturn=*/true, IsCheckedCast);
326 }
327
328 static void evalZeroParamNullReturn(const CallEvent &Call,
329                                     DefinedOrUnknownSVal DV,
330                                     CheckerContext &C) {
331   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
332                     /*IsNonNullReturn=*/false);
333 }
334
335 void CastValueChecker::evalCastAs(const CallEvent &Call,
336                                   DefinedOrUnknownSVal DV,
337                                   CheckerContext &C) const {
338   evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
339 }
340
341 void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
342                                  CheckerContext &C) const {
343   evalZeroParamNonNullReturn(Call, DV, C);
344   evalZeroParamNullReturn(Call, DV, C);
345 }
346
347 //===----------------------------------------------------------------------===//
348 // Evaluating isa, isa_and_nonnull.
349 //===----------------------------------------------------------------------===//
350
351 void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
352                                CheckerContext &C) const {
353   ProgramStateRef NonNullState, NullState;
354   std::tie(NonNullState, NullState) = C.getState()->assume(DV);
355
356   if (NonNullState) {
357     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
358     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
359   }
360
361   if (NullState) {
362     C.generateSink(NullState, C.getPredecessor());
363   }
364 }
365
366 void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call,
367                                          DefinedOrUnknownSVal DV,
368                                          CheckerContext &C) const {
369   ProgramStateRef NonNullState, NullState;
370   std::tie(NonNullState, NullState) = C.getState()->assume(DV);
371
372   if (NonNullState) {
373     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true);
374     addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false);
375   }
376
377   if (NullState) {
378     addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false);
379   }
380 }
381
382 //===----------------------------------------------------------------------===//
383 // Main logic to evaluate a call.
384 //===----------------------------------------------------------------------===//
385
386 bool CastValueChecker::evalCall(const CallEvent &Call,
387                                 CheckerContext &C) const {
388   const auto *Lookup = CDM.lookup(Call);
389   if (!Lookup)
390     return false;
391
392   const CastCheck &Check = Lookup->first;
393   CallKind Kind = Lookup->second;
394
395   Optional<DefinedOrUnknownSVal> DV;
396
397   switch (Kind) {
398   case CallKind::Function: {
399     // We only model casts from pointers to pointers or from references
400     // to references. Other casts are most likely specialized and we
401     // cannot model them.
402     QualType ParamT = Call.parameters()[0]->getType();
403     QualType ResultT = Call.getResultType();
404     if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&
405         !(ParamT->isReferenceType() && ResultT->isReferenceType()))
406       return false;
407
408     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
409     break;
410   }
411   case CallKind::InstanceOf: {
412     // We need to obtain the only template argument to determinte the type.
413     const FunctionDecl *FD = Call.getDecl()->getAsFunction();
414     if (!FD || !FD->getTemplateSpecializationArgs())
415       return false;
416
417     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
418     break;
419   }
420   case CallKind::Method:
421     const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
422     if (!InstanceCall)
423       return false;
424
425     DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
426     break;
427   }
428
429   if (!DV)
430     return false;
431
432   Check(this, Call, *DV, C);
433   return true;
434 }
435
436 void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
437                                         CheckerContext &C) const {
438   C.addTransition(removeDeadCasts(C.getState(), SR));
439 }
440
441 void ento::registerCastValueChecker(CheckerManager &Mgr) {
442   Mgr.registerChecker<CastValueChecker>();
443 }
444
445 bool ento::shouldRegisterCastValueChecker(const CheckerManager &mgr) {
446   return true;
447 }