]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
Fix a memory leak in if_delgroups() introduced in r334118.
[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 //===----------------------------------------------------------------------===//
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"
19
20 using namespace clang;
21 using namespace ento;
22
23 namespace {
24 class CastValueChecker : public Checker<eval::Call> {
25   using CastCheck =
26       std::function<void(const CastValueChecker *, const CallExpr *,
27                          DefinedOrUnknownSVal, CheckerContext &)>;
28
29 public:
30   // We have three cases to evaluate a cast:
31   // 1) The parameter is non-null, the return value is non-null
32   // 2) The parameter is non-null, the return value is null
33   // 3) The parameter is null, the return value is null
34   //
35   // cast: 1;  dyn_cast: 1, 2;  cast_or_null: 1, 3;  dyn_cast_or_null: 1, 2, 3.
36   bool evalCall(const CallEvent &Call, CheckerContext &C) const;
37
38 private:
39   // These are known in the LLVM project.
40   const CallDescriptionMap<CastCheck> CDM = {
41       {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast},
42       {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast},
43       {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull},
44       {{{"llvm", "dyn_cast_or_null"}, 1},
45        &CastValueChecker::evalDynCastOrNull}};
46
47   void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
48                 CheckerContext &C) const;
49   void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
50                    CheckerContext &C) const;
51   void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
52                       CheckerContext &C) const;
53   void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
54                          CheckerContext &C) const;
55 };
56 } // namespace
57
58 static std::string getCastName(const Expr *Cast) {
59   return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString();
60 }
61
62 static void evalNonNullParamNonNullReturn(const CallExpr *CE,
63                                           DefinedOrUnknownSVal ParamDV,
64                                           CheckerContext &C) {
65   ProgramStateRef State = C.getState()->assume(ParamDV, true);
66   if (!State)
67     return;
68
69   State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false);
70
71   std::string CastFromName = getCastName(CE->getArg(0));
72   std::string CastToName = getCastName(CE);
73
74   const NoteTag *CastTag = C.getNoteTag(
75       [CastFromName, CastToName](BugReport &) -> std::string {
76         SmallString<128> Msg;
77         llvm::raw_svector_ostream Out(Msg);
78
79         Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
80             << CastToName << "' succeeds";
81         return Out.str();
82       },
83       /*IsPrunable=*/true);
84
85   C.addTransition(State, CastTag);
86 }
87
88 static void evalNonNullParamNullReturn(const CallExpr *CE,
89                                        DefinedOrUnknownSVal ParamDV,
90                                        CheckerContext &C) {
91   ProgramStateRef State = C.getState()->assume(ParamDV, true);
92   if (!State)
93     return;
94
95   State = State->BindExpr(CE, C.getLocationContext(),
96                           C.getSValBuilder().makeNull(), false);
97
98   std::string CastFromName = getCastName(CE->getArg(0));
99   std::string CastToName = getCastName(CE);
100
101   const NoteTag *CastTag = C.getNoteTag(
102       [CastFromName, CastToName](BugReport &) -> std::string {
103         SmallString<128> Msg;
104         llvm::raw_svector_ostream Out(Msg);
105
106         Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
107             << CastToName << "' fails";
108         return Out.str();
109       },
110       /*IsPrunable=*/true);
111
112   C.addTransition(State, CastTag);
113 }
114
115 static void evalNullParamNullReturn(const CallExpr *CE,
116                                     DefinedOrUnknownSVal ParamDV,
117                                     CheckerContext &C) {
118   ProgramStateRef State = C.getState()->assume(ParamDV, false);
119   if (!State)
120     return;
121
122   State = State->BindExpr(CE, C.getLocationContext(),
123                           C.getSValBuilder().makeNull(), false);
124
125   const NoteTag *CastTag =
126       C.getNoteTag("Assuming null pointer is passed into cast",
127                    /*IsPrunable=*/true);
128
129   C.addTransition(State, CastTag);
130 }
131
132 void CastValueChecker::evalCast(const CallExpr *CE,
133                                 DefinedOrUnknownSVal ParamDV,
134                                 CheckerContext &C) const {
135   evalNonNullParamNonNullReturn(CE, ParamDV, C);
136 }
137
138 void CastValueChecker::evalDynCast(const CallExpr *CE,
139                                    DefinedOrUnknownSVal ParamDV,
140                                    CheckerContext &C) const {
141   evalNonNullParamNonNullReturn(CE, ParamDV, C);
142   evalNonNullParamNullReturn(CE, ParamDV, C);
143 }
144
145 void CastValueChecker::evalCastOrNull(const CallExpr *CE,
146                                       DefinedOrUnknownSVal ParamDV,
147                                       CheckerContext &C) const {
148   evalNonNullParamNonNullReturn(CE, ParamDV, C);
149   evalNullParamNullReturn(CE, ParamDV, C);
150 }
151
152 void CastValueChecker::evalDynCastOrNull(const CallExpr *CE,
153                                          DefinedOrUnknownSVal ParamDV,
154                                          CheckerContext &C) const {
155   evalNonNullParamNonNullReturn(CE, ParamDV, C);
156   evalNonNullParamNullReturn(CE, ParamDV, C);
157   evalNullParamNullReturn(CE, ParamDV, C);
158 }
159
160 bool CastValueChecker::evalCall(const CallEvent &Call,
161                                 CheckerContext &C) const {
162   const CastCheck *Check = CDM.lookup(Call);
163   if (!Check)
164     return false;
165
166   const auto *CE = cast<CallExpr>(Call.getOriginExpr());
167   if (!CE)
168     return false;
169
170   // If we cannot obtain both of the classes we cannot be sure how to model it.
171   if (!CE->getType()->getPointeeCXXRecordDecl() ||
172       !CE->getArg(0)->getType()->getPointeeCXXRecordDecl())
173     return false;
174
175   SVal ParamV = Call.getArgSVal(0);
176   auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>();
177   if (!ParamDV)
178     return false;
179
180   (*Check)(this, CE, *ParamDV, C);
181   return true;
182 }
183
184 void ento::registerCastValueChecker(CheckerManager &Mgr) {
185   Mgr.registerChecker<CastValueChecker>();
186 }
187
188 bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
189   return true;
190 }