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