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 | static CheckerProgramPointTag Tag("MPI-Checker" , "UnmatchedWait" ); |
77 | ExplodedNode *ErrorNode{nullptr}; |
78 | |
79 | // Check all request regions used by the wait function. |
80 | for (const auto &ReqRegion : ReqRegions) { |
81 | const Request *const Req = State->get<RequestMap>(key: ReqRegion); |
82 | State = State->set<RequestMap>(K: ReqRegion, E: Request::State::Wait); |
83 | if (!Req) { |
84 | if (!ErrorNode) { |
85 | ErrorNode = Ctx.generateNonFatalErrorNode(State, Tag: &Tag); |
86 | State = ErrorNode->getState(); |
87 | } |
88 | // A wait has no matching nonblocking call. |
89 | BReporter.reportUnmatchedWait(CE: PreCallEvent, RequestRegion: ReqRegion, ExplNode: ErrorNode, |
90 | BReporter&: Ctx.getBugReporter()); |
91 | } |
92 | } |
93 | |
94 | if (!ErrorNode) { |
95 | Ctx.addTransition(State); |
96 | } else { |
97 | Ctx.addTransition(State, Pred: ErrorNode); |
98 | } |
99 | } |
100 | |
101 | void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, |
102 | CheckerContext &Ctx) const { |
103 | ProgramStateRef State = Ctx.getState(); |
104 | const auto &Requests = State->get<RequestMap>(); |
105 | if (Requests.isEmpty()) |
106 | return; |
107 | |
108 | static CheckerProgramPointTag Tag("MPI-Checker" , "MissingWait" ); |
109 | ExplodedNode *ErrorNode{nullptr}; |
110 | |
111 | auto ReqMap = State->get<RequestMap>(); |
112 | for (const auto &Req : ReqMap) { |
113 | if (!SymReaper.isLiveRegion(region: Req.first)) { |
114 | if (Req.second.CurrentState == Request::State::Nonblocking) { |
115 | |
116 | if (!ErrorNode) { |
117 | ErrorNode = Ctx.generateNonFatalErrorNode(State, Tag: &Tag); |
118 | State = ErrorNode->getState(); |
119 | } |
120 | BReporter.reportMissingWait(Req: Req.second, RequestRegion: Req.first, ExplNode: ErrorNode, |
121 | BReporter&: Ctx.getBugReporter()); |
122 | } |
123 | State = State->remove<RequestMap>(K: Req.first); |
124 | } |
125 | } |
126 | |
127 | // Transition to update the state regarding removed requests. |
128 | if (!ErrorNode) { |
129 | Ctx.addTransition(State); |
130 | } else { |
131 | Ctx.addTransition(State, Pred: ErrorNode); |
132 | } |
133 | } |
134 | |
135 | const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { |
136 | |
137 | if (FuncClassifier->isMPI_Wait(IdentInfo: CE.getCalleeIdentifier())) { |
138 | return CE.getArgSVal(Index: 0).getAsRegion(); |
139 | } else if (FuncClassifier->isMPI_Waitall(IdentInfo: CE.getCalleeIdentifier())) { |
140 | return CE.getArgSVal(Index: 1).getAsRegion(); |
141 | } else { |
142 | return (const MemRegion *)nullptr; |
143 | } |
144 | } |
145 | |
146 | void MPIChecker::allRegionsUsedByWait( |
147 | llvm::SmallVector<const MemRegion *, 2> &ReqRegions, |
148 | const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { |
149 | |
150 | MemRegionManager &RegionManager = MR->getMemRegionManager(); |
151 | |
152 | if (FuncClassifier->isMPI_Waitall(IdentInfo: CE.getCalleeIdentifier())) { |
153 | const SubRegion *SuperRegion{nullptr}; |
154 | if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { |
155 | SuperRegion = cast<SubRegion>(Val: ER->getSuperRegion()); |
156 | } |
157 | |
158 | // A single request is passed to MPI_Waitall. |
159 | if (!SuperRegion) { |
160 | ReqRegions.push_back(Elt: MR); |
161 | return; |
162 | } |
163 | |
164 | DefinedOrUnknownSVal ElementCount = getDynamicElementCount( |
165 | State: Ctx.getState(), MR: SuperRegion, SVB&: Ctx.getSValBuilder(), |
166 | Ty: CE.getArgExpr(Index: 1)->getType()->getPointeeType()); |
167 | const llvm::APSInt &ArrSize = |
168 | ElementCount.castAs<nonloc::ConcreteInt>().getValue(); |
169 | |
170 | for (size_t i = 0; i < ArrSize; ++i) { |
171 | const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(idx: i); |
172 | |
173 | const ElementRegion *const ER = RegionManager.getElementRegion( |
174 | elementType: CE.getArgExpr(Index: 1)->getType()->getPointeeType(), Idx, superRegion: SuperRegion, |
175 | Ctx: Ctx.getASTContext()); |
176 | |
177 | ReqRegions.push_back(Elt: ER->getAs<MemRegion>()); |
178 | } |
179 | } else if (FuncClassifier->isMPI_Wait(IdentInfo: CE.getCalleeIdentifier())) { |
180 | ReqRegions.push_back(Elt: MR); |
181 | } |
182 | } |
183 | |
184 | } // end of namespace: mpi |
185 | } // end of namespace: ento |
186 | } // end of namespace: clang |
187 | |
188 | // Registers the checker for static analysis. |
189 | void clang::ento::registerMPIChecker(CheckerManager &MGR) { |
190 | MGR.registerChecker<clang::ento::mpi::MPIChecker>(); |
191 | } |
192 | |
193 | bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) { |
194 | return true; |
195 | } |
196 | |