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