blob: 9cf1b0a020a2620b4a01f50a38ccbd486b4c9576 [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//
9// This defines CastValueChecker which models casts of custom RTTIs.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14#include "clang/StaticAnalyzer/Core/Checker.h"
15#include "clang/StaticAnalyzer/Core/CheckerManager.h"
16#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18#include "llvm/ADT/Optional.h"
Csaba Dabiscf229d52019-08-09 02:24:42 +000019#include <utility>
Csaba Dabis693936a2019-07-10 00:20:03 +000020
21using namespace clang;
22using namespace ento;
23
24namespace {
25class CastValueChecker : public Checker<eval::Call> {
Csaba Dabiscf229d52019-08-09 02:24:42 +000026 enum class CastKind { Function, Method };
27
Csaba Dabis693936a2019-07-10 00:20:03 +000028 using CastCheck =
29 std::function<void(const CastValueChecker *, const CallExpr *,
30 DefinedOrUnknownSVal, CheckerContext &)>;
31
Csaba Dabiscf229d52019-08-09 02:24:42 +000032 using CheckKindPair = std::pair<CastCheck, CastKind>;
33
Csaba Dabis693936a2019-07-10 00:20:03 +000034public:
Csaba Dabiscf229d52019-08-09 02:24:42 +000035 // We have five cases to evaluate a cast:
Csaba Dabis693936a2019-07-10 00:20:03 +000036 // 1) The parameter is non-null, the return value is non-null
37 // 2) The parameter is non-null, the return value is null
38 // 3) The parameter is null, the return value is null
Csaba Dabis693936a2019-07-10 00:20:03 +000039 // 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 +000040 //
41 // 4) castAs: has no parameter, the return value is non-null.
42 // 5) getAs: has no parameter, the return value is null or non-null.
Csaba Dabis693936a2019-07-10 00:20:03 +000043 bool evalCall(const CallEvent &Call, CheckerContext &C) const;
44
45private:
Csaba Dabiscf229d52019-08-09 02:24:42 +000046 // These are known in the LLVM project. The pairs are in the following form:
47 // {{{namespace, call}, argument-count}, {callback, kind}}
48 const CallDescriptionMap<CheckKindPair> CDM = {
49 {{{"llvm", "cast"}, 1},
50 {&CastValueChecker::evalCast, CastKind::Function}},
51 {{{"llvm", "dyn_cast"}, 1},
52 {&CastValueChecker::evalDynCast, CastKind::Function}},
53 {{{"llvm", "cast_or_null"}, 1},
54 {&CastValueChecker::evalCastOrNull, CastKind::Function}},
Csaba Dabis693936a2019-07-10 00:20:03 +000055 {{{"llvm", "dyn_cast_or_null"}, 1},
Csaba Dabiscf229d52019-08-09 02:24:42 +000056 {&CastValueChecker::evalDynCastOrNull, CastKind::Function}},
57 {{{"clang", "castAs"}, 0},
58 {&CastValueChecker::evalCastAs, CastKind::Method}},
59 {{{"clang", "getAs"}, 0},
60 {&CastValueChecker::evalGetAs, CastKind::Method}}};
Csaba Dabis693936a2019-07-10 00:20:03 +000061
Csaba Dabiscf229d52019-08-09 02:24:42 +000062 void evalCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000063 CheckerContext &C) const;
Csaba Dabiscf229d52019-08-09 02:24:42 +000064 void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000065 CheckerContext &C) const;
Csaba Dabiscf229d52019-08-09 02:24:42 +000066 void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000067 CheckerContext &C) const;
Csaba Dabiscf229d52019-08-09 02:24:42 +000068 void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +000069 CheckerContext &C) const;
Csaba Dabiscf229d52019-08-09 02:24:42 +000070 void evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
71 CheckerContext &C) const;
72 void evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
73 CheckerContext &C) const;
Csaba Dabis693936a2019-07-10 00:20:03 +000074};
75} // namespace
76
77static std::string getCastName(const Expr *Cast) {
Csaba Dabiscf229d52019-08-09 02:24:42 +000078 QualType Ty = Cast->getType();
79 if (const CXXRecordDecl *RD = Ty->getAsCXXRecordDecl())
80 return RD->getNameAsString();
81
82 return Ty->getPointeeCXXRecordDecl()->getNameAsString();
Csaba Dabis693936a2019-07-10 00:20:03 +000083}
84
Csaba Dabiscf229d52019-08-09 02:24:42 +000085static const NoteTag *getCastTag(bool IsNullReturn, const CallExpr *CE,
86 CheckerContext &C,
87 bool IsCheckedCast = false) {
88 Optional<std::string> CastFromName = (CE->getNumArgs() > 0)
89 ? getCastName(CE->getArg(0))
90 : Optional<std::string>();
Csaba Dabis693936a2019-07-10 00:20:03 +000091 std::string CastToName = getCastName(CE);
92
Csaba Dabiscf229d52019-08-09 02:24:42 +000093 return C.getNoteTag(
94 [CastFromName, CastToName, IsNullReturn,
95 IsCheckedCast](BugReport &) -> std::string {
Csaba Dabis693936a2019-07-10 00:20:03 +000096 SmallString<128> Msg;
97 llvm::raw_svector_ostream Out(Msg);
98
Csaba Dabiscf229d52019-08-09 02:24:42 +000099 Out << (!IsCheckedCast ? "Assuming dynamic cast " : "Checked cast ");
100 if (CastFromName)
101 Out << "from '" << *CastFromName << "' ";
102
103 Out << "to '" << CastToName << "' "
104 << (!IsNullReturn ? "succeeds" : "fails");
105
Csaba Dabis693936a2019-07-10 00:20:03 +0000106 return Out.str();
107 },
108 /*IsPrunable=*/true);
Csaba Dabiscf229d52019-08-09 02:24:42 +0000109}
Csaba Dabis693936a2019-07-10 00:20:03 +0000110
Csaba Dabiscf229d52019-08-09 02:24:42 +0000111static ProgramStateRef getState(bool IsNullReturn,
112 DefinedOrUnknownSVal ReturnDV,
113 const CallExpr *CE, ProgramStateRef State,
114 CheckerContext &C) {
115 return State->BindExpr(
116 CE, C.getLocationContext(),
117 IsNullReturn ? C.getSValBuilder().makeNull() : ReturnDV, false);
118}
119
120//===----------------------------------------------------------------------===//
121// Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null.
122//===----------------------------------------------------------------------===//
123
124static void evalNonNullParamNonNullReturn(const CallExpr *CE,
125 DefinedOrUnknownSVal DV,
126 CheckerContext &C,
127 bool IsCheckedCast = false) {
128 bool IsNullReturn = false;
129 if (ProgramStateRef State = C.getState()->assume(DV, true))
130 C.addTransition(getState(IsNullReturn, DV, CE, State, C),
131 getCastTag(IsNullReturn, CE, C, IsCheckedCast));
Csaba Dabis693936a2019-07-10 00:20:03 +0000132}
133
134static void evalNonNullParamNullReturn(const CallExpr *CE,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000135 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000136 CheckerContext &C) {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000137 bool IsNullReturn = true;
138 if (ProgramStateRef State = C.getState()->assume(DV, true))
139 C.addTransition(getState(IsNullReturn, DV, CE, State, C),
140 getCastTag(IsNullReturn, CE, C));
Csaba Dabis693936a2019-07-10 00:20:03 +0000141}
142
Csaba Dabiscf229d52019-08-09 02:24:42 +0000143static void evalNullParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000144 CheckerContext &C) {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000145 if (ProgramStateRef State = C.getState()->assume(DV, false))
146 C.addTransition(getState(/*IsNullReturn=*/true, DV, CE, State, C),
147 C.getNoteTag("Assuming null pointer is passed into cast",
148 /*IsPrunable=*/true));
Csaba Dabis693936a2019-07-10 00:20:03 +0000149}
150
Csaba Dabiscf229d52019-08-09 02:24:42 +0000151void CastValueChecker::evalCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000152 CheckerContext &C) const {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000153 evalNonNullParamNonNullReturn(CE, DV, C, /*IsCheckedCast=*/true);
Csaba Dabis693936a2019-07-10 00:20:03 +0000154}
155
Csaba Dabiscf229d52019-08-09 02:24:42 +0000156void CastValueChecker::evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000157 CheckerContext &C) const {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000158 evalNonNullParamNonNullReturn(CE, DV, C);
159 evalNonNullParamNullReturn(CE, DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000160}
161
162void CastValueChecker::evalCastOrNull(const CallExpr *CE,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000163 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000164 CheckerContext &C) const {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000165 evalNonNullParamNonNullReturn(CE, DV, C);
166 evalNullParamNullReturn(CE, DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000167}
168
169void CastValueChecker::evalDynCastOrNull(const CallExpr *CE,
Csaba Dabiscf229d52019-08-09 02:24:42 +0000170 DefinedOrUnknownSVal DV,
Csaba Dabis693936a2019-07-10 00:20:03 +0000171 CheckerContext &C) const {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000172 evalNonNullParamNonNullReturn(CE, DV, C);
173 evalNonNullParamNullReturn(CE, DV, C);
174 evalNullParamNullReturn(CE, DV, C);
175}
176
177//===----------------------------------------------------------------------===//
178// Evaluating castAs, getAs.
179//===----------------------------------------------------------------------===//
180
181static void evalZeroParamNonNullReturn(const CallExpr *CE,
182 DefinedOrUnknownSVal DV,
183 CheckerContext &C,
184 bool IsCheckedCast = false) {
185 bool IsNullReturn = false;
186 if (ProgramStateRef State = C.getState()->assume(DV, true))
187 C.addTransition(getState(IsNullReturn, DV, CE, C.getState(), C),
188 getCastTag(IsNullReturn, CE, C, IsCheckedCast));
189}
190
191static void evalZeroParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal DV,
192 CheckerContext &C) {
193 bool IsNullReturn = true;
194 if (ProgramStateRef State = C.getState()->assume(DV, true))
195 C.addTransition(getState(IsNullReturn, DV, CE, C.getState(), C),
196 getCastTag(IsNullReturn, CE, C));
197}
198
199void CastValueChecker::evalCastAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
200 CheckerContext &C) const {
201 evalZeroParamNonNullReturn(CE, DV, C, /*IsCheckedCast=*/true);
202}
203
204void CastValueChecker::evalGetAs(const CallExpr *CE, DefinedOrUnknownSVal DV,
205 CheckerContext &C) const {
206 evalZeroParamNonNullReturn(CE, DV, C);
207 evalZeroParamNullReturn(CE, DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000208}
209
210bool CastValueChecker::evalCall(const CallEvent &Call,
211 CheckerContext &C) const {
Csaba Dabiscf229d52019-08-09 02:24:42 +0000212 const auto *Lookup = CDM.lookup(Call);
213 if (!Lookup)
Csaba Dabis693936a2019-07-10 00:20:03 +0000214 return false;
215
Csaba Dabiscf229d52019-08-09 02:24:42 +0000216 // If we cannot obtain the call's class we cannot be sure how to model it.
217 QualType ResultTy = Call.getResultType();
218 if (!ResultTy->getPointeeCXXRecordDecl())
219 return false;
220
221 const CastCheck &Check = Lookup->first;
222 CastKind Kind = Lookup->second;
223
Csaba Dabis693936a2019-07-10 00:20:03 +0000224 const auto *CE = cast<CallExpr>(Call.getOriginExpr());
Csaba Dabiscf229d52019-08-09 02:24:42 +0000225 Optional<DefinedOrUnknownSVal> DV;
226
227 switch (Kind) {
228 case CastKind::Function: {
229 // If we cannot obtain the arg's class we cannot be sure how to model it.
230 QualType ArgTy = Call.parameters()[0]->getType();
231 if (!ArgTy->getAsCXXRecordDecl() && !ArgTy->getPointeeCXXRecordDecl())
232 return false;
233
234 DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
235 break;
236 }
237 case CastKind::Method:
238 // If we cannot obtain the 'InstanceCall' we cannot be sure how to model it.
239 const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
240 if (!InstanceCall)
241 return false;
242
243 DV = InstanceCall->getCXXThisVal().getAs<DefinedOrUnknownSVal>();
244 break;
245 }
246
247 if (!DV)
Csaba Dabis693936a2019-07-10 00:20:03 +0000248 return false;
249
Csaba Dabiscf229d52019-08-09 02:24:42 +0000250 Check(this, CE, *DV, C);
Csaba Dabis693936a2019-07-10 00:20:03 +0000251 return true;
252}
253
254void ento::registerCastValueChecker(CheckerManager &Mgr) {
255 Mgr.registerChecker<CastValueChecker>();
256}
257
258bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
259 return true;
260}