1cf7d9f90SMandeep Singh Grang //== PointerSortingChecker.cpp --------------------------------- -*- C++ -*--=//
2c0773ab6SMandeep Singh Grang //
3cf7d9f90SMandeep Singh Grang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4cf7d9f90SMandeep Singh Grang // See https://llvm.org/LICENSE.txt for license information.
5cf7d9f90SMandeep Singh Grang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c0773ab6SMandeep Singh Grang //
7c0773ab6SMandeep Singh Grang //===----------------------------------------------------------------------===//
8c0773ab6SMandeep Singh Grang //
9c0773ab6SMandeep Singh Grang // This file defines PointerSortingChecker which checks for non-determinism
10c0773ab6SMandeep Singh Grang // caused due to sorting containers with pointer-like elements.
11c0773ab6SMandeep Singh Grang //
12c0773ab6SMandeep Singh Grang //===----------------------------------------------------------------------===//
13c0773ab6SMandeep Singh Grang 
14c0773ab6SMandeep Singh Grang #include "clang/ASTMatchers/ASTMatchFinder.h"
15c0773ab6SMandeep Singh Grang #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16c0773ab6SMandeep Singh Grang #include "clang/StaticAnalyzer/Core/Checker.h"
17c0773ab6SMandeep Singh Grang #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
18c0773ab6SMandeep Singh Grang 
19c0773ab6SMandeep Singh Grang using namespace clang;
20c0773ab6SMandeep Singh Grang using namespace ento;
21c0773ab6SMandeep Singh Grang using namespace ast_matchers;
22c0773ab6SMandeep Singh Grang 
23c0773ab6SMandeep Singh Grang namespace {
24c0773ab6SMandeep Singh Grang 
25c0773ab6SMandeep Singh Grang // ID of a node at which the diagnostic would be emitted.
26c0773ab6SMandeep Singh Grang constexpr llvm::StringLiteral WarnAtNode = "sort";
27c0773ab6SMandeep Singh Grang 
28c0773ab6SMandeep Singh Grang class PointerSortingChecker : public Checker<check::ASTCodeBody> {
29c0773ab6SMandeep Singh Grang public:
30c0773ab6SMandeep Singh Grang   void checkASTCodeBody(const Decl *D,
31c0773ab6SMandeep Singh Grang                         AnalysisManager &AM,
32c0773ab6SMandeep Singh Grang                         BugReporter &BR) const;
33c0773ab6SMandeep Singh Grang };
34c0773ab6SMandeep Singh Grang 
emitDiagnostics(const BoundNodes & Match,const Decl * D,BugReporter & BR,AnalysisManager & AM,const PointerSortingChecker * Checker)35c0773ab6SMandeep Singh Grang static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
36c0773ab6SMandeep Singh Grang                             BugReporter &BR, AnalysisManager &AM,
37c0773ab6SMandeep Singh Grang                             const PointerSortingChecker *Checker) {
38c0773ab6SMandeep Singh Grang   auto *ADC = AM.getAnalysisDeclContext(D);
39c0773ab6SMandeep Singh Grang 
40c0773ab6SMandeep Singh Grang   const auto *MarkedStmt = Match.getNodeAs<CallExpr>(WarnAtNode);
41c0773ab6SMandeep Singh Grang   assert(MarkedStmt);
42c0773ab6SMandeep Singh Grang 
43c0773ab6SMandeep Singh Grang   auto Range = MarkedStmt->getSourceRange();
44c0773ab6SMandeep Singh Grang   auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
45c0773ab6SMandeep Singh Grang                                                       BR.getSourceManager(),
46c0773ab6SMandeep Singh Grang                                                       ADC);
47c0773ab6SMandeep Singh Grang   std::string Diagnostics;
48c0773ab6SMandeep Singh Grang   llvm::raw_string_ostream OS(Diagnostics);
49c0773ab6SMandeep Singh Grang   OS << "Sorting pointer-like elements "
50c0773ab6SMandeep Singh Grang      << "can result in non-deterministic ordering";
51c0773ab6SMandeep Singh Grang 
52c0773ab6SMandeep Singh Grang   BR.EmitBasicReport(ADC->getDecl(), Checker,
53c0773ab6SMandeep Singh Grang                      "Sorting of pointer-like elements", "Non-determinism",
54c0773ab6SMandeep Singh Grang                      OS.str(), Location, Range);
55c0773ab6SMandeep Singh Grang }
56c0773ab6SMandeep Singh Grang 
callsName(const char * FunctionName)57ac66c61bSJustin Lebar decltype(auto) callsName(const char *FunctionName) {
58c0773ab6SMandeep Singh Grang   return callee(functionDecl(hasName(FunctionName)));
59c0773ab6SMandeep Singh Grang }
60c0773ab6SMandeep Singh Grang 
61c0773ab6SMandeep Singh Grang // FIXME: Currently we simply check if std::sort is used with pointer-like
62c0773ab6SMandeep Singh Grang // elements. This approach can have a big false positive rate. Using std::sort,
63c0773ab6SMandeep Singh Grang // std::unique and then erase is common technique for deduplicating a container
64c0773ab6SMandeep Singh Grang // (which in some cases might even be quicker than using, let's say std::set).
65c0773ab6SMandeep Singh Grang // In case a container contains arbitrary memory addresses (e.g. multiple
66c0773ab6SMandeep Singh Grang // things give different stuff but might give the same thing multiple times)
67c0773ab6SMandeep Singh Grang // which we don't want to do things with more than once, we might use
68c0773ab6SMandeep Singh Grang // sort-unique-erase and the sort call will emit a report.
matchSortWithPointers()69c0773ab6SMandeep Singh Grang auto matchSortWithPointers() -> decltype(decl()) {
70c0773ab6SMandeep Singh Grang   // Match any of these function calls.
71c0773ab6SMandeep Singh Grang   auto SortFuncM = anyOf(
72c0773ab6SMandeep Singh Grang                      callsName("std::is_sorted"),
73c0773ab6SMandeep Singh Grang                      callsName("std::nth_element"),
74c0773ab6SMandeep Singh Grang                      callsName("std::partial_sort"),
75c0773ab6SMandeep Singh Grang                      callsName("std::partition"),
76c0773ab6SMandeep Singh Grang                      callsName("std::sort"),
77c0773ab6SMandeep Singh Grang                      callsName("std::stable_partition"),
78c0773ab6SMandeep Singh Grang                      callsName("std::stable_sort")
79c0773ab6SMandeep Singh Grang                     );
80c0773ab6SMandeep Singh Grang 
81c0773ab6SMandeep Singh Grang   // Match only if the container has pointer-type elements.
82c0773ab6SMandeep Singh Grang   auto IteratesPointerEltsM = hasArgument(0,
83c0773ab6SMandeep Singh Grang                                 hasType(cxxRecordDecl(has(
84c0773ab6SMandeep Singh Grang                                   fieldDecl(hasType(hasCanonicalType(
85c0773ab6SMandeep Singh Grang                                     pointsTo(hasCanonicalType(pointerType()))
86c0773ab6SMandeep Singh Grang                                   )))
87c0773ab6SMandeep Singh Grang                               ))));
88c0773ab6SMandeep Singh Grang 
89*8d62eba1SStephen Kelly   auto PointerSortM = traverse(
90*8d62eba1SStephen Kelly       TK_AsIs,
91*8d62eba1SStephen Kelly       stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM))).bind(WarnAtNode));
92c0773ab6SMandeep Singh Grang 
93c0773ab6SMandeep Singh Grang   return decl(forEachDescendant(PointerSortM));
94c0773ab6SMandeep Singh Grang }
95c0773ab6SMandeep Singh Grang 
checkASTCodeBody(const Decl * D,AnalysisManager & AM,BugReporter & BR) const96c0773ab6SMandeep Singh Grang void PointerSortingChecker::checkASTCodeBody(const Decl *D,
97c0773ab6SMandeep Singh Grang                                              AnalysisManager &AM,
98c0773ab6SMandeep Singh Grang                                              BugReporter &BR) const {
99c0773ab6SMandeep Singh Grang   auto MatcherM = matchSortWithPointers();
100c0773ab6SMandeep Singh Grang 
101c0773ab6SMandeep Singh Grang   auto Matches = match(MatcherM, *D, AM.getASTContext());
102c0773ab6SMandeep Singh Grang   for (const auto &Match : Matches)
103c0773ab6SMandeep Singh Grang     emitDiagnostics(Match, D, BR, AM, this);
104c0773ab6SMandeep Singh Grang }
105c0773ab6SMandeep Singh Grang 
106c0773ab6SMandeep Singh Grang } // end of anonymous namespace
107c0773ab6SMandeep Singh Grang 
registerPointerSortingChecker(CheckerManager & Mgr)108c0773ab6SMandeep Singh Grang void ento::registerPointerSortingChecker(CheckerManager &Mgr) {
109c0773ab6SMandeep Singh Grang   Mgr.registerChecker<PointerSortingChecker>();
110c0773ab6SMandeep Singh Grang }
111c0773ab6SMandeep Singh Grang 
shouldRegisterPointerSortingChecker(const CheckerManager & mgr)112bda3dd0dSKirstóf Umann bool ento::shouldRegisterPointerSortingChecker(const CheckerManager &mgr) {
113bda3dd0dSKirstóf Umann   const LangOptions &LO = mgr.getLangOpts();
114c0773ab6SMandeep Singh Grang   return LO.CPlusPlus;
115c0773ab6SMandeep Singh Grang }
116