Index: clang-tidy/CMakeLists.txt =================================================================== --- clang-tidy/CMakeLists.txt +++ clang-tidy/CMakeLists.txt @@ -40,3 +40,4 @@ add_subdirectory(readability) add_subdirectory(tool) add_subdirectory(utils) +add_subdirectory(android) Index: clang-tidy/android/AndroidTidyModule.cpp =================================================================== --- clang-tidy/android/AndroidTidyModule.cpp +++ clang-tidy/android/AndroidTidyModule.cpp @@ -0,0 +1,53 @@ +//===--- AndroidTidyModule.cpp - clang-tidy +//--------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FileDescriptorCheck.h" +#include "../ClangTidy.h" +#include "../ClangTidyModule.h" +#include "../ClangTidyModuleRegistry.h" +#include "../readability/BracesAroundStatementsCheck.h" +#include "../readability/FunctionSizeCheck.h" +#include "../readability/NamespaceCommentCheck.h" +#include "../readability/RedundantSmartptrGetCheck.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace android { + +/// This module is for Android specific checks. + +class AndroidModule : public ClangTidyModule { +public: + void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "android-file-descriptor"); + } + + ClangTidyOptions getModuleOptions() override { + ClangTidyOptions Options; + // auto &Opts = Options.CheckOptions; + return Options; + } +}; + +// Register the AndroidTidyModule using this statically initialized variable. +static ClangTidyModuleRegistry::Add + X("android-module", "Adds Andrdoid lint checks."); + +} // namespace android + +// This anchor is used to force the linker to link in the generated object file +// and thus register the AndroidModule. +volatile int AndroidModuleAnchorSource = 0; + +} // namespace tidy +} // namespace clang Index: clang-tidy/android/CMakeLists.txt =================================================================== --- clang-tidy/android/CMakeLists.txt +++ clang-tidy/android/CMakeLists.txt @@ -0,0 +1,15 @@ +set(LLVM_LINK_COMPONENTS support) + +add_clang_library(clangTidyAndroidModule + AndroidTidyModule.cpp + FileDescriptorCheck.cpp + + LINK_LIBS + clangAST + clangASTMatchers + clangBasic + clangLex + clangTidy + clangTidyReadabilityModule + clangTidyUtils + ) Index: clang-tidy/android/FileDescriptorCheck.h =================================================================== --- clang-tidy/android/FileDescriptorCheck.h +++ clang-tidy/android/FileDescriptorCheck.h @@ -0,0 +1,45 @@ +//===--- FileDescriptorCheck.h - clang-tidy----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_FILE_DESCRIPTOR_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_FILE_DESCRIPTOR_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace android { + +/// Finds code that opens file descriptors without using the O_CLOEXEC flag. +/// +/// open(), openat(), and open64() must include O_CLOEXEC in their flags +/// argument. +/// Only consider simple cases that the corresponding argument is constant or +/// binary operation OR among constants like 'O_CLOEXEC' or 'O_CLOEXEC | +/// O_RDONLY'. No constant propagation is performed. + +class FileDescriptorCheck : public ClangTidyCheck { +public: + FileDescriptorCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool checkFlags(const Expr *Flags); + bool checkFlag(const IntegerLiteral *Flag); + +private: + static constexpr const char *FLAG = "O_CLOEXEC"; + static constexpr const char *HEADER_FILE = "fcntl.h"; +}; + +} // namespace android +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_GOOGLE_FILE_DESCRIPTOR_H Index: clang-tidy/android/FileDescriptorCheck.cpp =================================================================== --- clang-tidy/android/FileDescriptorCheck.cpp +++ clang-tidy/android/FileDescriptorCheck.cpp @@ -0,0 +1,102 @@ +//===--- FileDescriptorCheck.cpp - clang-tidy------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "FileDescriptorCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace android { + +void FileDescriptorCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + callExpr( + callee(functionDecl(allOf( + isExpansionInFileMatching(HEADER_FILE), returns(asString("int")), + hasParameter(1, hasType(isInteger())), + anyOf(matchesName("open"), matchesName("open64")))).bind("funcDecl"))) + .bind("openFn"), + this); + Finder->addMatcher( + callExpr(callee(functionDecl(allOf(isExpansionInFileMatching(HEADER_FILE), + returns(asString("int")), + hasParameter(2, hasType(isInteger())), + matchesName("openat"))).bind("funcDecl"))) + .bind("openatFn"), + this); +} + +void FileDescriptorCheck::check(const MatchFinder::MatchResult &Result) { + SourceManager &SM = *Result.SourceManager; + LangOptions LangOpts = getLangOpts(); + const CallExpr *MatchedCall; + const Expr *FlagArg; + if (MatchedCall = Result.Nodes.getNodeAs("openFn")) { + FlagArg = MatchedCall->getArg(1); + } else if (MatchedCall = Result.Nodes.getNodeAs("openatFn")) { + FlagArg = MatchedCall->getArg(2); + } else { + return; + } + + const auto *FD = Result.Nodes.getNodeAs("funcDecl"); + + if (!checkFlags(FlagArg)) { + SourceRange FlagsRange(FlagArg->getLocStart(), FlagArg->getLocEnd()); + + StringRef FlagsText = Lexer::getSourceText( + CharSourceRange::getTokenRange(FlagsRange), SM, LangOpts); + + std::string ReplacementText = + (llvm::Twine(FlagsText) + " | " + FileDescriptorCheck::FLAG).str(); + + diag(FlagArg->getLocStart(), + "%0 must include O_CLOEXEC in their flags argument.") + << FD->getName() + << FixItHint::CreateReplacement(FlagsRange, ReplacementText); + } +} + +// Check if a constant flag contains required flag. +bool FileDescriptorCheck::checkFlag(const IntegerLiteral *Flag) { + llvm::APInt aPInt = Flag->getValue(); + if (aPInt.isSignedIntN(aPInt.getBitWidth())) { + int64_t val = aPInt.getSExtValue(); + if ((val & O_CLOEXEC) != O_CLOEXEC) + return false; + } else { + uint64_t val = aPInt.getZExtValue(); + if ((val & O_CLOEXEC) != O_CLOEXEC) + return false; + } + return true; +} + +// Check if flags contain required flag. +bool FileDescriptorCheck::checkFlags(const Expr *Flags) { + if (isa(Flags)) { + return checkFlag(cast(Flags)); + } else if (isa(Flags)) { + if (cast(Flags)->getOpcode() == + clang::BinaryOperatorKind::BO_Or) { + return checkFlags(cast(Flags)->getLHS()) || + checkFlags(cast(Flags)->getRHS()); + } + } + return false; +} + +} // namespace android +} // namespace tidy +} // namespace clang Index: clang-tidy/plugin/CMakeLists.txt =================================================================== --- clang-tidy/plugin/CMakeLists.txt +++ clang-tidy/plugin/CMakeLists.txt @@ -8,6 +8,7 @@ clangFrontend clangSema clangTidy + clangTidyAndroidModule clangTidyBoostModule clangTidyCERTModule clangTidyCppCoreGuidelinesModule Index: clang-tidy/tool/CMakeLists.txt =================================================================== --- clang-tidy/tool/CMakeLists.txt +++ clang-tidy/tool/CMakeLists.txt @@ -13,6 +13,7 @@ clangASTMatchers clangBasic clangTidy + clangTidyAndroidModule clangTidyBoostModule clangTidyCERTModule clangTidyCppCoreGuidelinesModule Index: clang-tidy/tool/ClangTidyMain.cpp =================================================================== --- clang-tidy/tool/ClangTidyMain.cpp +++ clang-tidy/tool/ClangTidyMain.cpp @@ -477,6 +477,11 @@ static int LLVM_ATTRIBUTE_UNUSED GoogleModuleAnchorDestination = GoogleModuleAnchorSource; +// This anchor is used to force the linker to link the AndroidModule. +extern volatile int AndroidModuleAnchorSource; +static int LLVM_ATTRIBUTE_UNUSED AndroidModuleAnchorDestination = + AndroidModuleAnchorSource; + // This anchor is used to force the linker to link the MiscModule. extern volatile int MiscModuleAnchorSource; static int LLVM_ATTRIBUTE_UNUSED MiscModuleAnchorDestination = Index: test/clang-tidy/android-file-descriptor.cpp =================================================================== --- test/clang-tidy/android-file-descriptor.cpp +++ test/clang-tidy/android-file-descriptor.cpp @@ -0,0 +1,54 @@ +// RUN: %check_clang_tidy %s android-file-descriptor %t + +#include + +void a() { + open("filename", O_RDWR); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: open must include O_CLOEXEC in their flags argument. [android-file-descriptor] + // CHECK-FIXES: O_RDWR | O_CLOEXEC + open("filename", O_RDWR | O_EXCL); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: open must include O_CLOEXEC in their flags argument. [android-file-descriptor] + // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC +} + +void b() { + open64("filename", O_RDWR); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: open64 must include O_CLOEXEC in their flags argument. [android-file-descriptor] + // CHECK-FIXES: O_RDWR | O_CLOEXEC + open64("filename", O_RDWR | O_EXCL); + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: open64 must include O_CLOEXEC in their flags argument. [android-file-descriptor] + // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC +} + +void c() { + openat(0, "filename", O_RDWR); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: openat must include O_CLOEXEC in their flags argument. [android-file-descriptor] + // CHECK-FIXES: O_RDWR | O_CLOEXEC + openat(0, "filename", O_RDWR | O_EXCL); + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: openat must include O_CLOEXEC in their flags argument. [android-file-descriptor] + // CHECK-FIXES: O_RDWR | O_EXCL | O_CLOEXEC +} + +namespace i { +int open(const char *pathname, int flags) { return 0; } +int open(const char *pathname, int flags, mode_t mode) { return 0; } +int open64(const char *pathname, int flags) { return 0; } +int open64(const char *pathname, int flags, mode_t mode) { return 0; } +int openat(int dirfd, const char *pathname, int flags) { return 0; } +int openat(int dirfd, const char *pathname, int flags, mode_t mode) { return 0; } +} // namespace i + +void d() { + i::open("filename", O_RDWR); + i::open64("filename", O_RDWR); + i::openat(0, "filename", O_RDWR); +} + +void e() { + open("filename", O_CLOEXEC); + open("filename", O_RDWR | O_CLOEXEC); + open64("filename", O_CLOEXEC); + open64("filename", O_RDWR | O_CLOEXEC); + openat(0, "filename", O_CLOEXEC); + openat(0, "filename", O_RDWR | O_CLOEXEC); +} Index: unittests/clang-tidy/CMakeLists.txt =================================================================== --- unittests/clang-tidy/CMakeLists.txt +++ unittests/clang-tidy/CMakeLists.txt @@ -26,6 +26,7 @@ clangLex clangTidy clangTidyGoogleModule + clangTidyAndroidModule clangTidyLLVMModule clangTidyMiscModule clangTidyReadabilityModule