• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===--- NonTrivialTypesLibcMemoryCallsCheck.cpp - clang-tidy ----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "NonTrivialTypesLibcMemoryCallsCheck.h"
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/ASTMatchers/ASTMatchersInternal.h"
15 #include "clang/ASTMatchers/ASTMatchersMacros.h"
16 #include "llvm/ADT/SmallVector.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/raw_ostream.h"
19 
20 using namespace clang::ast_matchers;
21 
22 namespace clang {
23 namespace tidy {
24 namespace cert {
25 
26 namespace {
AST_MATCHER(CXXRecordDecl,isTriviallyDefaultConstructible)27 AST_MATCHER(CXXRecordDecl, isTriviallyDefaultConstructible) {
28   return Node.hasTrivialDefaultConstructor();
29 }
AST_MATCHER(CXXRecordDecl,isTriviallyCopyable)30 AST_MATCHER(CXXRecordDecl, isTriviallyCopyable) {
31   return Node.hasTrivialCopyAssignment() && Node.hasTrivialCopyConstructor();
32 }
AST_MATCHER_P(NamedDecl,hasAnyNameStdString,std::vector<std::string>,String)33 AST_MATCHER_P(NamedDecl, hasAnyNameStdString, std::vector<std::string>,
34               String) {
35   return ast_matchers::internal::HasNameMatcher(String).matchesNode(Node);
36 }
37 } // namespace
38 
39 static const char BuiltinMemSet[] = "::std::memset;"
40                                     "::memset;";
41 static const char BuiltinMemCpy[] = "::std::memcpy;"
42                                     "::memcpy;"
43                                     "::std::memmove;"
44                                     "::memmove;"
45                                     "::std::strcpy;"
46                                     "::strcpy;"
47                                     "::memccpy;"
48                                     "::stpncpy;"
49                                     "::strncpy;";
50 static const char BuiltinMemCmp[] = "::std::memcmp;"
51                                     "::memcmp;"
52                                     "::std::strcmp;"
53                                     "::strcmp;"
54                                     "::strncmp;";
55 static constexpr llvm::StringRef ComparisonOperators[] = {
56     "operator==", "operator!=", "operator<",
57     "operator>",  "operator<=", "operator>="};
58 
parseStringListPair(StringRef LHS,StringRef RHS)59 static std::vector<std::string> parseStringListPair(StringRef LHS,
60                                                     StringRef RHS) {
61   if (LHS.empty()) {
62     if (RHS.empty())
63       return {};
64     return utils::options::parseStringList(RHS);
65   }
66   if (RHS.empty())
67     return utils::options::parseStringList(LHS);
68   llvm::SmallString<512> Buffer;
69   return utils::options::parseStringList((LHS + RHS).toStringRef(Buffer));
70 }
71 
NonTrivialTypesLibcMemoryCallsCheck(StringRef Name,ClangTidyContext * Context)72 NonTrivialTypesLibcMemoryCallsCheck::NonTrivialTypesLibcMemoryCallsCheck(
73     StringRef Name, ClangTidyContext *Context)
74     : ClangTidyCheck(Name, Context),
75       MemSetNames(Options.get("MemSetNames", "")),
76       MemCpyNames(Options.get("MemCpyNames", "")),
77       MemCmpNames(Options.get("MemCmpNames", "")) {}
78 
storeOptions(ClangTidyOptions::OptionMap & Opts)79 void NonTrivialTypesLibcMemoryCallsCheck::storeOptions(
80     ClangTidyOptions::OptionMap &Opts) {
81   Options.store(Opts, "MemSetNames", MemSetNames);
82   Options.store(Opts, "MemCpyNames", MemCpyNames);
83   Options.store(Opts, "MemCmpNames", MemCmpNames);
84 }
85 
registerMatchers(MatchFinder * Finder)86 void NonTrivialTypesLibcMemoryCallsCheck::registerMatchers(
87     MatchFinder *Finder) {
88   using namespace ast_matchers::internal;
89   auto IsStructPointer = [](Matcher<CXXRecordDecl> Constraint = anything(),
90                             bool Bind = false) {
91     return expr(unaryOperator(
92         hasOperatorName("&"),
93         hasUnaryOperand(declRefExpr(
94             allOf(hasType(cxxRecordDecl(Constraint)),
95                   hasType(Bind ? qualType().bind("Record") : qualType()))))));
96   };
97   auto IsRecordSizeOf =
98       expr(sizeOfExpr(hasArgumentOfType(equalsBoundNode("Record"))));
99   auto ArgChecker = [&](Matcher<CXXRecordDecl> RecordConstraint,
100                         BindableMatcher<Stmt> SecondArg) {
101     return allOf(argumentCountIs(3),
102                  hasArgument(0, IsStructPointer(RecordConstraint, true)),
103                  hasArgument(1, SecondArg), hasArgument(2, IsRecordSizeOf));
104   };
105 
106   Finder->addMatcher(
107       callExpr(callee(namedDecl(hasAnyNameStdString(
108                    parseStringListPair(BuiltinMemSet, MemSetNames)))),
109                ArgChecker(unless(isTriviallyDefaultConstructible()),
110                           expr(integerLiteral(equals(0)))))
111           .bind("lazyConstruct"),
112       this);
113   Finder->addMatcher(
114       callExpr(callee(namedDecl(hasAnyNameStdString(
115                    parseStringListPair(BuiltinMemCpy, MemCpyNames)))),
116                ArgChecker(unless(isTriviallyCopyable()), IsStructPointer()))
117           .bind("lazyCopy"),
118       this);
119   Finder->addMatcher(
120       callExpr(callee(namedDecl(hasAnyNameStdString(
121                    parseStringListPair(BuiltinMemCmp, MemCmpNames)))),
122                ArgChecker(hasMethod(hasAnyName(ComparisonOperators)),
123                           IsStructPointer()))
124           .bind("lazyCompare"),
125       this);
126 }
127 
check(const MatchFinder::MatchResult & Result)128 void NonTrivialTypesLibcMemoryCallsCheck::check(
129     const MatchFinder::MatchResult &Result) {
130   if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyConstruct")) {
131     diag(Caller->getBeginLoc(), "calling %0 on a non-trivially default "
132                                 "constructible class is undefined")
133         << cast<NamedDecl>(Caller->getCalleeDecl());
134   }
135   if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyCopy")) {
136     diag(Caller->getBeginLoc(),
137          "calling %0 on a non-trivially copyable class is undefined")
138         << cast<NamedDecl>(Caller->getCalleeDecl());
139   }
140   if (const auto *Caller = Result.Nodes.getNodeAs<CallExpr>("lazyCompare")) {
141     diag(Caller->getBeginLoc(),
142          "consider using comparison operators instead of calling %0")
143         << cast<NamedDecl>(Caller->getCalleeDecl());
144   }
145 }
146 
147 } // namespace cert
148 } // namespace tidy
149 } // namespace clang
150