194 lines · cpp
1//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- 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/// \file10/// This file defines the main class of MPI-Checker which serves as an entry11/// point. It is created once for each translation unit analysed.12/// The checker defines path-sensitive checks, to verify correct usage of the13/// MPI API.14///15//===----------------------------------------------------------------------===//16 17#include "MPIChecker.h"18#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"19#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"20 21namespace clang {22namespace ento {23namespace mpi {24 25void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,26 CheckerContext &Ctx) const {27 if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {28 return;29 }30 const MemRegion *const MR =31 PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();32 if (!MR)33 return;34 const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);35 36 // The region must be typed, in order to reason about it.37 if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))38 return;39 40 ProgramStateRef State = Ctx.getState();41 const Request *const Req = State->get<RequestMap>(MR);42 43 // double nonblocking detected44 if (Req && Req->CurrentState == Request::State::Nonblocking) {45 ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();46 BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,47 Ctx.getBugReporter());48 Ctx.addTransition(ErrorNode->getState(), ErrorNode);49 }50 // no error51 else {52 State = State->set<RequestMap>(MR, Request::State::Nonblocking);53 Ctx.addTransition(State);54 }55}56 57void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,58 CheckerContext &Ctx) const {59 if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))60 return;61 const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);62 if (!MR)63 return;64 const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);65 66 // The region must be typed, in order to reason about it.67 if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))68 return;69 70 llvm::SmallVector<const MemRegion *, 2> ReqRegions;71 allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);72 if (ReqRegions.empty())73 return;74 75 ProgramStateRef State = Ctx.getState();76 ExplodedNode *ErrorNode{nullptr};77 78 // Check all request regions used by the wait function.79 for (const auto &ReqRegion : ReqRegions) {80 const Request *const Req = State->get<RequestMap>(ReqRegion);81 State = State->set<RequestMap>(ReqRegion, Request::State::Wait);82 if (!Req) {83 if (!ErrorNode) {84 ErrorNode = Ctx.generateNonFatalErrorNode(State);85 State = ErrorNode->getState();86 }87 // A wait has no matching nonblocking call.88 BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,89 Ctx.getBugReporter());90 }91 }92 93 if (!ErrorNode) {94 Ctx.addTransition(State);95 } else {96 Ctx.addTransition(State, ErrorNode);97 }98}99 100void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,101 CheckerContext &Ctx) const {102 ProgramStateRef State = Ctx.getState();103 const auto &Requests = State->get<RequestMap>();104 if (Requests.isEmpty())105 return;106 107 ExplodedNode *ErrorNode{nullptr};108 109 auto ReqMap = State->get<RequestMap>();110 for (const auto &Req : ReqMap) {111 if (!SymReaper.isLiveRegion(Req.first)) {112 if (Req.second.CurrentState == Request::State::Nonblocking) {113 114 if (!ErrorNode) {115 ErrorNode = Ctx.generateNonFatalErrorNode(State);116 State = ErrorNode->getState();117 }118 BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,119 Ctx.getBugReporter());120 }121 State = State->remove<RequestMap>(Req.first);122 }123 }124 125 // Transition to update the state regarding removed requests.126 if (!ErrorNode) {127 Ctx.addTransition(State);128 } else {129 Ctx.addTransition(State, ErrorNode);130 }131}132 133const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {134 135 if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {136 return CE.getArgSVal(0).getAsRegion();137 } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {138 return CE.getArgSVal(1).getAsRegion();139 } else {140 return (const MemRegion *)nullptr;141 }142}143 144void MPIChecker::allRegionsUsedByWait(145 llvm::SmallVector<const MemRegion *, 2> &ReqRegions,146 const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {147 148 MemRegionManager &RegionManager = MR->getMemRegionManager();149 150 if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {151 const SubRegion *SuperRegion{nullptr};152 if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {153 SuperRegion = cast<SubRegion>(ER->getSuperRegion());154 }155 156 // A single request is passed to MPI_Waitall.157 if (!SuperRegion) {158 ReqRegions.push_back(MR);159 return;160 }161 162 DefinedOrUnknownSVal ElementCount = getDynamicElementCount(163 Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),164 CE.getArgExpr(1)->getType()->getPointeeType());165 const llvm::APSInt &ArrSize =166 ElementCount.castAs<nonloc::ConcreteInt>().getValue();167 168 for (size_t i = 0; i < ArrSize; ++i) {169 const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);170 171 const ElementRegion *const ER = RegionManager.getElementRegion(172 CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,173 Ctx.getASTContext());174 175 ReqRegions.push_back(ER->getAs<MemRegion>());176 }177 } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {178 ReqRegions.push_back(MR);179 }180}181 182} // end of namespace: mpi183} // end of namespace: ento184} // end of namespace: clang185 186// Registers the checker for static analysis.187void clang::ento::registerMPIChecker(CheckerManager &MGR) {188 MGR.registerChecker<clang::ento::mpi::MPIChecker>();189}190 191bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {192 return true;193}194