CastValueChecker.cpp revision 351350
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
20using namespace clang;
21using namespace ento;
22
23namespace {
24class CastValueChecker : public Checker<eval::Call> {
25  using CastCheck =
26      std::function<void(const CastValueChecker *, const CallExpr *,
27                         DefinedOrUnknownSVal, CheckerContext &)>;
28
29public:
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
38private:
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
58static std::string getCastName(const Expr *Cast) {
59  return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString();
60}
61
62static 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
88static 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
115static 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
132void CastValueChecker::evalCast(const CallExpr *CE,
133                                DefinedOrUnknownSVal ParamDV,
134                                CheckerContext &C) const {
135  evalNonNullParamNonNullReturn(CE, ParamDV, C);
136}
137
138void CastValueChecker::evalDynCast(const CallExpr *CE,
139                                   DefinedOrUnknownSVal ParamDV,
140                                   CheckerContext &C) const {
141  evalNonNullParamNonNullReturn(CE, ParamDV, C);
142  evalNonNullParamNullReturn(CE, ParamDV, C);
143}
144
145void CastValueChecker::evalCastOrNull(const CallExpr *CE,
146                                      DefinedOrUnknownSVal ParamDV,
147                                      CheckerContext &C) const {
148  evalNonNullParamNonNullReturn(CE, ParamDV, C);
149  evalNullParamNullReturn(CE, ParamDV, C);
150}
151
152void 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
160bool 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
184void ento::registerCastValueChecker(CheckerManager &Mgr) {
185  Mgr.registerChecker<CastValueChecker>();
186}
187
188bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
189  return true;
190}
191