| 1 | //===--- MtUnsafeCheck.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 "MtUnsafeCheck.h" |
| 10 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 11 | |
| 12 | using namespace clang::ast_matchers; |
| 13 | |
| 14 | // Initial list was extracted from gcc documentation |
| 15 | static const clang::StringRef GlibcFunctions[] = { |
| 16 | "::argp_error" , |
| 17 | "::argp_help" , |
| 18 | "::argp_parse" , |
| 19 | "::argp_state_help" , |
| 20 | "::argp_usage" , |
| 21 | "::asctime" , |
| 22 | "::clearenv" , |
| 23 | "::crypt" , |
| 24 | "::ctime" , |
| 25 | "::cuserid" , |
| 26 | "::drand48" , |
| 27 | "::ecvt" , |
| 28 | "::encrypt" , |
| 29 | "::endfsent" , |
| 30 | "::endgrent" , |
| 31 | "::endhostent" , |
| 32 | "::endnetent" , |
| 33 | "::endnetgrent" , |
| 34 | "::endprotoent" , |
| 35 | "::endpwent" , |
| 36 | "::endservent" , |
| 37 | "::endutent" , |
| 38 | "::endutxent" , |
| 39 | "::erand48" , |
| 40 | "::error_at_line" , |
| 41 | "::exit" , |
| 42 | "::fcloseall" , |
| 43 | "::fcvt" , |
| 44 | "::fgetgrent" , |
| 45 | "::fgetpwent" , |
| 46 | "::gammal" , |
| 47 | "::getchar_unlocked" , |
| 48 | "::getdate" , |
| 49 | "::getfsent" , |
| 50 | "::getfsfile" , |
| 51 | "::getfsspec" , |
| 52 | "::getgrent" , |
| 53 | "::getgrent_r" , |
| 54 | "::getgrgid" , |
| 55 | "::getgrnam" , |
| 56 | "::gethostbyaddr" , |
| 57 | "::gethostbyname" , |
| 58 | "::gethostbyname2" , |
| 59 | "::gethostent" , |
| 60 | "::getlogin" , |
| 61 | "::getmntent" , |
| 62 | "::getnetbyaddr" , |
| 63 | "::getnetbyname" , |
| 64 | "::getnetent" , |
| 65 | "::getnetgrent" , |
| 66 | "::getnetgrent_r" , |
| 67 | "::getopt" , |
| 68 | "::getopt_long" , |
| 69 | "::getopt_long_only" , |
| 70 | "::getpass" , |
| 71 | "::getprotobyname" , |
| 72 | "::getprotobynumber" , |
| 73 | "::getprotoent" , |
| 74 | "::getpwent" , |
| 75 | "::getpwent_r" , |
| 76 | "::getpwnam" , |
| 77 | "::getpwuid" , |
| 78 | "::getservbyname" , |
| 79 | "::getservbyport" , |
| 80 | "::getservent" , |
| 81 | "::getutent" , |
| 82 | "::getutent_r" , |
| 83 | "::getutid" , |
| 84 | "::getutid_r" , |
| 85 | "::getutline" , |
| 86 | "::getutline_r" , |
| 87 | "::getutxent" , |
| 88 | "::getutxid" , |
| 89 | "::getutxline" , |
| 90 | "::getwchar_unlocked" , |
| 91 | "::glob" , |
| 92 | "::glob64" , |
| 93 | "::gmtime" , |
| 94 | "::hcreate" , |
| 95 | "::hdestroy" , |
| 96 | "::hsearch" , |
| 97 | "::innetgr" , |
| 98 | "::jrand48" , |
| 99 | "::l64a" , |
| 100 | "::lcong48" , |
| 101 | "::lgammafNx" , |
| 102 | "::localeconv" , |
| 103 | "::localtime" , |
| 104 | "::login" , |
| 105 | "::login_tty" , |
| 106 | "::logout" , |
| 107 | "::logwtmp" , |
| 108 | "::lrand48" , |
| 109 | "::mallinfo" , |
| 110 | "::mallopt" , |
| 111 | "::mblen" , |
| 112 | "::mbrlen" , |
| 113 | "::mbrtowc" , |
| 114 | "::mbsnrtowcs" , |
| 115 | "::mbsrtowcs" , |
| 116 | "::mbtowc" , |
| 117 | "::mcheck" , |
| 118 | "::mprobe" , |
| 119 | "::mrand48" , |
| 120 | "::mtrace" , |
| 121 | "::muntrace" , |
| 122 | "::nrand48" , |
| 123 | "::__ppc_get_timebase_freq" , |
| 124 | "::ptsname" , |
| 125 | "::putchar_unlocked" , |
| 126 | "::putenv" , |
| 127 | "::pututline" , |
| 128 | "::pututxline" , |
| 129 | "::putwchar_unlocked" , |
| 130 | "::qecvt" , |
| 131 | "::qfcvt" , |
| 132 | "::register_printf_function" , |
| 133 | "::seed48" , |
| 134 | "::setenv" , |
| 135 | "::setfsent" , |
| 136 | "::setgrent" , |
| 137 | "::sethostent" , |
| 138 | "::sethostid" , |
| 139 | "::setkey" , |
| 140 | "::setlocale" , |
| 141 | "::setlogmask" , |
| 142 | "::setnetent" , |
| 143 | "::setnetgrent" , |
| 144 | "::setprotoent" , |
| 145 | "::setpwent" , |
| 146 | "::setservent" , |
| 147 | "::setutent" , |
| 148 | "::setutxent" , |
| 149 | "::siginterrupt" , |
| 150 | "::sigpause" , |
| 151 | "::sigprocmask" , |
| 152 | "::sigsuspend" , |
| 153 | "::sleep" , |
| 154 | "::srand48" , |
| 155 | "::strsignal" , |
| 156 | "::strtok" , |
| 157 | "::tcflow" , |
| 158 | "::tcsendbreak" , |
| 159 | "::tmpnam" , |
| 160 | "::ttyname" , |
| 161 | "::unsetenv" , |
| 162 | "::updwtmp" , |
| 163 | "::utmpname" , |
| 164 | "::utmpxname" , |
| 165 | "::valloc" , |
| 166 | "::vlimit" , |
| 167 | "::wcrtomb" , |
| 168 | "::wcsnrtombs" , |
| 169 | "::wcsrtombs" , |
| 170 | "::wctomb" , |
| 171 | "::wordexp" , |
| 172 | }; |
| 173 | |
| 174 | static const clang::StringRef PosixFunctions[] = { |
| 175 | "::asctime" , |
| 176 | "::basename" , |
| 177 | "::catgets" , |
| 178 | "::crypt" , |
| 179 | "::ctime" , |
| 180 | "::dbm_clearerr" , |
| 181 | "::dbm_close" , |
| 182 | "::dbm_delete" , |
| 183 | "::dbm_error" , |
| 184 | "::dbm_fetch" , |
| 185 | "::dbm_firstkey" , |
| 186 | "::dbm_nextkey" , |
| 187 | "::dbm_open" , |
| 188 | "::dbm_store" , |
| 189 | "::dirname" , |
| 190 | "::dlerror" , |
| 191 | "::drand48" , |
| 192 | "::encrypt" , |
| 193 | "::endgrent" , |
| 194 | "::endpwent" , |
| 195 | "::endutxent" , |
| 196 | "::ftw" , |
| 197 | "::getc_unlocked" , |
| 198 | "::getchar_unlocked" , |
| 199 | "::getdate" , |
| 200 | "::getenv" , |
| 201 | "::getgrent" , |
| 202 | "::getgrgid" , |
| 203 | "::getgrnam" , |
| 204 | "::gethostent" , |
| 205 | "::getlogin" , |
| 206 | "::getnetbyaddr" , |
| 207 | "::getnetbyname" , |
| 208 | "::getnetent" , |
| 209 | "::getopt" , |
| 210 | "::getprotobyname" , |
| 211 | "::getprotobynumber" , |
| 212 | "::getprotoent" , |
| 213 | "::getpwent" , |
| 214 | "::getpwnam" , |
| 215 | "::getpwuid" , |
| 216 | "::getservbyname" , |
| 217 | "::getservbyport" , |
| 218 | "::getservent" , |
| 219 | "::getutxent" , |
| 220 | "::getutxid" , |
| 221 | "::getutxline" , |
| 222 | "::gmtime" , |
| 223 | "::hcreate" , |
| 224 | "::hdestroy" , |
| 225 | "::hsearch" , |
| 226 | "::inet_ntoa" , |
| 227 | "::l64a" , |
| 228 | "::lgamma" , |
| 229 | "::lgammaf" , |
| 230 | "::lgammal" , |
| 231 | "::localeconv" , |
| 232 | "::localtime" , |
| 233 | "::lrand48" , |
| 234 | "::mrand48" , |
| 235 | "::nftw" , |
| 236 | "::nl_langinfo" , |
| 237 | "::ptsname" , |
| 238 | "::putc_unlocked" , |
| 239 | "::putchar_unlocked" , |
| 240 | "::putenv" , |
| 241 | "::pututxline" , |
| 242 | "::rand" , |
| 243 | "::readdir" , |
| 244 | "::setenv" , |
| 245 | "::setgrent" , |
| 246 | "::setkey" , |
| 247 | "::setpwent" , |
| 248 | "::setutxent" , |
| 249 | "::strerror" , |
| 250 | "::strsignal" , |
| 251 | "::strtok" , |
| 252 | "::system" , |
| 253 | "::ttyname" , |
| 254 | "::unsetenv" , |
| 255 | "::wcstombs" , |
| 256 | "::wctomb" , |
| 257 | }; |
| 258 | |
| 259 | namespace clang::tidy { |
| 260 | |
| 261 | template <> struct OptionEnumMapping<concurrency::MtUnsafeCheck::FunctionSet> { |
| 262 | static llvm::ArrayRef< |
| 263 | std::pair<concurrency::MtUnsafeCheck::FunctionSet, StringRef>> |
| 264 | getEnumMapping() { |
| 265 | static constexpr std::pair<concurrency::MtUnsafeCheck::FunctionSet, |
| 266 | StringRef> |
| 267 | Mapping[] = {{concurrency::MtUnsafeCheck::FunctionSet::Posix, "posix" }, |
| 268 | {concurrency::MtUnsafeCheck::FunctionSet::Glibc, "glibc" }, |
| 269 | {concurrency::MtUnsafeCheck::FunctionSet::Any, "any" }}; |
| 270 | return {Mapping}; |
| 271 | } |
| 272 | }; |
| 273 | |
| 274 | namespace concurrency { |
| 275 | |
| 276 | static ast_matchers::internal::Matcher<clang::NamedDecl> |
| 277 | hasAnyMtUnsafeNames(MtUnsafeCheck::FunctionSet Libc) { |
| 278 | switch (Libc) { |
| 279 | case MtUnsafeCheck::FunctionSet::Posix: |
| 280 | return hasAnyName(PosixFunctions); |
| 281 | case MtUnsafeCheck::FunctionSet::Glibc: |
| 282 | return hasAnyName(GlibcFunctions); |
| 283 | case MtUnsafeCheck::FunctionSet::Any: |
| 284 | return anyOf(hasAnyName(PosixFunctions), hasAnyName(GlibcFunctions)); |
| 285 | } |
| 286 | llvm_unreachable("invalid FunctionSet" ); |
| 287 | } |
| 288 | |
| 289 | MtUnsafeCheck::MtUnsafeCheck(StringRef Name, ClangTidyContext *Context) |
| 290 | : ClangTidyCheck(Name, Context), |
| 291 | FuncSet(Options.get(LocalName: "FunctionSet" , Default: MtUnsafeCheck::FunctionSet::Any)) {} |
| 292 | |
| 293 | void MtUnsafeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
| 294 | Options.store(Options&: Opts, LocalName: "FunctionSet" , Value: FuncSet); |
| 295 | } |
| 296 | |
| 297 | void MtUnsafeCheck::registerMatchers(MatchFinder *Finder) { |
| 298 | Finder->addMatcher( |
| 299 | NodeMatch: callExpr(callee(InnerMatcher: functionDecl(hasAnyMtUnsafeNames(Libc: FuncSet)))) |
| 300 | .bind(ID: "mt-unsafe" ), |
| 301 | Action: this); |
| 302 | } |
| 303 | |
| 304 | void MtUnsafeCheck::check(const MatchFinder::MatchResult &Result) { |
| 305 | const auto *Call = Result.Nodes.getNodeAs<CallExpr>(ID: "mt-unsafe" ); |
| 306 | assert(Call && "Unhandled binding in the Matcher" ); |
| 307 | |
| 308 | diag(Loc: Call->getBeginLoc(), Description: "function is not thread safe" ); |
| 309 | } |
| 310 | |
| 311 | } // namespace concurrency |
| 312 | } // namespace clang::tidy |
| 313 | |