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
20namespace clang {
21namespace ento {
22namespace mpi {
23
24void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
25                                        CheckerContext &Ctx) const {
26  if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
27    return;
28  }
29  const MemRegion *const MR =
30      PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
31  if (!MR)
32    return;
33  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
34
35  // The region must be typed, in order to reason about it.
36  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
37    return;
38
39  ProgramStateRef State = Ctx.getState();
40  const Request *const Req = State->get<RequestMap>(MR);
41
42  // double nonblocking detected
43  if (Req && Req->CurrentState == Request::State::Nonblocking) {
44    ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
45    BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
46                                      Ctx.getBugReporter());
47    Ctx.addTransition(ErrorNode->getState(), ErrorNode);
48  }
49  // no error
50  else {
51    State = State->set<RequestMap>(MR, Request::State::Nonblocking);
52    Ctx.addTransition(State);
53  }
54}
55
56void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
57                                     CheckerContext &Ctx) const {
58  if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
59    return;
60  const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
61  if (!MR)
62    return;
63  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
64
65  // The region must be typed, in order to reason about it.
66  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
67    return;
68
69  llvm::SmallVector<const MemRegion *, 2> ReqRegions;
70  allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
71  if (ReqRegions.empty())
72    return;
73
74  ProgramStateRef State = Ctx.getState();
75  static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait");
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>(ReqRegion);
81    State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
82    if (!Req) {
83      if (!ErrorNode) {
84        ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
85        State = ErrorNode->getState();
86      }
87      // A wait has no matching nonblocking call.
88      BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
89                                    Ctx.getBugReporter());
90    }
91  }
92
93  if (!ErrorNode) {
94    Ctx.addTransition(State);
95  } else {
96    Ctx.addTransition(State, 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  static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
108  ExplodedNode *ErrorNode{nullptr};
109
110  auto ReqMap = State->get<RequestMap>();
111  for (const auto &Req : ReqMap) {
112    if (!SymReaper.isLiveRegion(Req.first)) {
113      if (Req.second.CurrentState == Request::State::Nonblocking) {
114
115        if (!ErrorNode) {
116          ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
117          State = ErrorNode->getState();
118        }
119        BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
120                                    Ctx.getBugReporter());
121      }
122      State = State->remove<RequestMap>(Req.first);
123    }
124  }
125
126  // Transition to update the state regarding removed requests.
127  if (!ErrorNode) {
128    Ctx.addTransition(State);
129  } else {
130    Ctx.addTransition(State, ErrorNode);
131  }
132}
133
134const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
135
136  if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
137    return CE.getArgSVal(0).getAsRegion();
138  } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
139    return CE.getArgSVal(1).getAsRegion();
140  } else {
141    return (const MemRegion *)nullptr;
142  }
143}
144
145void MPIChecker::allRegionsUsedByWait(
146    llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
147    const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
148
149  MemRegionManager *const RegionManager = MR->getMemRegionManager();
150
151  if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
152    const SubRegion *SuperRegion{nullptr};
153    if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
154      SuperRegion = cast<SubRegion>(ER->getSuperRegion());
155    }
156
157    // A single request is passed to MPI_Waitall.
158    if (!SuperRegion) {
159      ReqRegions.push_back(MR);
160      return;
161    }
162
163    const auto &Size = Ctx.getStoreManager().getSizeInElements(
164        Ctx.getState(), SuperRegion,
165        CE.getArgExpr(1)->getType()->getPointeeType());
166    const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue();
167
168    for (size_t i = 0; i < ArrSize; ++i) {
169      const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
170
171      const ElementRegion *const ER = RegionManager->getElementRegion(
172          CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
173          Ctx.getASTContext());
174
175      ReqRegions.push_back(ER->getAs<MemRegion>());
176    }
177  } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
178    ReqRegions.push_back(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 LangOptions &LO) {
192  return true;
193}
194