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 20 namespace clang { 21 namespace ento { 22 namespace mpi { 23 24 void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, 25 CheckerContext &Ctx) const { 26 if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { 27 return; 28 } 29 const MemRegion *const MR = 30 PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); 31 if (!MR) 32 return; 33 const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); 34 35 // The region must be typed, in order to reason about it. 36 if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) 37 return; 38 39 ProgramStateRef State = Ctx.getState(); 40 const Request *const Req = State->get<RequestMap>(MR); 41 42 // double nonblocking detected 43 if (Req && Req->CurrentState == Request::State::Nonblocking) { 44 ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); 45 BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, 46 Ctx.getBugReporter()); 47 Ctx.addTransition(ErrorNode->getState(), ErrorNode); 48 } 49 // no error 50 else { 51 State = State->set<RequestMap>(MR, Request::State::Nonblocking); 52 Ctx.addTransition(State); 53 } 54 } 55 56 void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, 57 CheckerContext &Ctx) const { 58 if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) 59 return; 60 const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); 61 if (!MR) 62 return; 63 const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); 64 65 // The region must be typed, in order to reason about it. 66 if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) 67 return; 68 69 llvm::SmallVector<const MemRegion *, 2> ReqRegions; 70 allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); 71 if (ReqRegions.empty()) 72 return; 73 74 ProgramStateRef State = Ctx.getState(); 75 static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); 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, &Tag); 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 100 void 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 static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); 108 ExplodedNode *ErrorNode{nullptr}; 109 110 auto ReqMap = State->get<RequestMap>(); 111 for (const auto &Req : ReqMap) { 112 if (!SymReaper.isLiveRegion(Req.first)) { 113 if (Req.second.CurrentState == Request::State::Nonblocking) { 114 115 if (!ErrorNode) { 116 ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); 117 State = ErrorNode->getState(); 118 } 119 BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, 120 Ctx.getBugReporter()); 121 } 122 State = State->remove<RequestMap>(Req.first); 123 } 124 } 125 126 // Transition to update the state regarding removed requests. 127 if (!ErrorNode) { 128 Ctx.addTransition(State); 129 } else { 130 Ctx.addTransition(State, ErrorNode); 131 } 132 } 133 134 const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { 135 136 if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { 137 return CE.getArgSVal(0).getAsRegion(); 138 } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { 139 return CE.getArgSVal(1).getAsRegion(); 140 } else { 141 return (const MemRegion *)nullptr; 142 } 143 } 144 145 void MPIChecker::allRegionsUsedByWait( 146 llvm::SmallVector<const MemRegion *, 2> &ReqRegions, 147 const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { 148 149 MemRegionManager *const RegionManager = MR->getMemRegionManager(); 150 151 if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { 152 const SubRegion *SuperRegion{nullptr}; 153 if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { 154 SuperRegion = cast<SubRegion>(ER->getSuperRegion()); 155 } 156 157 // A single request is passed to MPI_Waitall. 158 if (!SuperRegion) { 159 ReqRegions.push_back(MR); 160 return; 161 } 162 163 const auto &Size = Ctx.getStoreManager().getSizeInElements( 164 Ctx.getState(), SuperRegion, 165 CE.getArgExpr(1)->getType()->getPointeeType()); 166 const llvm::APSInt &ArrSize = Size.getAs<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: mpi 183 } // end of namespace: ento 184 } // end of namespace: clang 185 186 // Registers the checker for static analysis. 187 void clang::ento::registerMPIChecker(CheckerManager &MGR) { 188 MGR.registerChecker<clang::ento::mpi::MPIChecker>(); 189 } 190 191 bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) { 192 return true; 193 } 194