xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp (revision ec727ea710c91afd8ce4f788c5aaa8482b7b69b2)
1e5dd7070Spatrick //== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // This file defines PointerIterationChecker which checks for non-determinism
10e5dd7070Spatrick // caused due to iteration of unordered containers of pointer elements.
11e5dd7070Spatrick //
12e5dd7070Spatrick //===----------------------------------------------------------------------===//
13e5dd7070Spatrick 
14e5dd7070Spatrick #include "clang/ASTMatchers/ASTMatchFinder.h"
15e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
17e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18e5dd7070Spatrick 
19e5dd7070Spatrick using namespace clang;
20e5dd7070Spatrick using namespace ento;
21e5dd7070Spatrick using namespace ast_matchers;
22e5dd7070Spatrick 
23e5dd7070Spatrick namespace {
24e5dd7070Spatrick 
25e5dd7070Spatrick // ID of a node at which the diagnostic would be emitted.
26e5dd7070Spatrick constexpr llvm::StringLiteral WarnAtNode = "iter";
27e5dd7070Spatrick 
28e5dd7070Spatrick class PointerIterationChecker : public Checker<check::ASTCodeBody> {
29e5dd7070Spatrick public:
30e5dd7070Spatrick   void checkASTCodeBody(const Decl *D,
31e5dd7070Spatrick                         AnalysisManager &AM,
32e5dd7070Spatrick                         BugReporter &BR) const;
33e5dd7070Spatrick };
34e5dd7070Spatrick 
emitDiagnostics(const BoundNodes & Match,const Decl * D,BugReporter & BR,AnalysisManager & AM,const PointerIterationChecker * Checker)35e5dd7070Spatrick static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
36e5dd7070Spatrick                             BugReporter &BR, AnalysisManager &AM,
37e5dd7070Spatrick                             const PointerIterationChecker *Checker) {
38e5dd7070Spatrick   auto *ADC = AM.getAnalysisDeclContext(D);
39e5dd7070Spatrick 
40e5dd7070Spatrick   const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
41e5dd7070Spatrick   assert(MarkedStmt);
42e5dd7070Spatrick 
43e5dd7070Spatrick   auto Range = MarkedStmt->getSourceRange();
44e5dd7070Spatrick   auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
45e5dd7070Spatrick                                                       BR.getSourceManager(),
46e5dd7070Spatrick                                                       ADC);
47e5dd7070Spatrick   std::string Diagnostics;
48e5dd7070Spatrick   llvm::raw_string_ostream OS(Diagnostics);
49e5dd7070Spatrick   OS << "Iteration of pointer-like elements "
50e5dd7070Spatrick      << "can result in non-deterministic ordering";
51e5dd7070Spatrick 
52e5dd7070Spatrick   BR.EmitBasicReport(ADC->getDecl(), Checker,
53e5dd7070Spatrick                      "Iteration of pointer-like elements", "Non-determinism",
54e5dd7070Spatrick                      OS.str(), Location, Range);
55e5dd7070Spatrick }
56e5dd7070Spatrick 
57e5dd7070Spatrick // Assumption: Iteration of ordered containers of pointers is deterministic.
58e5dd7070Spatrick 
59e5dd7070Spatrick // TODO: Currently, we only check for std::unordered_set. Other unordered
60e5dd7070Spatrick // containers like std::unordered_map also need to be handled.
61e5dd7070Spatrick 
62e5dd7070Spatrick // TODO: Currently, we do not check what the for loop does with the iterated
63e5dd7070Spatrick // pointer values. Not all iterations may cause non-determinism. For example,
64e5dd7070Spatrick // counting or summing up the elements should not be non-deterministic.
65e5dd7070Spatrick 
matchUnorderedIterWithPointers()66e5dd7070Spatrick auto matchUnorderedIterWithPointers() -> decltype(decl()) {
67e5dd7070Spatrick 
68e5dd7070Spatrick   auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
69e5dd7070Spatrick                                recordDecl(hasName("std::unordered_set")
70e5dd7070Spatrick                              )))));
71e5dd7070Spatrick 
72e5dd7070Spatrick   auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
73e5dd7070Spatrick 
74e5dd7070Spatrick   auto PointerIterM = stmt(cxxForRangeStmt(
75e5dd7070Spatrick                              hasLoopVariable(PointerTypeM),
76e5dd7070Spatrick                              hasRangeInit(UnorderedContainerM)
77e5dd7070Spatrick                       )).bind(WarnAtNode);
78e5dd7070Spatrick 
79e5dd7070Spatrick   return decl(forEachDescendant(PointerIterM));
80e5dd7070Spatrick }
81e5dd7070Spatrick 
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const82e5dd7070Spatrick void PointerIterationChecker::checkASTCodeBody(const Decl *D,
83e5dd7070Spatrick                                              AnalysisManager &AM,
84e5dd7070Spatrick                                              BugReporter &BR) const {
85e5dd7070Spatrick   auto MatcherM = matchUnorderedIterWithPointers();
86e5dd7070Spatrick 
87e5dd7070Spatrick   auto Matches = match(MatcherM, *D, AM.getASTContext());
88e5dd7070Spatrick   for (const auto &Match : Matches)
89e5dd7070Spatrick     emitDiagnostics(Match, D, BR, AM, this);
90e5dd7070Spatrick }
91e5dd7070Spatrick 
92e5dd7070Spatrick } // end of anonymous namespace
93e5dd7070Spatrick 
registerPointerIterationChecker(CheckerManager & Mgr)94e5dd7070Spatrick void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
95e5dd7070Spatrick   Mgr.registerChecker<PointerIterationChecker>();
96e5dd7070Spatrick }
97e5dd7070Spatrick 
shouldRegisterPointerIterationChecker(const CheckerManager & mgr)98*ec727ea7Spatrick bool ento::shouldRegisterPointerIterationChecker(const CheckerManager &mgr) {
99*ec727ea7Spatrick   const LangOptions &LO = mgr.getLangOpts();
100e5dd7070Spatrick   return LO.CPlusPlus;
101e5dd7070Spatrick }
102