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(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 | |
57 | void 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 | |
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(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 | |
133 | const 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 | |
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(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. |
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 | |