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-exception 6// 7//===----------------------------------------------------------------------===// 8/// 9/// \file 10/// This file defines the main class of MPI-Checker which serves as an entry 11/// point. It is created once for each translation unit analysed. 12/// The checker defines path-sensitive checks, to verify correct usage of the 13/// MPI API. 14/// 15//===----------------------------------------------------------------------===// 16 17#include "MPIChecker.h" 18#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 19#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.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 detected 44 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 error 51 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 static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); 77 ExplodedNode *ErrorNode{nullptr}; 78 79 // Check all request regions used by the wait function. 80 for (const auto &ReqRegion : ReqRegions) { 81 const Request *const Req = State->get<RequestMap>(ReqRegion); 82 State = State->set<RequestMap>(ReqRegion, Request::State::Wait); 83 if (!Req) { 84 if (!ErrorNode) { 85 ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); 86 State = ErrorNode->getState(); 87 } 88 // A wait has no matching nonblocking call. 89 BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, 90 Ctx.getBugReporter()); 91 } 92 } 93 94 if (!ErrorNode) { 95 Ctx.addTransition(State); 96 } else { 97 Ctx.addTransition(State, ErrorNode); 98 } 99} 100 101void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, 102 CheckerContext &Ctx) const { 103 ProgramStateRef State = Ctx.getState(); 104 const auto &Requests = State->get<RequestMap>(); 105 if (Requests.isEmpty()) 106 return; 107 108 static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); 109 ExplodedNode *ErrorNode{nullptr}; 110 111 auto ReqMap = State->get<RequestMap>(); 112 for (const auto &Req : ReqMap) { 113 if (!SymReaper.isLiveRegion(Req.first)) { 114 if (Req.second.CurrentState == Request::State::Nonblocking) { 115 116 if (!ErrorNode) { 117 ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); 118 State = ErrorNode->getState(); 119 } 120 BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, 121 Ctx.getBugReporter()); 122 } 123 State = State->remove<RequestMap>(Req.first); 124 } 125 } 126 127 // Transition to update the state regarding removed requests. 128 if (!ErrorNode) { 129 Ctx.addTransition(State); 130 } else { 131 Ctx.addTransition(State, ErrorNode); 132 } 133} 134 135const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { 136 137 if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { 138 return CE.getArgSVal(0).getAsRegion(); 139 } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { 140 return CE.getArgSVal(1).getAsRegion(); 141 } else { 142 return (const MemRegion *)nullptr; 143 } 144} 145 146void MPIChecker::allRegionsUsedByWait( 147 llvm::SmallVector<const MemRegion *, 2> &ReqRegions, 148 const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { 149 150 MemRegionManager &RegionManager = MR->getMemRegionManager(); 151 152 if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { 153 const SubRegion *SuperRegion{nullptr}; 154 if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { 155 SuperRegion = cast<SubRegion>(ER->getSuperRegion()); 156 } 157 158 // A single request is passed to MPI_Waitall. 159 if (!SuperRegion) { 160 ReqRegions.push_back(MR); 161 return; 162 } 163 164 DefinedOrUnknownSVal ElementCount = getDynamicElementCount( 165 Ctx.getState(), SuperRegion, Ctx.getSValBuilder(), 166 CE.getArgExpr(1)->getType()->getPointeeType()); 167 const llvm::APSInt &ArrSize = 168 ElementCount.getAs<nonloc::ConcreteInt>()->getValue(); 169 170 for (size_t i = 0; i < ArrSize; ++i) { 171 const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); 172 173 const ElementRegion *const ER = RegionManager.getElementRegion( 174 CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, 175 Ctx.getASTContext()); 176 177 ReqRegions.push_back(ER->getAs<MemRegion>()); 178 } 179 } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { 180 ReqRegions.push_back(MR); 181 } 182} 183 184} // end of namespace: mpi 185} // end of namespace: ento 186} // end of namespace: clang 187 188// Registers the checker for static analysis. 189void clang::ento::registerMPIChecker(CheckerManager &MGR) { 190 MGR.registerChecker<clang::ento::mpi::MPIChecker>(); 191} 192 193bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) { 194 return true; 195} 196