CastValueChecker.cpp revision 360660
1276789Sdim//===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===//
2276789Sdim//
3276789Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4276789Sdim// See https://llvm.org/LICENSE.txt for license information.
5276789Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6276789Sdim//
7276789Sdim//===----------------------------------------------------------------------===//
8276789Sdim//
9276789Sdim// This defines CastValueChecker which models casts of custom RTTIs.
10276789Sdim//
11276789Sdim//===----------------------------------------------------------------------===//
12276789Sdim
13276789Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
14276789Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
15276789Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
16276789Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
17276789Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18276789Sdim#include "llvm/ADT/Optional.h"
19276789Sdim
20276789Sdimusing namespace clang;
21276789Sdimusing namespace ento;
22276789Sdim
23276789Sdimnamespace {
24276789Sdimclass CastValueChecker : public Checker<eval::Call> {
25276789Sdim  using CastCheck =
26276789Sdim      std::function<void(const CastValueChecker *, const CallExpr *,
27276789Sdim                         DefinedOrUnknownSVal, CheckerContext &)>;
28276789Sdim
29276789Sdimpublic:
30276789Sdim  // We have three cases to evaluate a cast:
31276789Sdim  // 1) The parameter is non-null, the return value is non-null
32276789Sdim  // 2) The parameter is non-null, the return value is null
33276789Sdim  // 3) The parameter is null, the return value is null
34276789Sdim  //
35276789Sdim  // cast: 1;  dyn_cast: 1, 2;  cast_or_null: 1, 3;  dyn_cast_or_null: 1, 2, 3.
36276789Sdim  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
37276789Sdim
38276789Sdimprivate:
39276789Sdim  // These are known in the LLVM project.
40276789Sdim  const CallDescriptionMap<CastCheck> CDM = {
41276789Sdim      {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast},
42276789Sdim      {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast},
43276789Sdim      {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull},
44276789Sdim      {{{"llvm", "dyn_cast_or_null"}, 1},
45276789Sdim       &CastValueChecker::evalDynCastOrNull}};
46276789Sdim
47276789Sdim  void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
48276789Sdim                CheckerContext &C) const;
49276789Sdim  void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
50276789Sdim                   CheckerContext &C) const;
51276789Sdim  void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
52276789Sdim                      CheckerContext &C) const;
53276789Sdim  void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
54276789Sdim                         CheckerContext &C) const;
55276789Sdim};
56276789Sdim} // namespace
57276789Sdim
58276789Sdimstatic std::string getCastName(const Expr *Cast) {
59276789Sdim  return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString();
60276789Sdim}
61276789Sdim
62276789Sdimstatic void evalNonNullParamNonNullReturn(const CallExpr *CE,
63276789Sdim                                          DefinedOrUnknownSVal ParamDV,
64276789Sdim                                          CheckerContext &C) {
65276789Sdim  ProgramStateRef State = C.getState()->assume(ParamDV, true);
66276789Sdim  if (!State)
67276789Sdim    return;
68276789Sdim
69276789Sdim  State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false);
70276789Sdim
71276789Sdim  std::string CastFromName = getCastName(CE->getArg(0));
72276789Sdim  std::string CastToName = getCastName(CE);
73276789Sdim
74276789Sdim  const NoteTag *CastTag = C.getNoteTag(
75276789Sdim      [CastFromName, CastToName](BugReport &) -> std::string {
76276789Sdim        SmallString<128> Msg;
77276789Sdim        llvm::raw_svector_ostream Out(Msg);
78276789Sdim
79276789Sdim        Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
80276789Sdim            << CastToName << "' succeeds";
81276789Sdim        return Out.str();
82276789Sdim      },
83276789Sdim      /*IsPrunable=*/true);
84276789Sdim
85276789Sdim  C.addTransition(State, CastTag);
86276789Sdim}
87276789Sdim
88276789Sdimstatic void evalNonNullParamNullReturn(const CallExpr *CE,
89276789Sdim                                       DefinedOrUnknownSVal ParamDV,
90276789Sdim                                       CheckerContext &C) {
91276789Sdim  ProgramStateRef State = C.getState()->assume(ParamDV, true);
92276789Sdim  if (!State)
93276789Sdim    return;
94276789Sdim
95276789Sdim  State = State->BindExpr(CE, C.getLocationContext(),
96276789Sdim                          C.getSValBuilder().makeNull(), false);
97276789Sdim
98276789Sdim  std::string CastFromName = getCastName(CE->getArg(0));
99276789Sdim  std::string CastToName = getCastName(CE);
100276789Sdim
101276789Sdim  const NoteTag *CastTag = C.getNoteTag(
102276789Sdim      [CastFromName, CastToName](BugReport &) -> std::string {
103276789Sdim        SmallString<128> Msg;
104276789Sdim        llvm::raw_svector_ostream Out(Msg);
105276789Sdim
106276789Sdim        Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
107276789Sdim            << CastToName << "' fails";
108276789Sdim        return Out.str();
109276789Sdim      },
110276789Sdim      /*IsPrunable=*/true);
111276789Sdim
112276789Sdim  C.addTransition(State, CastTag);
113276789Sdim}
114276789Sdim
115276789Sdimstatic void evalNullParamNullReturn(const CallExpr *CE,
116276789Sdim                                    DefinedOrUnknownSVal ParamDV,
117276789Sdim                                    CheckerContext &C) {
118276789Sdim  ProgramStateRef State = C.getState()->assume(ParamDV, false);
119276789Sdim  if (!State)
120276789Sdim    return;
121276789Sdim
122276789Sdim  State = State->BindExpr(CE, C.getLocationContext(),
123276789Sdim                          C.getSValBuilder().makeNull(), false);
124276789Sdim
125276789Sdim  const NoteTag *CastTag =
126276789Sdim      C.getNoteTag("Assuming null pointer is passed into cast",
127276789Sdim                   /*IsPrunable=*/true);
128276789Sdim
129276789Sdim  C.addTransition(State, CastTag);
130276789Sdim}
131276789Sdim
132276789Sdimvoid CastValueChecker::evalCast(const CallExpr *CE,
133276789Sdim                                DefinedOrUnknownSVal ParamDV,
134276789Sdim                                CheckerContext &C) const {
135276789Sdim  evalNonNullParamNonNullReturn(CE, ParamDV, C);
136276789Sdim}
137276789Sdim
138276789Sdimvoid CastValueChecker::evalDynCast(const CallExpr *CE,
139276789Sdim                                   DefinedOrUnknownSVal ParamDV,
140276789Sdim                                   CheckerContext &C) const {
141276789Sdim  evalNonNullParamNonNullReturn(CE, ParamDV, C);
142276789Sdim  evalNonNullParamNullReturn(CE, ParamDV, C);
143276789Sdim}
144276789Sdim
145276789Sdimvoid CastValueChecker::evalCastOrNull(const CallExpr *CE,
146276789Sdim                                      DefinedOrUnknownSVal ParamDV,
147276789Sdim                                      CheckerContext &C) const {
148276789Sdim  evalNonNullParamNonNullReturn(CE, ParamDV, C);
149276789Sdim  evalNullParamNullReturn(CE, ParamDV, C);
150276789Sdim}
151276789Sdim
152276789Sdimvoid CastValueChecker::evalDynCastOrNull(const CallExpr *CE,
153276789Sdim                                         DefinedOrUnknownSVal ParamDV,
154276789Sdim                                         CheckerContext &C) const {
155276789Sdim  evalNonNullParamNonNullReturn(CE, ParamDV, C);
156276789Sdim  evalNonNullParamNullReturn(CE, ParamDV, C);
157276789Sdim  evalNullParamNullReturn(CE, ParamDV, C);
158276789Sdim}
159276789Sdim
160276789Sdimbool CastValueChecker::evalCall(const CallEvent &Call,
161276789Sdim                                CheckerContext &C) const {
162309124Sdim  const CastCheck *Check = CDM.lookup(Call);
163309124Sdim  if (!Check)
164309124Sdim    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