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