xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp (revision 22dc44ff896a34a94c7ed6d3bf7b577b98e34fbd)
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/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"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
25 #include "llvm/ADT/Optional.h"
26 #include <utility>
27 
28 using namespace clang;
29 using namespace ento;
30 
31 namespace {
32 class CastValueChecker : public Checker<eval::Call> {
33   enum class CallKind { Function, Method };
34 
35   using CastCheck =
36       std::function<void(const CastValueChecker *, const CallEvent &Call,
37                          DefinedOrUnknownSVal, CheckerContext &)>;
38 
39 public:
40   // We have five cases to evaluate a cast:
41   // 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.
44   // cast: 1;  dyn_cast: 1, 2;  cast_or_null: 1, 3;  dyn_cast_or_null: 1, 2, 3.
45   //
46   // 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.
48   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
49   void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
50 
51 private:
52   // These are known in the LLVM project. The pairs are in the following form:
53   // {{{namespace, call}, argument-count}, {callback, kind}}
54   const CallDescriptionMap<std::pair<CastCheck, CallKind>> CDM = {
55       {{{"llvm", "cast"}, 1},
56        {&CastValueChecker::evalCast, CallKind::Function}},
57       {{{"llvm", "dyn_cast"}, 1},
58        {&CastValueChecker::evalDynCast, CallKind::Function}},
59       {{{"llvm", "cast_or_null"}, 1},
60        {&CastValueChecker::evalCastOrNull, CallKind::Function}},
61       {{{"llvm", "dyn_cast_or_null"}, 1},
62        {&CastValueChecker::evalDynCastOrNull, CallKind::Function}},
63       {{{"clang", "castAs"}, 0},
64        {&CastValueChecker::evalCastAs, CallKind::Method}},
65       {{{"clang", "getAs"}, 0},
66        {&CastValueChecker::evalGetAs, CallKind::Method}}};
67 
68   void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
69                 CheckerContext &C) const;
70   void evalDynCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
71                    CheckerContext &C) const;
72   void evalCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
73                       CheckerContext &C) const;
74   void evalDynCastOrNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
75                          CheckerContext &C) const;
76   void evalCastAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
77                   CheckerContext &C) const;
78   void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
79                  CheckerContext &C) const;
80 };
81 } // namespace
82 
83 static QualType getRecordType(QualType Ty) {
84   Ty = Ty.getCanonicalType();
85 
86   if (Ty->isPointerType())
87     Ty = Ty->getPointeeType();
88 
89   if (Ty->isReferenceType())
90     Ty = Ty.getNonReferenceType();
91 
92   return Ty.getUnqualifiedType();
93 }
94 
95 static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
96                              bool CastSucceeds) {
97   if (!CastInfo)
98     return false;
99 
100   return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
101 }
102 
103 static 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();
111 
112   return C.getNoteTag(
113       [=]() -> std::string {
114         SmallString<128> Msg;
115         llvm::raw_svector_ostream Out(Msg);
116 
117         if (!IsKnownCast)
118           Out << "Assuming ";
119 
120         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             << '\'';
131 
132         return Out.str();
133       },
134       /*IsPrunable=*/true);
135 }
136 
137 //===----------------------------------------------------------------------===//
138 // Main logic to evaluate a cast.
139 //===----------------------------------------------------------------------===//
140 
141 static 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));
190 }
191 
192 //===----------------------------------------------------------------------===//
193 // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
194 //===----------------------------------------------------------------------===//
195 
196 static void evalNonNullParamNonNullReturn(const CallEvent &Call,
197                                           DefinedOrUnknownSVal DV,
198                                           CheckerContext &C,
199                                           bool IsCheckedCast = false) {
200   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
201                     /*IsNonNullReturn=*/true, IsCheckedCast);
202 }
203 
204 static void evalNonNullParamNullReturn(const CallEvent &Call,
205                                        DefinedOrUnknownSVal DV,
206                                        CheckerContext &C) {
207   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
208                     /*IsNonNullReturn=*/false);
209 }
210 
211 static void evalNullParamNullReturn(const CallEvent &Call,
212                                     DefinedOrUnknownSVal DV,
213                                     CheckerContext &C) {
214   if (ProgramStateRef State = C.getState()->assume(DV, false))
215     C.addTransition(State->BindExpr(Call.getOriginExpr(),
216                                     C.getLocationContext(),
217                                     C.getSValBuilder().makeNull(), false),
218                     C.getNoteTag("Assuming null pointer is passed into cast",
219                                  /*IsPrunable=*/true));
220 }
221 
222 void CastValueChecker::evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
223                                 CheckerContext &C) const {
224   evalNonNullParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
225 }
226 
227 void CastValueChecker::evalDynCast(const CallEvent &Call,
228                                    DefinedOrUnknownSVal DV,
229                                    CheckerContext &C) const {
230   evalNonNullParamNonNullReturn(Call, DV, C);
231   evalNonNullParamNullReturn(Call, DV, C);
232 }
233 
234 void CastValueChecker::evalCastOrNull(const CallEvent &Call,
235                                       DefinedOrUnknownSVal DV,
236                                       CheckerContext &C) const {
237   evalNonNullParamNonNullReturn(Call, DV, C);
238   evalNullParamNullReturn(Call, DV, C);
239 }
240 
241 void CastValueChecker::evalDynCastOrNull(const CallEvent &Call,
242                                          DefinedOrUnknownSVal DV,
243                                          CheckerContext &C) const {
244   evalNonNullParamNonNullReturn(Call, DV, C);
245   evalNonNullParamNullReturn(Call, DV, C);
246   evalNullParamNullReturn(Call, DV, C);
247 }
248 
249 //===----------------------------------------------------------------------===//
250 // Evaluating castAs, getAs.
251 //===----------------------------------------------------------------------===//
252 
253 static void evalZeroParamNonNullReturn(const CallEvent &Call,
254                                        DefinedOrUnknownSVal DV,
255                                        CheckerContext &C,
256                                        bool IsCheckedCast = false) {
257   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
258                     /*IsNonNullReturn=*/true, IsCheckedCast);
259 }
260 
261 static void evalZeroParamNullReturn(const CallEvent &Call,
262                                     DefinedOrUnknownSVal DV,
263                                     CheckerContext &C) {
264   addCastTransition(Call, DV, C, /*IsNonNullParam=*/true,
265                     /*IsNonNullReturn=*/false);
266 }
267 
268 void CastValueChecker::evalCastAs(const CallEvent &Call,
269                                   DefinedOrUnknownSVal DV,
270                                   CheckerContext &C) const {
271   evalZeroParamNonNullReturn(Call, DV, C, /*IsCheckedCast=*/true);
272 }
273 
274 void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
275                                  CheckerContext &C) const {
276   evalZeroParamNonNullReturn(Call, DV, C);
277   evalZeroParamNullReturn(Call, DV, C);
278 }
279 
280 //===----------------------------------------------------------------------===//
281 // Main logic to evaluate a call.
282 //===----------------------------------------------------------------------===//
283 
284 bool CastValueChecker::evalCall(const CallEvent &Call,
285                                 CheckerContext &C) const {
286   const auto *Lookup = CDM.lookup(Call);
287   if (!Lookup)
288     return false;
289 
290   // We need to obtain the record type of the call's result to model it.
291   if (!getRecordType(Call.getResultType())->isRecordType())
292     return false;
293 
294   const CastCheck &Check = Lookup->first;
295   CallKind Kind = Lookup->second;
296   Optional<DefinedOrUnknownSVal> DV;
297 
298   switch (Kind) {
299   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())
302       return false;
303 
304     DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
305     break;
306   }
307   case CallKind::Method:
308     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)
317     return false;
318 
319   Check(this, Call, *DV, C);
320   return true;
321 }
322 
323 void CastValueChecker::checkDeadSymbols(SymbolReaper &SR,
324                                         CheckerContext &C) const {
325   C.addTransition(removeDeadCasts(C.getState(), SR));
326 }
327 
328 void ento::registerCastValueChecker(CheckerManager &Mgr) {
329   Mgr.registerChecker<CastValueChecker>();
330 }
331 
332 bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
333   return true;
334 }
335