PointerIterationChecker.cpp 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. //== PointerIterationChecker.cpp ------------------------------- -*- 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. // This file defines PointerIterationChecker which checks for non-determinism
  10. // caused due to iteration of unordered containers of pointer elements.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #include "clang/ASTMatchers/ASTMatchFinder.h"
  14. #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
  15. #include "clang/StaticAnalyzer/Core/Checker.h"
  16. #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
  17. using namespace clang;
  18. using namespace ento;
  19. using namespace ast_matchers;
  20. namespace {
  21. // ID of a node at which the diagnostic would be emitted.
  22. constexpr llvm::StringLiteral WarnAtNode = "iter";
  23. class PointerIterationChecker : public Checker<check::ASTCodeBody> {
  24. public:
  25. void checkASTCodeBody(const Decl *D,
  26. AnalysisManager &AM,
  27. BugReporter &BR) const;
  28. };
  29. static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
  30. BugReporter &BR, AnalysisManager &AM,
  31. const PointerIterationChecker *Checker) {
  32. auto *ADC = AM.getAnalysisDeclContext(D);
  33. const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
  34. assert(MarkedStmt);
  35. auto Range = MarkedStmt->getSourceRange();
  36. auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
  37. BR.getSourceManager(),
  38. ADC);
  39. std::string Diagnostics;
  40. llvm::raw_string_ostream OS(Diagnostics);
  41. OS << "Iteration of pointer-like elements "
  42. << "can result in non-deterministic ordering";
  43. BR.EmitBasicReport(ADC->getDecl(), Checker,
  44. "Iteration of pointer-like elements", "Non-determinism",
  45. OS.str(), Location, Range);
  46. }
  47. // Assumption: Iteration of ordered containers of pointers is deterministic.
  48. // TODO: Currently, we only check for std::unordered_set. Other unordered
  49. // containers like std::unordered_map also need to be handled.
  50. // TODO: Currently, we do not check what the for loop does with the iterated
  51. // pointer values. Not all iterations may cause non-determinism. For example,
  52. // counting or summing up the elements should not be non-deterministic.
  53. auto matchUnorderedIterWithPointers() -> decltype(decl()) {
  54. auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
  55. recordDecl(hasName("std::unordered_set")
  56. )))));
  57. auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
  58. auto PointerIterM = stmt(cxxForRangeStmt(
  59. hasLoopVariable(PointerTypeM),
  60. hasRangeInit(UnorderedContainerM)
  61. )).bind(WarnAtNode);
  62. return decl(forEachDescendant(PointerIterM));
  63. }
  64. void PointerIterationChecker::checkASTCodeBody(const Decl *D,
  65. AnalysisManager &AM,
  66. BugReporter &BR) const {
  67. auto MatcherM = matchUnorderedIterWithPointers();
  68. auto Matches = match(MatcherM, *D, AM.getASTContext());
  69. for (const auto &Match : Matches)
  70. emitDiagnostics(Match, D, BR, AM, this);
  71. }
  72. } // end of anonymous namespace
  73. void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
  74. Mgr.registerChecker<PointerIterationChecker>();
  75. }
  76. bool ento::shouldRegisterPointerIterationChecker(const CheckerManager &mgr) {
  77. const LangOptions &LO = mgr.getLangOpts();
  78. return LO.CPlusPlus;
  79. }