PointerIterationChecker.cpp revision 353358
1228072Sbapt//== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=// 2228072Sbapt// 3228072Sbapt// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4228072Sbapt// See https://llvm.org/LICENSE.txt for license information. 5228072Sbapt// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6228072Sbapt// 7228072Sbapt//===----------------------------------------------------------------------===// 8228072Sbapt// 9228072Sbapt// This file defines PointerIterationChecker which checks for non-determinism 10228072Sbapt// caused due to iteration of unordered containers of pointer elements. 11228072Sbapt// 12228072Sbapt//===----------------------------------------------------------------------===// 13228072Sbapt 14228072Sbapt#include "clang/ASTMatchers/ASTMatchFinder.h" 15228072Sbapt#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 16228072Sbapt#include "clang/StaticAnalyzer/Core/Checker.h" 17228072Sbapt#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 18228072Sbapt 19228072Sbaptusing namespace clang; 20228072Sbaptusing namespace ento; 21228072Sbaptusing namespace ast_matchers; 22228072Sbapt 23228072Sbaptnamespace { 24228072Sbapt 25228072Sbapt// ID of a node at which the diagnostic would be emitted. 26228072Sbaptconstexpr llvm::StringLiteral WarnAtNode = "iter"; 27228072Sbapt 28228072Sbaptclass PointerIterationChecker : public Checker<check::ASTCodeBody> { 29228072Sbaptpublic: 30228072Sbapt void checkASTCodeBody(const Decl *D, 31228072Sbapt AnalysisManager &AM, 32228072Sbapt BugReporter &BR) const; 33228072Sbapt}; 34228072Sbapt 35228072Sbaptstatic void emitDiagnostics(const BoundNodes &Match, const Decl *D, 36228072Sbapt BugReporter &BR, AnalysisManager &AM, 37228072Sbapt const PointerIterationChecker *Checker) { 38228072Sbapt auto *ADC = AM.getAnalysisDeclContext(D); 39250874Sjkim 40228072Sbapt const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode); 41228072Sbapt assert(MarkedStmt); 42228072Sbapt 43228072Sbapt auto Range = MarkedStmt->getSourceRange(); 44228072Sbapt auto Location = PathDiagnosticLocation::createBegin(MarkedStmt, 45228072Sbapt BR.getSourceManager(), 46228072Sbapt ADC); 47228072Sbapt std::string Diagnostics; 48228072Sbapt llvm::raw_string_ostream OS(Diagnostics); 49228072Sbapt OS << "Iteration of pointer-like elements " 50228072Sbapt << "can result in non-deterministic ordering"; 51228072Sbapt 52228072Sbapt BR.EmitBasicReport(ADC->getDecl(), Checker, 53228072Sbapt "Iteration of pointer-like elements", "Non-determinism", 54228072Sbapt OS.str(), Location, Range); 55228072Sbapt} 56228072Sbapt 57228072Sbapt// Assumption: Iteration of ordered containers of pointers is deterministic. 58228072Sbapt 59228072Sbapt// TODO: Currently, we only check for std::unordered_set. Other unordered 60228072Sbapt// containers like std::unordered_map also need to be handled. 61228072Sbapt 62228072Sbapt// TODO: Currently, we do not check what the for loop does with the iterated 63228072Sbapt// pointer values. Not all iterations may cause non-determinism. For example, 64228072Sbapt// counting or summing up the elements should not be non-deterministic. 65228072Sbapt 66228072Sbaptauto matchUnorderedIterWithPointers() -> decltype(decl()) { 67228072Sbapt 68228072Sbapt auto UnorderedContainerM = declRefExpr(to(varDecl(hasType( 69228072Sbapt recordDecl(hasName("std::unordered_set") 70228072Sbapt ))))); 71228072Sbapt 72228072Sbapt auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType()))); 73228072Sbapt 74228072Sbapt auto PointerIterM = stmt(cxxForRangeStmt( 75228072Sbapt hasLoopVariable(PointerTypeM), 76228072Sbapt hasRangeInit(UnorderedContainerM) 77228072Sbapt )).bind(WarnAtNode); 78228072Sbapt 79228072Sbapt return decl(forEachDescendant(PointerIterM)); 80228072Sbapt} 81228072Sbapt 82228072Sbaptvoid PointerIterationChecker::checkASTCodeBody(const Decl *D, 83228072Sbapt AnalysisManager &AM, 84228072Sbapt BugReporter &BR) const { 85228072Sbapt auto MatcherM = matchUnorderedIterWithPointers(); 86228072Sbapt 87228072Sbapt auto Matches = match(MatcherM, *D, AM.getASTContext()); 88228072Sbapt for (const auto &Match : Matches) 89228072Sbapt emitDiagnostics(Match, D, BR, AM, this); 90228072Sbapt} 91228072Sbapt 92228072Sbapt} // end of anonymous namespace 93228072Sbapt 94228072Sbaptvoid ento::registerPointerIterationChecker(CheckerManager &Mgr) { 95228072Sbapt Mgr.registerChecker<PointerIterationChecker>(); 96228072Sbapt} 97228072Sbapt 98228072Sbaptbool ento::shouldRegisterPointerIterationChecker(const LangOptions &LO) { 99228072Sbapt return LO.CPlusPlus; 100228072Sbapt} 101228072Sbapt