xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp (revision 9c77fb6aaa366cbabc80ee1b834bcfe4df135491)
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/DynamicExtent.h"
20 
21 namespace clang {
22 namespace ento {
23 namespace mpi {
24 
25 void 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 
57 void 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 
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   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 
133 const 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 
144 void 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: 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 CheckerManager &mgr) {
192   return true;
193 }
194