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 | |