xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick ///
9e5dd7070Spatrick /// \file
10e5dd7070Spatrick /// This file defines the main class of MPI-Checker which serves as an entry
11e5dd7070Spatrick /// point. It is created once for each translation unit analysed.
12e5dd7070Spatrick /// The checker defines path-sensitive checks, to verify correct usage of the
13e5dd7070Spatrick /// MPI API.
14e5dd7070Spatrick ///
15e5dd7070Spatrick //===----------------------------------------------------------------------===//
16e5dd7070Spatrick 
17e5dd7070Spatrick #include "MPIChecker.h"
18e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19a9ac8606Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
20e5dd7070Spatrick 
21e5dd7070Spatrick namespace clang {
22e5dd7070Spatrick namespace ento {
23e5dd7070Spatrick namespace mpi {
24e5dd7070Spatrick 
checkDoubleNonblocking(const CallEvent & PreCallEvent,CheckerContext & Ctx) const25e5dd7070Spatrick void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
26e5dd7070Spatrick                                         CheckerContext &Ctx) const {
27e5dd7070Spatrick   if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
28e5dd7070Spatrick     return;
29e5dd7070Spatrick   }
30e5dd7070Spatrick   const MemRegion *const MR =
31e5dd7070Spatrick       PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
32e5dd7070Spatrick   if (!MR)
33e5dd7070Spatrick     return;
34e5dd7070Spatrick   const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
35e5dd7070Spatrick 
36e5dd7070Spatrick   // The region must be typed, in order to reason about it.
37e5dd7070Spatrick   if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
38e5dd7070Spatrick     return;
39e5dd7070Spatrick 
40e5dd7070Spatrick   ProgramStateRef State = Ctx.getState();
41e5dd7070Spatrick   const Request *const Req = State->get<RequestMap>(MR);
42e5dd7070Spatrick 
43e5dd7070Spatrick   // double nonblocking detected
44e5dd7070Spatrick   if (Req && Req->CurrentState == Request::State::Nonblocking) {
45e5dd7070Spatrick     ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
46e5dd7070Spatrick     BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
47e5dd7070Spatrick                                       Ctx.getBugReporter());
48e5dd7070Spatrick     Ctx.addTransition(ErrorNode->getState(), ErrorNode);
49e5dd7070Spatrick   }
50e5dd7070Spatrick   // no error
51e5dd7070Spatrick   else {
52e5dd7070Spatrick     State = State->set<RequestMap>(MR, Request::State::Nonblocking);
53e5dd7070Spatrick     Ctx.addTransition(State);
54e5dd7070Spatrick   }
55e5dd7070Spatrick }
56e5dd7070Spatrick 
checkUnmatchedWaits(const CallEvent & PreCallEvent,CheckerContext & Ctx) const57e5dd7070Spatrick void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
58e5dd7070Spatrick                                      CheckerContext &Ctx) const {
59e5dd7070Spatrick   if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
60e5dd7070Spatrick     return;
61e5dd7070Spatrick   const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
62e5dd7070Spatrick   if (!MR)
63e5dd7070Spatrick     return;
64e5dd7070Spatrick   const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
65e5dd7070Spatrick 
66e5dd7070Spatrick   // The region must be typed, in order to reason about it.
67e5dd7070Spatrick   if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
68e5dd7070Spatrick     return;
69e5dd7070Spatrick 
70e5dd7070Spatrick   llvm::SmallVector<const MemRegion *, 2> ReqRegions;
71e5dd7070Spatrick   allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
72e5dd7070Spatrick   if (ReqRegions.empty())
73e5dd7070Spatrick     return;
74e5dd7070Spatrick 
75e5dd7070Spatrick   ProgramStateRef State = Ctx.getState();
76e5dd7070Spatrick   static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait");
77e5dd7070Spatrick   ExplodedNode *ErrorNode{nullptr};
78e5dd7070Spatrick 
79e5dd7070Spatrick   // Check all request regions used by the wait function.
80e5dd7070Spatrick   for (const auto &ReqRegion : ReqRegions) {
81e5dd7070Spatrick     const Request *const Req = State->get<RequestMap>(ReqRegion);
82e5dd7070Spatrick     State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
83e5dd7070Spatrick     if (!Req) {
84e5dd7070Spatrick       if (!ErrorNode) {
85e5dd7070Spatrick         ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
86e5dd7070Spatrick         State = ErrorNode->getState();
87e5dd7070Spatrick       }
88e5dd7070Spatrick       // A wait has no matching nonblocking call.
89e5dd7070Spatrick       BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
90e5dd7070Spatrick                                     Ctx.getBugReporter());
91e5dd7070Spatrick     }
92e5dd7070Spatrick   }
93e5dd7070Spatrick 
94e5dd7070Spatrick   if (!ErrorNode) {
95e5dd7070Spatrick     Ctx.addTransition(State);
96e5dd7070Spatrick   } else {
97e5dd7070Spatrick     Ctx.addTransition(State, ErrorNode);
98e5dd7070Spatrick   }
99e5dd7070Spatrick }
100e5dd7070Spatrick 
checkMissingWaits(SymbolReaper & SymReaper,CheckerContext & Ctx) const101e5dd7070Spatrick void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,
102e5dd7070Spatrick                                    CheckerContext &Ctx) const {
103e5dd7070Spatrick   ProgramStateRef State = Ctx.getState();
104e5dd7070Spatrick   const auto &Requests = State->get<RequestMap>();
105e5dd7070Spatrick   if (Requests.isEmpty())
106e5dd7070Spatrick     return;
107e5dd7070Spatrick 
108e5dd7070Spatrick   static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
109e5dd7070Spatrick   ExplodedNode *ErrorNode{nullptr};
110e5dd7070Spatrick 
111e5dd7070Spatrick   auto ReqMap = State->get<RequestMap>();
112e5dd7070Spatrick   for (const auto &Req : ReqMap) {
113e5dd7070Spatrick     if (!SymReaper.isLiveRegion(Req.first)) {
114e5dd7070Spatrick       if (Req.second.CurrentState == Request::State::Nonblocking) {
115e5dd7070Spatrick 
116e5dd7070Spatrick         if (!ErrorNode) {
117e5dd7070Spatrick           ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
118e5dd7070Spatrick           State = ErrorNode->getState();
119e5dd7070Spatrick         }
120e5dd7070Spatrick         BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
121e5dd7070Spatrick                                     Ctx.getBugReporter());
122e5dd7070Spatrick       }
123e5dd7070Spatrick       State = State->remove<RequestMap>(Req.first);
124e5dd7070Spatrick     }
125e5dd7070Spatrick   }
126e5dd7070Spatrick 
127e5dd7070Spatrick   // Transition to update the state regarding removed requests.
128e5dd7070Spatrick   if (!ErrorNode) {
129e5dd7070Spatrick     Ctx.addTransition(State);
130e5dd7070Spatrick   } else {
131e5dd7070Spatrick     Ctx.addTransition(State, ErrorNode);
132e5dd7070Spatrick   }
133e5dd7070Spatrick }
134e5dd7070Spatrick 
topRegionUsedByWait(const CallEvent & CE) const135e5dd7070Spatrick const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
136e5dd7070Spatrick 
137e5dd7070Spatrick   if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
138e5dd7070Spatrick     return CE.getArgSVal(0).getAsRegion();
139e5dd7070Spatrick   } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
140e5dd7070Spatrick     return CE.getArgSVal(1).getAsRegion();
141e5dd7070Spatrick   } else {
142e5dd7070Spatrick     return (const MemRegion *)nullptr;
143e5dd7070Spatrick   }
144e5dd7070Spatrick }
145e5dd7070Spatrick 
allRegionsUsedByWait(llvm::SmallVector<const MemRegion *,2> & ReqRegions,const MemRegion * const MR,const CallEvent & CE,CheckerContext & Ctx) const146e5dd7070Spatrick void MPIChecker::allRegionsUsedByWait(
147e5dd7070Spatrick     llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
148e5dd7070Spatrick     const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
149e5dd7070Spatrick 
150ec727ea7Spatrick   MemRegionManager &RegionManager = MR->getMemRegionManager();
151e5dd7070Spatrick 
152e5dd7070Spatrick   if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
153e5dd7070Spatrick     const SubRegion *SuperRegion{nullptr};
154e5dd7070Spatrick     if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
155e5dd7070Spatrick       SuperRegion = cast<SubRegion>(ER->getSuperRegion());
156e5dd7070Spatrick     }
157e5dd7070Spatrick 
158e5dd7070Spatrick     // A single request is passed to MPI_Waitall.
159e5dd7070Spatrick     if (!SuperRegion) {
160e5dd7070Spatrick       ReqRegions.push_back(MR);
161e5dd7070Spatrick       return;
162e5dd7070Spatrick     }
163e5dd7070Spatrick 
164ec727ea7Spatrick     DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
165ec727ea7Spatrick         Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
166e5dd7070Spatrick         CE.getArgExpr(1)->getType()->getPointeeType());
167ec727ea7Spatrick     const llvm::APSInt &ArrSize =
168*12c85518Srobert         ElementCount.castAs<nonloc::ConcreteInt>().getValue();
169e5dd7070Spatrick 
170e5dd7070Spatrick     for (size_t i = 0; i < ArrSize; ++i) {
171e5dd7070Spatrick       const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
172e5dd7070Spatrick 
173ec727ea7Spatrick       const ElementRegion *const ER = RegionManager.getElementRegion(
174e5dd7070Spatrick           CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
175e5dd7070Spatrick           Ctx.getASTContext());
176e5dd7070Spatrick 
177e5dd7070Spatrick       ReqRegions.push_back(ER->getAs<MemRegion>());
178e5dd7070Spatrick     }
179e5dd7070Spatrick   } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
180e5dd7070Spatrick     ReqRegions.push_back(MR);
181e5dd7070Spatrick   }
182e5dd7070Spatrick }
183e5dd7070Spatrick 
184e5dd7070Spatrick } // end of namespace: mpi
185e5dd7070Spatrick } // end of namespace: ento
186e5dd7070Spatrick } // end of namespace: clang
187e5dd7070Spatrick 
188e5dd7070Spatrick // Registers the checker for static analysis.
registerMPIChecker(CheckerManager & MGR)189e5dd7070Spatrick void clang::ento::registerMPIChecker(CheckerManager &MGR) {
190e5dd7070Spatrick   MGR.registerChecker<clang::ento::mpi::MPIChecker>();
191e5dd7070Spatrick }
192e5dd7070Spatrick 
shouldRegisterMPIChecker(const CheckerManager & mgr)193ec727ea7Spatrick bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {
194e5dd7070Spatrick   return true;
195e5dd7070Spatrick }
196