10cdc5dddSMandeep Singh Grang //== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=//
20cdc5dddSMandeep Singh Grang //
30cdc5dddSMandeep Singh Grang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40cdc5dddSMandeep Singh Grang // See https://llvm.org/LICENSE.txt for license information.
50cdc5dddSMandeep Singh Grang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60cdc5dddSMandeep Singh Grang //
70cdc5dddSMandeep Singh Grang //===----------------------------------------------------------------------===//
80cdc5dddSMandeep Singh Grang //
90cdc5dddSMandeep Singh Grang // This file defines PointerIterationChecker which checks for non-determinism
100cdc5dddSMandeep Singh Grang // caused due to iteration of unordered containers of pointer elements.
110cdc5dddSMandeep Singh Grang //
120cdc5dddSMandeep Singh Grang //===----------------------------------------------------------------------===//
130cdc5dddSMandeep Singh Grang 
140cdc5dddSMandeep Singh Grang #include "clang/ASTMatchers/ASTMatchFinder.h"
150cdc5dddSMandeep Singh Grang #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160cdc5dddSMandeep Singh Grang #include "clang/StaticAnalyzer/Core/Checker.h"
170cdc5dddSMandeep Singh Grang #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
180cdc5dddSMandeep Singh Grang 
190cdc5dddSMandeep Singh Grang using namespace clang;
200cdc5dddSMandeep Singh Grang using namespace ento;
210cdc5dddSMandeep Singh Grang using namespace ast_matchers;
220cdc5dddSMandeep Singh Grang 
230cdc5dddSMandeep Singh Grang namespace {
240cdc5dddSMandeep Singh Grang 
250cdc5dddSMandeep Singh Grang // ID of a node at which the diagnostic would be emitted.
260cdc5dddSMandeep Singh Grang constexpr llvm::StringLiteral WarnAtNode = "iter";
270cdc5dddSMandeep Singh Grang 
280cdc5dddSMandeep Singh Grang class PointerIterationChecker : public Checker<check::ASTCodeBody> {
290cdc5dddSMandeep Singh Grang public:
300cdc5dddSMandeep Singh Grang   void checkASTCodeBody(const Decl *D,
310cdc5dddSMandeep Singh Grang                         AnalysisManager &AM,
320cdc5dddSMandeep Singh Grang                         BugReporter &BR) const;
330cdc5dddSMandeep Singh Grang };
340cdc5dddSMandeep Singh Grang 
emitDiagnostics(const BoundNodes & Match,const Decl * D,BugReporter & BR,AnalysisManager & AM,const PointerIterationChecker * Checker)350cdc5dddSMandeep Singh Grang static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
360cdc5dddSMandeep Singh Grang                             BugReporter &BR, AnalysisManager &AM,
370cdc5dddSMandeep Singh Grang                             const PointerIterationChecker *Checker) {
380cdc5dddSMandeep Singh Grang   auto *ADC = AM.getAnalysisDeclContext(D);
390cdc5dddSMandeep Singh Grang 
400cdc5dddSMandeep Singh Grang   const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
410cdc5dddSMandeep Singh Grang   assert(MarkedStmt);
420cdc5dddSMandeep Singh Grang 
430cdc5dddSMandeep Singh Grang   auto Range = MarkedStmt->getSourceRange();
440cdc5dddSMandeep Singh Grang   auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
450cdc5dddSMandeep Singh Grang                                                       BR.getSourceManager(),
460cdc5dddSMandeep Singh Grang                                                       ADC);
470cdc5dddSMandeep Singh Grang   std::string Diagnostics;
480cdc5dddSMandeep Singh Grang   llvm::raw_string_ostream OS(Diagnostics);
490cdc5dddSMandeep Singh Grang   OS << "Iteration of pointer-like elements "
500cdc5dddSMandeep Singh Grang      << "can result in non-deterministic ordering";
510cdc5dddSMandeep Singh Grang 
520cdc5dddSMandeep Singh Grang   BR.EmitBasicReport(ADC->getDecl(), Checker,
530cdc5dddSMandeep Singh Grang                      "Iteration of pointer-like elements", "Non-determinism",
540cdc5dddSMandeep Singh Grang                      OS.str(), Location, Range);
550cdc5dddSMandeep Singh Grang }
560cdc5dddSMandeep Singh Grang 
570cdc5dddSMandeep Singh Grang // Assumption: Iteration of ordered containers of pointers is deterministic.
580cdc5dddSMandeep Singh Grang 
590cdc5dddSMandeep Singh Grang // TODO: Currently, we only check for std::unordered_set. Other unordered
600cdc5dddSMandeep Singh Grang // containers like std::unordered_map also need to be handled.
610cdc5dddSMandeep Singh Grang 
620cdc5dddSMandeep Singh Grang // TODO: Currently, we do not check what the for loop does with the iterated
630cdc5dddSMandeep Singh Grang // pointer values. Not all iterations may cause non-determinism. For example,
640cdc5dddSMandeep Singh Grang // counting or summing up the elements should not be non-deterministic.
650cdc5dddSMandeep Singh Grang 
matchUnorderedIterWithPointers()660cdc5dddSMandeep Singh Grang auto matchUnorderedIterWithPointers() -> decltype(decl()) {
670cdc5dddSMandeep Singh Grang 
680cdc5dddSMandeep Singh Grang   auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
690cdc5dddSMandeep Singh Grang                                recordDecl(hasName("std::unordered_set")
700cdc5dddSMandeep Singh Grang                              )))));
710cdc5dddSMandeep Singh Grang 
720cdc5dddSMandeep Singh Grang   auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
730cdc5dddSMandeep Singh Grang 
740cdc5dddSMandeep Singh Grang   auto PointerIterM = stmt(cxxForRangeStmt(
750cdc5dddSMandeep Singh Grang                              hasLoopVariable(PointerTypeM),
760cdc5dddSMandeep Singh Grang                              hasRangeInit(UnorderedContainerM)
770cdc5dddSMandeep Singh Grang                       )).bind(WarnAtNode);
780cdc5dddSMandeep Singh Grang 
790cdc5dddSMandeep Singh Grang   return decl(forEachDescendant(PointerIterM));
800cdc5dddSMandeep Singh Grang }
810cdc5dddSMandeep Singh Grang 
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const820cdc5dddSMandeep Singh Grang void PointerIterationChecker::checkASTCodeBody(const Decl *D,
830cdc5dddSMandeep Singh Grang                                              AnalysisManager &AM,
840cdc5dddSMandeep Singh Grang                                              BugReporter &BR) const {
850cdc5dddSMandeep Singh Grang   auto MatcherM = matchUnorderedIterWithPointers();
860cdc5dddSMandeep Singh Grang 
870cdc5dddSMandeep Singh Grang   auto Matches = match(MatcherM, *D, AM.getASTContext());
880cdc5dddSMandeep Singh Grang   for (const auto &Match : Matches)
890cdc5dddSMandeep Singh Grang     emitDiagnostics(Match, D, BR, AM, this);
900cdc5dddSMandeep Singh Grang }
910cdc5dddSMandeep Singh Grang 
920cdc5dddSMandeep Singh Grang } // end of anonymous namespace
930cdc5dddSMandeep Singh Grang 
registerPointerIterationChecker(CheckerManager & Mgr)940cdc5dddSMandeep Singh Grang void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
950cdc5dddSMandeep Singh Grang   Mgr.registerChecker<PointerIterationChecker>();
960cdc5dddSMandeep Singh Grang }
970cdc5dddSMandeep Singh Grang 
shouldRegisterPointerIterationChecker(const CheckerManager & mgr)98*bda3dd0dSKirstóf Umann bool ento::shouldRegisterPointerIterationChecker(const CheckerManager &mgr) {
99*bda3dd0dSKirstóf Umann   const LangOptions &LO = mgr.getLangOpts();
1000cdc5dddSMandeep Singh Grang   return LO.CPlusPlus;
1010cdc5dddSMandeep Singh Grang }
102