xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp (revision 81ad626541db97eb356e2c1d4a20eb2a26a766ab)
10b57cec5SDimitry Andric //===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric ///
90b57cec5SDimitry Andric /// \file
100b57cec5SDimitry Andric /// This file defines the main class of MPI-Checker which serves as an entry
110b57cec5SDimitry Andric /// point. It is created once for each translation unit analysed.
120b57cec5SDimitry Andric /// The checker defines path-sensitive checks, to verify correct usage of the
130b57cec5SDimitry Andric /// MPI API.
140b57cec5SDimitry Andric ///
150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
160b57cec5SDimitry Andric 
170b57cec5SDimitry Andric #include "MPIChecker.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
19fe6060f1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric namespace clang {
220b57cec5SDimitry Andric namespace ento {
230b57cec5SDimitry Andric namespace mpi {
240b57cec5SDimitry Andric 
checkDoubleNonblocking(const CallEvent & PreCallEvent,CheckerContext & Ctx) const250b57cec5SDimitry Andric void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
260b57cec5SDimitry Andric                                         CheckerContext &Ctx) const {
270b57cec5SDimitry Andric   if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
280b57cec5SDimitry Andric     return;
290b57cec5SDimitry Andric   }
300b57cec5SDimitry Andric   const MemRegion *const MR =
310b57cec5SDimitry Andric       PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
320b57cec5SDimitry Andric   if (!MR)
330b57cec5SDimitry Andric     return;
340b57cec5SDimitry Andric   const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric   // The region must be typed, in order to reason about it.
370b57cec5SDimitry Andric   if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
380b57cec5SDimitry Andric     return;
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric   ProgramStateRef State = Ctx.getState();
410b57cec5SDimitry Andric   const Request *const Req = State->get<RequestMap>(MR);
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   // double nonblocking detected
440b57cec5SDimitry Andric   if (Req && Req->CurrentState == Request::State::Nonblocking) {
450b57cec5SDimitry Andric     ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
460b57cec5SDimitry Andric     BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
470b57cec5SDimitry Andric                                       Ctx.getBugReporter());
480b57cec5SDimitry Andric     Ctx.addTransition(ErrorNode->getState(), ErrorNode);
490b57cec5SDimitry Andric   }
500b57cec5SDimitry Andric   // no error
510b57cec5SDimitry Andric   else {
520b57cec5SDimitry Andric     State = State->set<RequestMap>(MR, Request::State::Nonblocking);
530b57cec5SDimitry Andric     Ctx.addTransition(State);
540b57cec5SDimitry Andric   }
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric 
checkUnmatchedWaits(const CallEvent & PreCallEvent,CheckerContext & Ctx) const570b57cec5SDimitry Andric void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
580b57cec5SDimitry Andric                                      CheckerContext &Ctx) const {
590b57cec5SDimitry Andric   if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
600b57cec5SDimitry Andric     return;
610b57cec5SDimitry Andric   const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
620b57cec5SDimitry Andric   if (!MR)
630b57cec5SDimitry Andric     return;
640b57cec5SDimitry Andric   const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   // The region must be typed, in order to reason about it.
670b57cec5SDimitry Andric   if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
680b57cec5SDimitry Andric     return;
690b57cec5SDimitry Andric 
700b57cec5SDimitry Andric   llvm::SmallVector<const MemRegion *, 2> ReqRegions;
710b57cec5SDimitry Andric   allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
720b57cec5SDimitry Andric   if (ReqRegions.empty())
730b57cec5SDimitry Andric     return;
740b57cec5SDimitry Andric 
750b57cec5SDimitry Andric   ProgramStateRef State = Ctx.getState();
760b57cec5SDimitry Andric   static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait");
770b57cec5SDimitry Andric   ExplodedNode *ErrorNode{nullptr};
780b57cec5SDimitry Andric 
790b57cec5SDimitry Andric   // Check all request regions used by the wait function.
800b57cec5SDimitry Andric   for (const auto &ReqRegion : ReqRegions) {
810b57cec5SDimitry Andric     const Request *const Req = State->get<RequestMap>(ReqRegion);
820b57cec5SDimitry Andric     State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
830b57cec5SDimitry Andric     if (!Req) {
840b57cec5SDimitry Andric       if (!ErrorNode) {
850b57cec5SDimitry Andric         ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
860b57cec5SDimitry Andric         State = ErrorNode->getState();
870b57cec5SDimitry Andric       }
880b57cec5SDimitry Andric       // A wait has no matching nonblocking call.
890b57cec5SDimitry Andric       BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
900b57cec5SDimitry Andric                                     Ctx.getBugReporter());
910b57cec5SDimitry Andric     }
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   if (!ErrorNode) {
950b57cec5SDimitry Andric     Ctx.addTransition(State);
960b57cec5SDimitry Andric   } else {
970b57cec5SDimitry Andric     Ctx.addTransition(State, ErrorNode);
980b57cec5SDimitry Andric   }
990b57cec5SDimitry Andric }
1000b57cec5SDimitry Andric 
checkMissingWaits(SymbolReaper & SymReaper,CheckerContext & Ctx) const1010b57cec5SDimitry Andric void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,
1020b57cec5SDimitry Andric                                    CheckerContext &Ctx) const {
1030b57cec5SDimitry Andric   ProgramStateRef State = Ctx.getState();
1040b57cec5SDimitry Andric   const auto &Requests = State->get<RequestMap>();
1050b57cec5SDimitry Andric   if (Requests.isEmpty())
1060b57cec5SDimitry Andric     return;
1070b57cec5SDimitry Andric 
1080b57cec5SDimitry Andric   static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
1090b57cec5SDimitry Andric   ExplodedNode *ErrorNode{nullptr};
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   auto ReqMap = State->get<RequestMap>();
1120b57cec5SDimitry Andric   for (const auto &Req : ReqMap) {
1130b57cec5SDimitry Andric     if (!SymReaper.isLiveRegion(Req.first)) {
1140b57cec5SDimitry Andric       if (Req.second.CurrentState == Request::State::Nonblocking) {
1150b57cec5SDimitry Andric 
1160b57cec5SDimitry Andric         if (!ErrorNode) {
1170b57cec5SDimitry Andric           ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
1180b57cec5SDimitry Andric           State = ErrorNode->getState();
1190b57cec5SDimitry Andric         }
1200b57cec5SDimitry Andric         BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
1210b57cec5SDimitry Andric                                     Ctx.getBugReporter());
1220b57cec5SDimitry Andric       }
1230b57cec5SDimitry Andric       State = State->remove<RequestMap>(Req.first);
1240b57cec5SDimitry Andric     }
1250b57cec5SDimitry Andric   }
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   // Transition to update the state regarding removed requests.
1280b57cec5SDimitry Andric   if (!ErrorNode) {
1290b57cec5SDimitry Andric     Ctx.addTransition(State);
1300b57cec5SDimitry Andric   } else {
1310b57cec5SDimitry Andric     Ctx.addTransition(State, ErrorNode);
1320b57cec5SDimitry Andric   }
1330b57cec5SDimitry Andric }
1340b57cec5SDimitry Andric 
topRegionUsedByWait(const CallEvent & CE) const1350b57cec5SDimitry Andric const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric   if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
1380b57cec5SDimitry Andric     return CE.getArgSVal(0).getAsRegion();
1390b57cec5SDimitry Andric   } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
1400b57cec5SDimitry Andric     return CE.getArgSVal(1).getAsRegion();
1410b57cec5SDimitry Andric   } else {
1420b57cec5SDimitry Andric     return (const MemRegion *)nullptr;
1430b57cec5SDimitry Andric   }
1440b57cec5SDimitry Andric }
1450b57cec5SDimitry Andric 
allRegionsUsedByWait(llvm::SmallVector<const MemRegion *,2> & ReqRegions,const MemRegion * const MR,const CallEvent & CE,CheckerContext & Ctx) const1460b57cec5SDimitry Andric void MPIChecker::allRegionsUsedByWait(
1470b57cec5SDimitry Andric     llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
1480b57cec5SDimitry Andric     const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
1490b57cec5SDimitry Andric 
1505ffd83dbSDimitry Andric   MemRegionManager &RegionManager = MR->getMemRegionManager();
1510b57cec5SDimitry Andric 
1520b57cec5SDimitry Andric   if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
1530b57cec5SDimitry Andric     const SubRegion *SuperRegion{nullptr};
1540b57cec5SDimitry Andric     if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
1550b57cec5SDimitry Andric       SuperRegion = cast<SubRegion>(ER->getSuperRegion());
1560b57cec5SDimitry Andric     }
1570b57cec5SDimitry Andric 
1580b57cec5SDimitry Andric     // A single request is passed to MPI_Waitall.
1590b57cec5SDimitry Andric     if (!SuperRegion) {
1600b57cec5SDimitry Andric       ReqRegions.push_back(MR);
1610b57cec5SDimitry Andric       return;
1620b57cec5SDimitry Andric     }
1630b57cec5SDimitry Andric 
1645ffd83dbSDimitry Andric     DefinedOrUnknownSVal ElementCount = getDynamicElementCount(
1655ffd83dbSDimitry Andric         Ctx.getState(), SuperRegion, Ctx.getSValBuilder(),
1660b57cec5SDimitry Andric         CE.getArgExpr(1)->getType()->getPointeeType());
1675ffd83dbSDimitry Andric     const llvm::APSInt &ArrSize =
168*81ad6265SDimitry Andric         ElementCount.castAs<nonloc::ConcreteInt>().getValue();
1690b57cec5SDimitry Andric 
1700b57cec5SDimitry Andric     for (size_t i = 0; i < ArrSize; ++i) {
1710b57cec5SDimitry Andric       const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
1720b57cec5SDimitry Andric 
1735ffd83dbSDimitry Andric       const ElementRegion *const ER = RegionManager.getElementRegion(
1740b57cec5SDimitry Andric           CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
1750b57cec5SDimitry Andric           Ctx.getASTContext());
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric       ReqRegions.push_back(ER->getAs<MemRegion>());
1780b57cec5SDimitry Andric     }
1790b57cec5SDimitry Andric   } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
1800b57cec5SDimitry Andric     ReqRegions.push_back(MR);
1810b57cec5SDimitry Andric   }
1820b57cec5SDimitry Andric }
1830b57cec5SDimitry Andric 
1840b57cec5SDimitry Andric } // end of namespace: mpi
1850b57cec5SDimitry Andric } // end of namespace: ento
1860b57cec5SDimitry Andric } // end of namespace: clang
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric // Registers the checker for static analysis.
registerMPIChecker(CheckerManager & MGR)1890b57cec5SDimitry Andric void clang::ento::registerMPIChecker(CheckerManager &MGR) {
1900b57cec5SDimitry Andric   MGR.registerChecker<clang::ento::mpi::MPIChecker>();
1910b57cec5SDimitry Andric }
1920b57cec5SDimitry Andric 
shouldRegisterMPIChecker(const CheckerManager & mgr)1935ffd83dbSDimitry Andric bool clang::ento::shouldRegisterMPIChecker(const CheckerManager &mgr) {
1940b57cec5SDimitry Andric   return true;
1950b57cec5SDimitry Andric }
196