|  | //===--- MtUnsafeCheck.cpp - clang-tidy -----------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "MtUnsafeCheck.h" | 
|  | #include "clang/AST/ASTContext.h" | 
|  | #include "clang/ASTMatchers/ASTMatchFinder.h" | 
|  |  | 
|  | using namespace clang::ast_matchers; | 
|  |  | 
|  | // Initial list was extracted from gcc documentation | 
|  | static const clang::StringRef GlibcFunctions[] = { | 
|  | "::argp_error", | 
|  | "::argp_help", | 
|  | "::argp_parse", | 
|  | "::argp_state_help", | 
|  | "::argp_usage", | 
|  | "::asctime", | 
|  | "::clearenv", | 
|  | "::crypt", | 
|  | "::ctime", | 
|  | "::cuserid", | 
|  | "::drand48", | 
|  | "::ecvt", | 
|  | "::encrypt", | 
|  | "::endfsent", | 
|  | "::endgrent", | 
|  | "::endhostent", | 
|  | "::endnetent", | 
|  | "::endnetgrent", | 
|  | "::endprotoent", | 
|  | "::endpwent", | 
|  | "::endservent", | 
|  | "::endutent", | 
|  | "::endutxent", | 
|  | "::erand48", | 
|  | "::error_at_line", | 
|  | "::exit", | 
|  | "::fcloseall", | 
|  | "::fcvt", | 
|  | "::fgetgrent", | 
|  | "::fgetpwent", | 
|  | "::gammal", | 
|  | "::getchar_unlocked", | 
|  | "::getdate", | 
|  | "::getfsent", | 
|  | "::getfsfile", | 
|  | "::getfsspec", | 
|  | "::getgrent", | 
|  | "::getgrent_r", | 
|  | "::getgrgid", | 
|  | "::getgrnam", | 
|  | "::gethostbyaddr", | 
|  | "::gethostbyname", | 
|  | "::gethostbyname2", | 
|  | "::gethostent", | 
|  | "::getlogin", | 
|  | "::getmntent", | 
|  | "::getnetbyaddr", | 
|  | "::getnetbyname", | 
|  | "::getnetent", | 
|  | "::getnetgrent", | 
|  | "::getnetgrent_r", | 
|  | "::getopt", | 
|  | "::getopt_long", | 
|  | "::getopt_long_only", | 
|  | "::getpass", | 
|  | "::getprotobyname", | 
|  | "::getprotobynumber", | 
|  | "::getprotoent", | 
|  | "::getpwent", | 
|  | "::getpwent_r", | 
|  | "::getpwnam", | 
|  | "::getpwuid", | 
|  | "::getservbyname", | 
|  | "::getservbyport", | 
|  | "::getservent", | 
|  | "::getutent", | 
|  | "::getutent_r", | 
|  | "::getutid", | 
|  | "::getutid_r", | 
|  | "::getutline", | 
|  | "::getutline_r", | 
|  | "::getutxent", | 
|  | "::getutxid", | 
|  | "::getutxline", | 
|  | "::getwchar_unlocked", | 
|  | "::glob", | 
|  | "::glob64", | 
|  | "::gmtime", | 
|  | "::hcreate", | 
|  | "::hdestroy", | 
|  | "::hsearch", | 
|  | "::innetgr", | 
|  | "::jrand48", | 
|  | "::l64a", | 
|  | "::lcong48", | 
|  | "::lgammafNx", | 
|  | "::localeconv", | 
|  | "::localtime", | 
|  | "::login", | 
|  | "::login_tty", | 
|  | "::logout", | 
|  | "::logwtmp", | 
|  | "::lrand48", | 
|  | "::mallinfo", | 
|  | "::mallopt", | 
|  | "::mblen", | 
|  | "::mbrlen", | 
|  | "::mbrtowc", | 
|  | "::mbsnrtowcs", | 
|  | "::mbsrtowcs", | 
|  | "::mbtowc", | 
|  | "::mcheck", | 
|  | "::mprobe", | 
|  | "::mrand48", | 
|  | "::mtrace", | 
|  | "::muntrace", | 
|  | "::nrand48", | 
|  | "::__ppc_get_timebase_freq", | 
|  | "::ptsname", | 
|  | "::putchar_unlocked", | 
|  | "::putenv", | 
|  | "::pututline", | 
|  | "::pututxline", | 
|  | "::putwchar_unlocked", | 
|  | "::qecvt", | 
|  | "::qfcvt", | 
|  | "::register_printf_function", | 
|  | "::seed48", | 
|  | "::setenv", | 
|  | "::setfsent", | 
|  | "::setgrent", | 
|  | "::sethostent", | 
|  | "::sethostid", | 
|  | "::setkey", | 
|  | "::setlocale", | 
|  | "::setlogmask", | 
|  | "::setnetent", | 
|  | "::setnetgrent", | 
|  | "::setprotoent", | 
|  | "::setpwent", | 
|  | "::setservent", | 
|  | "::setutent", | 
|  | "::setutxent", | 
|  | "::siginterrupt", | 
|  | "::sigpause", | 
|  | "::sigprocmask", | 
|  | "::sigsuspend", | 
|  | "::sleep", | 
|  | "::srand48", | 
|  | "::strerror", | 
|  | "::strsignal", | 
|  | "::strtok", | 
|  | "::tcflow", | 
|  | "::tcsendbreak", | 
|  | "::tmpnam", | 
|  | "::ttyname", | 
|  | "::unsetenv", | 
|  | "::updwtmp", | 
|  | "::utmpname", | 
|  | "::utmpxname", | 
|  | "::valloc", | 
|  | "::vlimit", | 
|  | "::wcrtomb", | 
|  | "::wcsnrtombs", | 
|  | "::wcsrtombs", | 
|  | "::wctomb", | 
|  | "::wordexp", | 
|  | }; | 
|  |  | 
|  | static const clang::StringRef PosixFunctions[] = { | 
|  | "::asctime", | 
|  | "::basename", | 
|  | "::catgets", | 
|  | "::crypt", | 
|  | "::ctime", | 
|  | "::dbm_clearerr", | 
|  | "::dbm_close", | 
|  | "::dbm_delete", | 
|  | "::dbm_error", | 
|  | "::dbm_fetch", | 
|  | "::dbm_firstkey", | 
|  | "::dbm_nextkey", | 
|  | "::dbm_open", | 
|  | "::dbm_store", | 
|  | "::dirname", | 
|  | "::dlerror", | 
|  | "::drand48", | 
|  | "::encrypt", | 
|  | "::endgrent", | 
|  | "::endpwent", | 
|  | "::endutxent", | 
|  | "::ftw", | 
|  | "::getc_unlocked", | 
|  | "::getchar_unlocked", | 
|  | "::getdate", | 
|  | "::getenv", | 
|  | "::getgrent", | 
|  | "::getgrgid", | 
|  | "::getgrnam", | 
|  | "::gethostent", | 
|  | "::getlogin", | 
|  | "::getnetbyaddr", | 
|  | "::getnetbyname", | 
|  | "::getnetent", | 
|  | "::getopt", | 
|  | "::getprotobyname", | 
|  | "::getprotobynumber", | 
|  | "::getprotoent", | 
|  | "::getpwent", | 
|  | "::getpwnam", | 
|  | "::getpwuid", | 
|  | "::getservbyname", | 
|  | "::getservbyport", | 
|  | "::getservent", | 
|  | "::getutxent", | 
|  | "::getutxid", | 
|  | "::getutxline", | 
|  | "::gmtime", | 
|  | "::hcreate", | 
|  | "::hdestroy", | 
|  | "::hsearch", | 
|  | "::inet_ntoa", | 
|  | "::l64a", | 
|  | "::lgamma", | 
|  | "::lgammaf", | 
|  | "::lgammal", | 
|  | "::localeconv", | 
|  | "::localtime", | 
|  | "::lrand48", | 
|  | "::mrand48", | 
|  | "::nftw", | 
|  | "::nl_langinfo", | 
|  | "::ptsname", | 
|  | "::putc_unlocked", | 
|  | "::putchar_unlocked", | 
|  | "::putenv", | 
|  | "::pututxline", | 
|  | "::rand", | 
|  | "::readdir", | 
|  | "::setenv", | 
|  | "::setgrent", | 
|  | "::setkey", | 
|  | "::setpwent", | 
|  | "::setutxent", | 
|  | "::strerror", | 
|  | "::strsignal", | 
|  | "::strtok", | 
|  | "::system", | 
|  | "::ttyname", | 
|  | "::unsetenv", | 
|  | "::wcstombs", | 
|  | "::wctomb", | 
|  | }; | 
|  |  | 
|  | namespace clang { | 
|  | namespace tidy { | 
|  |  | 
|  | template <> struct OptionEnumMapping<concurrency::MtUnsafeCheck::FunctionSet> { | 
|  | static llvm::ArrayRef< | 
|  | std::pair<concurrency::MtUnsafeCheck::FunctionSet, StringRef>> | 
|  | getEnumMapping() { | 
|  | static constexpr std::pair<concurrency::MtUnsafeCheck::FunctionSet, | 
|  | StringRef> | 
|  | Mapping[] = {{concurrency::MtUnsafeCheck::FunctionSet::Posix, "posix"}, | 
|  | {concurrency::MtUnsafeCheck::FunctionSet::Glibc, "glibc"}, | 
|  | {concurrency::MtUnsafeCheck::FunctionSet::Any, "any"}}; | 
|  | return makeArrayRef(Mapping); | 
|  | } | 
|  | }; | 
|  |  | 
|  | namespace concurrency { | 
|  |  | 
|  | static ast_matchers::internal::Matcher<clang::NamedDecl> | 
|  | hasAnyMtUnsafeNames(MtUnsafeCheck::FunctionSet Libc) { | 
|  | switch (Libc) { | 
|  | case MtUnsafeCheck::FunctionSet::Posix: | 
|  | return hasAnyName(PosixFunctions); | 
|  | case MtUnsafeCheck::FunctionSet::Glibc: | 
|  | return hasAnyName(GlibcFunctions); | 
|  | case MtUnsafeCheck::FunctionSet::Any: | 
|  | return anyOf(hasAnyName(PosixFunctions), hasAnyName(GlibcFunctions)); | 
|  | } | 
|  | llvm_unreachable("invalid FunctionSet"); | 
|  | } | 
|  |  | 
|  | MtUnsafeCheck::MtUnsafeCheck(StringRef Name, ClangTidyContext *Context) | 
|  | : ClangTidyCheck(Name, Context), | 
|  | FuncSet(Options.get("FunctionSet", MtUnsafeCheck::FunctionSet::Any)) {} | 
|  |  | 
|  | void MtUnsafeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { | 
|  | Options.store(Opts, "FunctionSet", FuncSet); | 
|  | } | 
|  |  | 
|  | void MtUnsafeCheck::registerMatchers(MatchFinder *Finder) { | 
|  | Finder->addMatcher( | 
|  | callExpr(callee(functionDecl(hasAnyMtUnsafeNames(FuncSet)))) | 
|  | .bind("mt-unsafe"), | 
|  | this); | 
|  | } | 
|  |  | 
|  | void MtUnsafeCheck::check(const MatchFinder::MatchResult &Result) { | 
|  | const auto *Call = Result.Nodes.getNodeAs<CallExpr>("mt-unsafe"); | 
|  | assert(Call && "Unhandled binding in the Matcher"); | 
|  |  | 
|  | diag(Call->getBeginLoc(), "function is not thread safe"); | 
|  | } | 
|  |  | 
|  | } // namespace concurrency | 
|  | } // namespace tidy | 
|  | } // namespace clang |