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