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
emitDiagnostics(const BoundNodes & Match,const Decl * D,BugReporter & BR,AnalysisManager & AM,const PointerIterationChecker * Checker)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
matchUnorderedIterWithPointers()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
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const820b57cec5SDimitry 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
registerPointerIterationChecker(CheckerManager & Mgr)940b57cec5SDimitry Andric void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
950b57cec5SDimitry Andric Mgr.registerChecker<PointerIterationChecker>();
960b57cec5SDimitry Andric }
970b57cec5SDimitry Andric
shouldRegisterPointerIterationChecker(const CheckerManager & mgr)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