1303233Sdim//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// 2303233Sdim// 3353358Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353358Sdim// See https://llvm.org/LICENSE.txt for license information. 5353358Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6303233Sdim// 7303233Sdim//===----------------------------------------------------------------------===// 8303233Sdim/// 9303233Sdim/// \file 10303233Sdim/// This file defines the main class of MPI-Checker which serves as an entry 11303233Sdim/// point. It is created once for each translation unit analysed. 12303233Sdim/// The checker defines path-sensitive checks, to verify correct usage of the 13303233Sdim/// MPI API. 14303233Sdim/// 15303233Sdim//===----------------------------------------------------------------------===// 16303233Sdim 17303233Sdim#include "MPIChecker.h" 18344779Sdim#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 19303233Sdim 20303233Sdimnamespace clang { 21303233Sdimnamespace ento { 22303233Sdimnamespace mpi { 23303233Sdim 24303233Sdimvoid MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, 25303233Sdim CheckerContext &Ctx) const { 26303233Sdim if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { 27303233Sdim return; 28303233Sdim } 29303233Sdim const MemRegion *const MR = 30303233Sdim PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); 31303233Sdim if (!MR) 32303233Sdim return; 33303233Sdim const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); 34303233Sdim 35303233Sdim // The region must be typed, in order to reason about it. 36303233Sdim if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) 37303233Sdim return; 38303233Sdim 39303233Sdim ProgramStateRef State = Ctx.getState(); 40303233Sdim const Request *const Req = State->get<RequestMap>(MR); 41303233Sdim 42303233Sdim // double nonblocking detected 43303233Sdim if (Req && Req->CurrentState == Request::State::Nonblocking) { 44303233Sdim ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); 45314564Sdim BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, 46314564Sdim Ctx.getBugReporter()); 47303233Sdim Ctx.addTransition(ErrorNode->getState(), ErrorNode); 48303233Sdim } 49303233Sdim // no error 50303233Sdim else { 51303233Sdim State = State->set<RequestMap>(MR, Request::State::Nonblocking); 52303233Sdim Ctx.addTransition(State); 53303233Sdim } 54303233Sdim} 55303233Sdim 56303233Sdimvoid MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, 57303233Sdim CheckerContext &Ctx) const { 58303233Sdim if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) 59303233Sdim return; 60303233Sdim const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); 61303233Sdim if (!MR) 62303233Sdim return; 63303233Sdim const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); 64303233Sdim 65303233Sdim // The region must be typed, in order to reason about it. 66303233Sdim if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) 67303233Sdim return; 68303233Sdim 69303233Sdim llvm::SmallVector<const MemRegion *, 2> ReqRegions; 70303233Sdim allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); 71303233Sdim if (ReqRegions.empty()) 72303233Sdim return; 73303233Sdim 74303233Sdim ProgramStateRef State = Ctx.getState(); 75303233Sdim static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); 76303233Sdim ExplodedNode *ErrorNode{nullptr}; 77303233Sdim 78303233Sdim // Check all request regions used by the wait function. 79303233Sdim for (const auto &ReqRegion : ReqRegions) { 80303233Sdim const Request *const Req = State->get<RequestMap>(ReqRegion); 81303233Sdim State = State->set<RequestMap>(ReqRegion, Request::State::Wait); 82303233Sdim if (!Req) { 83303233Sdim if (!ErrorNode) { 84303233Sdim ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); 85303233Sdim State = ErrorNode->getState(); 86303233Sdim } 87303233Sdim // A wait has no matching nonblocking call. 88314564Sdim BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, 89314564Sdim Ctx.getBugReporter()); 90303233Sdim } 91303233Sdim } 92303233Sdim 93303233Sdim if (!ErrorNode) { 94303233Sdim Ctx.addTransition(State); 95303233Sdim } else { 96303233Sdim Ctx.addTransition(State, ErrorNode); 97303233Sdim } 98303233Sdim} 99303233Sdim 100303233Sdimvoid MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, 101303233Sdim CheckerContext &Ctx) const { 102303233Sdim ProgramStateRef State = Ctx.getState(); 103303233Sdim const auto &Requests = State->get<RequestMap>(); 104303233Sdim if (Requests.isEmpty()) 105303233Sdim return; 106303233Sdim 107303233Sdim static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); 108303233Sdim ExplodedNode *ErrorNode{nullptr}; 109303233Sdim 110303233Sdim auto ReqMap = State->get<RequestMap>(); 111303233Sdim for (const auto &Req : ReqMap) { 112303233Sdim if (!SymReaper.isLiveRegion(Req.first)) { 113303233Sdim if (Req.second.CurrentState == Request::State::Nonblocking) { 114303233Sdim 115303233Sdim if (!ErrorNode) { 116303233Sdim ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); 117303233Sdim State = ErrorNode->getState(); 118303233Sdim } 119314564Sdim BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, 120314564Sdim Ctx.getBugReporter()); 121303233Sdim } 122303233Sdim State = State->remove<RequestMap>(Req.first); 123303233Sdim } 124303233Sdim } 125303233Sdim 126303233Sdim // Transition to update the state regarding removed requests. 127303233Sdim if (!ErrorNode) { 128303233Sdim Ctx.addTransition(State); 129303233Sdim } else { 130303233Sdim Ctx.addTransition(State, ErrorNode); 131303233Sdim } 132303233Sdim} 133303233Sdim 134303233Sdimconst MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { 135303233Sdim 136303233Sdim if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { 137303233Sdim return CE.getArgSVal(0).getAsRegion(); 138303233Sdim } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { 139303233Sdim return CE.getArgSVal(1).getAsRegion(); 140303233Sdim } else { 141303233Sdim return (const MemRegion *)nullptr; 142303233Sdim } 143303233Sdim} 144303233Sdim 145303233Sdimvoid MPIChecker::allRegionsUsedByWait( 146303233Sdim llvm::SmallVector<const MemRegion *, 2> &ReqRegions, 147303233Sdim const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { 148303233Sdim 149303233Sdim MemRegionManager *const RegionManager = MR->getMemRegionManager(); 150303233Sdim 151303233Sdim if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { 152321369Sdim const SubRegion *SuperRegion{nullptr}; 153303233Sdim if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { 154321369Sdim SuperRegion = cast<SubRegion>(ER->getSuperRegion()); 155303233Sdim } 156303233Sdim 157303233Sdim // A single request is passed to MPI_Waitall. 158303233Sdim if (!SuperRegion) { 159303233Sdim ReqRegions.push_back(MR); 160303233Sdim return; 161303233Sdim } 162303233Sdim 163303233Sdim const auto &Size = Ctx.getStoreManager().getSizeInElements( 164303233Sdim Ctx.getState(), SuperRegion, 165303233Sdim CE.getArgExpr(1)->getType()->getPointeeType()); 166303233Sdim const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); 167303233Sdim 168303233Sdim for (size_t i = 0; i < ArrSize; ++i) { 169303233Sdim const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); 170303233Sdim 171303233Sdim const ElementRegion *const ER = RegionManager->getElementRegion( 172303233Sdim CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, 173303233Sdim Ctx.getASTContext()); 174303233Sdim 175303233Sdim ReqRegions.push_back(ER->getAs<MemRegion>()); 176303233Sdim } 177303233Sdim } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { 178303233Sdim ReqRegions.push_back(MR); 179303233Sdim } 180303233Sdim} 181303233Sdim 182303233Sdim} // end of namespace: mpi 183303233Sdim} // end of namespace: ento 184303233Sdim} // end of namespace: clang 185303233Sdim 186303233Sdim// Registers the checker for static analysis. 187303233Sdimvoid clang::ento::registerMPIChecker(CheckerManager &MGR) { 188303233Sdim MGR.registerChecker<clang::ento::mpi::MPIChecker>(); 189303233Sdim} 190353358Sdim 191353358Sdimbool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) { 192353358Sdim return true; 193353358Sdim} 194