• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /freebsd-13-stable/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/
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/DynamicSize.h"
20
21namespace clang {
22namespace ento {
23namespace mpi {
24
25void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
26                                        CheckerContext &Ctx) const {
27  if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
28    return;
29  }
30  const MemRegion *const MR =
31      PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
32  if (!MR)
33    return;
34  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
35
36  // The region must be typed, in order to reason about it.
37  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
38    return;
39
40  ProgramStateRef State = Ctx.getState();
41  const Request *const Req = State->get<RequestMap>(MR);
42
43  // double nonblocking detected
44  if (Req && Req->CurrentState == Request::State::Nonblocking) {
45    ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
46    BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
47                                      Ctx.getBugReporter());
48    Ctx.addTransition(ErrorNode->getState(), ErrorNode);
49  }
50  // no error
51  else {
52    State = State->set<RequestMap>(MR, Request::State::Nonblocking);
53    Ctx.addTransition(State);
54  }
55}
56
57void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
58                                     CheckerContext &Ctx) const {
59  if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
60    return;
61  const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
62  if (!MR)
63    return;
64  const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
65
66  // The region must be typed, in order to reason about it.
67  if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
68    return;
69
70  llvm::SmallVector<const MemRegion *, 2> ReqRegions;
71  allRegionsUsedByWait(ReqRegions, MR, 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>(ReqRegion);
82    State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
83    if (!Req) {
84      if (!ErrorNode) {
85        ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
86        State = ErrorNode->getState();
87      }
88      // A wait has no matching nonblocking call.
89      BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
90                                    Ctx.getBugReporter());
91    }
92  }
93
94  if (!ErrorNode) {
95    Ctx.addTransition(State);
96  } else {
97    Ctx.addTransition(State, ErrorNode);
98  }
99}
100
101void 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(Req.first)) {
114      if (Req.second.CurrentState == Request::State::Nonblocking) {
115
116        if (!ErrorNode) {
117          ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
118          State = ErrorNode->getState();
119        }
120        BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
121                                    Ctx.getBugReporter());
122      }
123      State = State->remove<RequestMap>(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, ErrorNode);
132  }
133}
134
135const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
136
137  if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
138    return CE.getArgSVal(0).getAsRegion();
139  } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
140    return CE.getArgSVal(1).getAsRegion();
141  } else {
142    return (const MemRegion *)nullptr;
143  }
144}
145
146void 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(CE.getCalleeIdentifier())) {
153    const SubRegion *SuperRegion{nullptr};
154    if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
155      SuperRegion = cast<SubRegion>(ER->getSuperRegion());
156    }
157
158    // A single request is passed to MPI_Waitall.
159    if (!SuperRegion) {
160      ReqRegions.push_back(MR);
161      return;
162    }
163
164    DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
165        Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
166        CE.getArgExpr(1)->getType()->getPointeeType());
167    const llvm::APSInt &ArrSize =
168        ElementCount.getAs<nonloc::ConcreteInt>()->getValue();
169
170    for (size_t i = 0; i < ArrSize; ++i) {
171      const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
172
173      const ElementRegion *const ER = RegionManager.getElementRegion(
174          CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
175          Ctx.getASTContext());
176
177      ReqRegions.push_back(ER->getAs<MemRegion>());
178    }
179  } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
180    ReqRegions.push_back(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.
189void clang::ento::registerMPIChecker(CheckerManager &MGR) {
190  MGR.registerChecker<clang::ento::mpi::MPIChecker>();
191}
192
193bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {
194  return true;
195}
196