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
21namespace clang {
22namespace ento {
23namespace mpi {
24
25void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
26 CheckerContext &Ctx) const {
27 if (!FuncClassifier->isNonBlockingType(IdentInfo: PreCallEvent.getCalleeIdentifier())) {
28 return;
29 }
30 const MemRegion *const MR =
31 PreCallEvent.getArgSVal(Index: PreCallEvent.getNumArgs() - 1).getAsRegion();
32 if (!MR)
33 return;
34 const ElementRegion *const ER = dyn_cast<ElementRegion>(Val: MR);
35
36 // The region must be typed, in order to reason about it.
37 if (!isa<TypedRegion>(Val: MR) || (ER && !isa<TypedRegion>(Val: ER->getSuperRegion())))
38 return;
39
40 ProgramStateRef State = Ctx.getState();
41 const Request *const Req = State->get<RequestMap>(key: MR);
42
43 // double nonblocking detected
44 if (Req && Req->CurrentState == Request::State::Nonblocking) {
45 ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
46 BReporter.reportDoubleNonblocking(MPICallEvent: PreCallEvent, Req: *Req, RequestRegion: MR, ExplNode: ErrorNode,
47 BReporter&: Ctx.getBugReporter());
48 Ctx.addTransition(State: ErrorNode->getState(), Pred: ErrorNode);
49 }
50 // no error
51 else {
52 State = State->set<RequestMap>(K: MR, E: Request::State::Nonblocking);
53 Ctx.addTransition(State);
54 }
55}
56
57void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
58 CheckerContext &Ctx) const {
59 if (!FuncClassifier->isWaitType(IdentInfo: PreCallEvent.getCalleeIdentifier()))
60 return;
61 const MemRegion *const MR = topRegionUsedByWait(CE: PreCallEvent);
62 if (!MR)
63 return;
64 const ElementRegion *const ER = dyn_cast<ElementRegion>(Val: MR);
65
66 // The region must be typed, in order to reason about it.
67 if (!isa<TypedRegion>(Val: MR) || (ER && !isa<TypedRegion>(Val: ER->getSuperRegion())))
68 return;
69
70 llvm::SmallVector<const MemRegion *, 2> ReqRegions;
71 allRegionsUsedByWait(ReqRegions, MR, CE: 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>(key: ReqRegion);
81 State = State->set<RequestMap>(K: ReqRegion, E: 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(CE: PreCallEvent, RequestRegion: ReqRegion, ExplNode: ErrorNode,
89 BReporter&: Ctx.getBugReporter());
90 }
91 }
92
93 if (!ErrorNode) {
94 Ctx.addTransition(State);
95 } else {
96 Ctx.addTransition(State, Pred: ErrorNode);
97 }
98}
99
100void 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(region: 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: Req.second, RequestRegion: Req.first, ExplNode: ErrorNode,
119 BReporter&: Ctx.getBugReporter());
120 }
121 State = State->remove<RequestMap>(K: 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, Pred: ErrorNode);
130 }
131}
132
133const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
134
135 if (FuncClassifier->isMPI_Wait(IdentInfo: CE.getCalleeIdentifier())) {
136 return CE.getArgSVal(Index: 0).getAsRegion();
137 } else if (FuncClassifier->isMPI_Waitall(IdentInfo: CE.getCalleeIdentifier())) {
138 return CE.getArgSVal(Index: 1).getAsRegion();
139 } else {
140 return (const MemRegion *)nullptr;
141 }
142}
143
144void 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(IdentInfo: CE.getCalleeIdentifier())) {
151 const SubRegion *SuperRegion{nullptr};
152 if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
153 SuperRegion = cast<SubRegion>(Val: ER->getSuperRegion());
154 }
155
156 // A single request is passed to MPI_Waitall.
157 if (!SuperRegion) {
158 ReqRegions.push_back(Elt: MR);
159 return;
160 }
161
162 DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
163 State: Ctx.getState(), MR: SuperRegion, SVB&: Ctx.getSValBuilder(),
164 Ty: CE.getArgExpr(Index: 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(idx: i);
170
171 const ElementRegion *const ER = RegionManager.getElementRegion(
172 elementType: CE.getArgExpr(Index: 1)->getType()->getPointeeType(), Idx, superRegion: SuperRegion,
173 Ctx: Ctx.getASTContext());
174
175 ReqRegions.push_back(Elt: ER->getAs<MemRegion>());
176 }
177 } else if (FuncClassifier->isMPI_Wait(IdentInfo: CE.getCalleeIdentifier())) {
178 ReqRegions.push_back(Elt: 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.
187void clang::ento::registerMPIChecker(CheckerManager &MGR) {
188 MGR.registerChecker<clang::ento::mpi::MPIChecker>();
189}
190
191bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {
192 return true;
193}
194