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::tidy { |
262 | |
263 | template <> struct OptionEnumMapping<concurrency::MtUnsafeCheck::FunctionSet> { |
264 | static llvm::ArrayRef< |
265 | std::pair<concurrency::MtUnsafeCheck::FunctionSet, StringRef>> |
266 | getEnumMapping() { |
267 | static constexpr std::pair<concurrency::MtUnsafeCheck::FunctionSet, |
268 | StringRef> |
269 | Mapping[] = {{concurrency::MtUnsafeCheck::FunctionSet::Posix, "posix" }, |
270 | {concurrency::MtUnsafeCheck::FunctionSet::Glibc, "glibc" }, |
271 | {concurrency::MtUnsafeCheck::FunctionSet::Any, "any" }}; |
272 | return {Mapping}; |
273 | } |
274 | }; |
275 | |
276 | namespace concurrency { |
277 | |
278 | static ast_matchers::internal::Matcher<clang::NamedDecl> |
279 | hasAnyMtUnsafeNames(MtUnsafeCheck::FunctionSet Libc) { |
280 | switch (Libc) { |
281 | case MtUnsafeCheck::FunctionSet::Posix: |
282 | return hasAnyName(PosixFunctions); |
283 | case MtUnsafeCheck::FunctionSet::Glibc: |
284 | return hasAnyName(GlibcFunctions); |
285 | case MtUnsafeCheck::FunctionSet::Any: |
286 | return anyOf(hasAnyName(PosixFunctions), hasAnyName(GlibcFunctions)); |
287 | } |
288 | llvm_unreachable("invalid FunctionSet" ); |
289 | } |
290 | |
291 | MtUnsafeCheck::MtUnsafeCheck(StringRef Name, ClangTidyContext *Context) |
292 | : ClangTidyCheck(Name, Context), |
293 | FuncSet(Options.get(LocalName: "FunctionSet" , Default: MtUnsafeCheck::FunctionSet::Any)) {} |
294 | |
295 | void MtUnsafeCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
296 | Options.store(Options&: Opts, LocalName: "FunctionSet" , Value: FuncSet); |
297 | } |
298 | |
299 | void MtUnsafeCheck::registerMatchers(MatchFinder *Finder) { |
300 | Finder->addMatcher( |
301 | NodeMatch: callExpr(callee(InnerMatcher: functionDecl(hasAnyMtUnsafeNames(Libc: FuncSet)))) |
302 | .bind(ID: "mt-unsafe" ), |
303 | Action: this); |
304 | } |
305 | |
306 | void MtUnsafeCheck::check(const MatchFinder::MatchResult &Result) { |
307 | const auto *Call = Result.Nodes.getNodeAs<CallExpr>(ID: "mt-unsafe" ); |
308 | assert(Call && "Unhandled binding in the Matcher" ); |
309 | |
310 | diag(Loc: Call->getBeginLoc(), Description: "function is not thread safe" ); |
311 | } |
312 | |
313 | } // namespace concurrency |
314 | } // namespace clang::tidy |
315 | |