clang  9.0.0
CastValueChecker.cpp
Go to the documentation of this file.
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 
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 
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 }
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:2673
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
LineState State
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
static void evalNullParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, CheckerContext &C)
static void evalNonNullParamNonNullReturn(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, CheckerContext &C)
This represents one expression.
Definition: Expr.h:108
const CXXRecordDecl * getPointeeCXXRecordDecl() const
If this is a pointer or reference to a RecordType, return the CXXRecordDecl that the type refers to...
Definition: Type.cpp:1621
static void evalNonNullParamNullReturn(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, CheckerContext &C)
QualType getType() const
Definition: Expr.h:137
static std::string getCastName(const Expr *Cast)
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:291
Dataflow Directional Tag Classes.
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2516
An immutable map from CallDescriptions to arbitrary data.
Definition: CallEvent.h:1095