brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.7 KiB · 6c27f58 Raw
230 lines · cpp
1//=======- VirtualCallChecker.cpp --------------------------------*- 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-exception6//7//===----------------------------------------------------------------------===//8//9//  This file defines a checker that checks virtual method calls during10//  construction or destruction of C++ objects.11//12//===----------------------------------------------------------------------===//13 14#include "clang/AST/Attr.h"15#include "clang/AST/DeclCXX.h"16#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"17#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"18#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"19#include "clang/StaticAnalyzer/Core/Checker.h"20#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"21#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"22#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"23#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"24 25using namespace clang;26using namespace ento;27 28namespace {29enum class ObjectState : bool { CtorCalled, DtorCalled };30} // end namespace31  // FIXME: Ascending over StackFrameContext maybe another method.32 33namespace llvm {34template <> struct FoldingSetTrait<ObjectState> {35  static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {36    ID.AddInteger(static_cast<int>(X));37  }38};39} // end namespace llvm40 41namespace {42class VirtualCallChecker43    : public CheckerFamily<check::BeginFunction, check::EndFunction,44                           check::PreCall> {45public:46  CheckerFrontendWithBugType PureChecker{"Pure virtual method call",47                                         categories::CXXObjectLifecycle};48  CheckerFrontendWithBugType ImpureChecker{49      "Unexpected loss of virtual dispatch", categories::CXXObjectLifecycle};50 51  bool ShowFixIts = false;52 53  void checkBeginFunction(CheckerContext &C) const;54  void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;55  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;56 57  /// Identifies this checker family for debugging purposes.58  StringRef getDebugTag() const override { return "VirtualCallChecker"; }59 60private:61  void registerCtorDtorCallInState(bool IsBeginFunction,62                                   CheckerContext &C) const;63};64} // end namespace65 66// GDM (generic data map) to the memregion of this for the ctor and dtor.67REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)68 69// The function to check if a callexpr is a virtual method call.70static bool isVirtualCall(const CallExpr *CE) {71  bool CallIsNonVirtual = false;72 73  if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {74    // The member access is fully qualified (i.e., X::F).75    // Treat this as a non-virtual call and do not warn.76    if (CME->getQualifier())77      CallIsNonVirtual = true;78 79    if (const Expr *Base = CME->getBase()) {80      // The most derived class is marked final.81      if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())82        CallIsNonVirtual = true;83    }84  }85 86  const CXXMethodDecl *MD =87      dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());88  if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&89      !MD->getParent()->hasAttr<FinalAttr>())90    return true;91  return false;92}93 94// The BeginFunction callback when enter a constructor or a destructor.95void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {96  registerCtorDtorCallInState(true, C);97}98 99// The EndFunction callback when leave a constructor or a destructor.100void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,101                                          CheckerContext &C) const {102  registerCtorDtorCallInState(false, C);103}104 105void VirtualCallChecker::checkPreCall(const CallEvent &Call,106                                      CheckerContext &C) const {107  const auto MC = dyn_cast<CXXMemberCall>(&Call);108  if (!MC)109    return;110 111  const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());112  if (!MD)113    return;114 115  ProgramStateRef State = C.getState();116  // Member calls are always represented by a call-expression.117  const auto *CE = cast<CallExpr>(Call.getOriginExpr());118  if (!isVirtualCall(CE))119    return;120 121  const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();122  const ObjectState *ObState = State->get<CtorDtorMap>(Reg);123  if (!ObState)124    return;125 126  bool IsPure = MD->isPureVirtual();127 128  // At this point we're sure that we're calling a virtual method129  // during construction or destruction, so we'll emit a report.130  SmallString<128> Msg;131  llvm::raw_svector_ostream OS(Msg);132  OS << "Call to ";133  if (IsPure)134    OS << "pure ";135  OS << "virtual method '" << MD->getParent()->getDeclName()136     << "::" << MD->getDeclName() << "' during ";137  if (*ObState == ObjectState::CtorCalled)138    OS << "construction ";139  else140    OS << "destruction ";141  if (IsPure)142    OS << "has undefined behavior";143  else144    OS << "bypasses virtual dispatch";145 146  ExplodedNode *N =147      IsPure ? C.generateErrorNode() : C.generateNonFatalErrorNode();148  if (!N)149    return;150 151  const CheckerFrontendWithBugType &Part = IsPure ? PureChecker : ImpureChecker;152 153  if (!Part.isEnabled()) {154    // The respective check is disabled.155    return;156  }157 158  auto Report = std::make_unique<PathSensitiveBugReport>(Part, OS.str(), N);159 160  if (ShowFixIts && !IsPure) {161    // FIXME: These hints are valid only when the virtual call is made162    // directly from the constructor/destructor. Otherwise the dispatch163    // will work just fine from other callees, and the fix may break164    // the otherwise correct program.165    FixItHint Fixit = FixItHint::CreateInsertion(166        CE->getBeginLoc(), MD->getParent()->getNameAsString() + "::");167    Report->addFixItHint(Fixit);168  }169 170  C.emitReport(std::move(Report));171}172 173void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,174                                                     CheckerContext &C) const {175  const auto *LCtx = C.getLocationContext();176  const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());177  if (!MD)178    return;179 180  ProgramStateRef State = C.getState();181  auto &SVB = C.getSValBuilder();182 183  // Enter a constructor, set the corresponding memregion be true.184  if (isa<CXXConstructorDecl>(MD)) {185    auto ThiSVal =186        State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));187    const MemRegion *Reg = ThiSVal.getAsRegion();188    if (IsBeginFunction)189      State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);190    else191      State = State->remove<CtorDtorMap>(Reg);192 193    C.addTransition(State);194    return;195  }196 197  // Enter a Destructor, set the corresponding memregion be true.198  if (isa<CXXDestructorDecl>(MD)) {199    auto ThiSVal =200        State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));201    const MemRegion *Reg = ThiSVal.getAsRegion();202    if (IsBeginFunction)203      State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);204    else205      State = State->remove<CtorDtorMap>(Reg);206 207    C.addTransition(State);208    return;209  }210}211 212void ento::registerPureVirtualCallChecker(CheckerManager &Mgr) {213  Mgr.getChecker<VirtualCallChecker>()->PureChecker.enable(Mgr);214}215 216bool ento::shouldRegisterPureVirtualCallChecker(const CheckerManager &Mgr) {217  return Mgr.getLangOpts().CPlusPlus;218}219 220void ento::registerVirtualCallChecker(CheckerManager &Mgr) {221  auto *Chk = Mgr.getChecker<VirtualCallChecker>();222  Chk->ImpureChecker.enable(Mgr);223  Chk->ShowFixIts = Mgr.getAnalyzerOptions().getCheckerBooleanOption(224      Mgr.getCurrentCheckerName(), "ShowFixIts");225}226 227bool ento::shouldRegisterVirtualCallChecker(const CheckerManager &Mgr) {228  return Mgr.getLangOpts().CPlusPlus;229}230