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