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