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