1 //===--- SignalHandlerCheck.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 "SignalHandlerCheck.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "llvm/ADT/DepthFirstIterator.h"
12 #include "llvm/ADT/STLExtras.h"
13
14 // This is the minimal set of safe functions.
15 // https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers
16 constexpr std::initializer_list<llvm::StringRef> MinimalConformingFunctions = {
17 "signal", "abort", "_Exit", "quick_exit"};
18
19 // The POSIX-defined set of safe functions.
20 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03
21 // 'quick_exit' is added to the set additionally because it looks like the
22 // mentioned POSIX specification was not updated after 'quick_exit' appeared
23 // in the C11 standard.
24 // Also, we want to keep the "minimal set" a subset of the "POSIX set".
25 constexpr std::initializer_list<llvm::StringRef> POSIXConformingFunctions = {
26 "_Exit",
27 "_exit",
28 "abort",
29 "accept",
30 "access",
31 "aio_error",
32 "aio_return",
33 "aio_suspend",
34 "alarm",
35 "bind",
36 "cfgetispeed",
37 "cfgetospeed",
38 "cfsetispeed",
39 "cfsetospeed",
40 "chdir",
41 "chmod",
42 "chown",
43 "clock_gettime",
44 "close",
45 "connect",
46 "creat",
47 "dup",
48 "dup2",
49 "execl",
50 "execle",
51 "execv",
52 "execve",
53 "faccessat",
54 "fchdir",
55 "fchmod",
56 "fchmodat",
57 "fchown",
58 "fchownat",
59 "fcntl",
60 "fdatasync",
61 "fexecve",
62 "ffs",
63 "fork",
64 "fstat",
65 "fstatat",
66 "fsync",
67 "ftruncate",
68 "futimens",
69 "getegid",
70 "geteuid",
71 "getgid",
72 "getgroups",
73 "getpeername",
74 "getpgrp",
75 "getpid",
76 "getppid",
77 "getsockname",
78 "getsockopt",
79 "getuid",
80 "htonl",
81 "htons",
82 "kill",
83 "link",
84 "linkat",
85 "listen",
86 "longjmp",
87 "lseek",
88 "lstat",
89 "memccpy",
90 "memchr",
91 "memcmp",
92 "memcpy",
93 "memmove",
94 "memset",
95 "mkdir",
96 "mkdirat",
97 "mkfifo",
98 "mkfifoat",
99 "mknod",
100 "mknodat",
101 "ntohl",
102 "ntohs",
103 "open",
104 "openat",
105 "pause",
106 "pipe",
107 "poll",
108 "posix_trace_event",
109 "pselect",
110 "pthread_kill",
111 "pthread_self",
112 "pthread_sigmask",
113 "quick_exit",
114 "raise",
115 "read",
116 "readlink",
117 "readlinkat",
118 "recv",
119 "recvfrom",
120 "recvmsg",
121 "rename",
122 "renameat",
123 "rmdir",
124 "select",
125 "sem_post",
126 "send",
127 "sendmsg",
128 "sendto",
129 "setgid",
130 "setpgid",
131 "setsid",
132 "setsockopt",
133 "setuid",
134 "shutdown",
135 "sigaction",
136 "sigaddset",
137 "sigdelset",
138 "sigemptyset",
139 "sigfillset",
140 "sigismember",
141 "siglongjmp",
142 "signal",
143 "sigpause",
144 "sigpending",
145 "sigprocmask",
146 "sigqueue",
147 "sigset",
148 "sigsuspend",
149 "sleep",
150 "sockatmark",
151 "socket",
152 "socketpair",
153 "stat",
154 "stpcpy",
155 "stpncpy",
156 "strcat",
157 "strchr",
158 "strcmp",
159 "strcpy",
160 "strcspn",
161 "strlen",
162 "strncat",
163 "strncmp",
164 "strncpy",
165 "strnlen",
166 "strpbrk",
167 "strrchr",
168 "strspn",
169 "strstr",
170 "strtok_r",
171 "symlink",
172 "symlinkat",
173 "tcdrain",
174 "tcflow",
175 "tcflush",
176 "tcgetattr",
177 "tcgetpgrp",
178 "tcsendbreak",
179 "tcsetattr",
180 "tcsetpgrp",
181 "time",
182 "timer_getoverrun",
183 "timer_gettime",
184 "timer_settime",
185 "times",
186 "umask",
187 "uname",
188 "unlink",
189 "unlinkat",
190 "utime",
191 "utimensat",
192 "utimes",
193 "wait",
194 "waitpid",
195 "wcpcpy",
196 "wcpncpy",
197 "wcscat",
198 "wcschr",
199 "wcscmp",
200 "wcscpy",
201 "wcscspn",
202 "wcslen",
203 "wcsncat",
204 "wcsncmp",
205 "wcsncpy",
206 "wcsnlen",
207 "wcspbrk",
208 "wcsrchr",
209 "wcsspn",
210 "wcsstr",
211 "wcstok",
212 "wmemchr",
213 "wmemcmp",
214 "wmemcpy",
215 "wmemmove",
216 "wmemset",
217 "write"};
218
219 using namespace clang::ast_matchers;
220
221 namespace clang {
222 namespace tidy {
223
224 template <>
225 struct OptionEnumMapping<
226 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind> {
227 static llvm::ArrayRef<std::pair<
228 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef>>
getEnumMappingclang::tidy::OptionEnumMapping229 getEnumMapping() {
230 static constexpr std::pair<
231 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef>
232 Mapping[] = {
233 {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind::Minimal,
234 "minimal"},
235 {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind::POSIX,
236 "POSIX"},
237 };
238 return makeArrayRef(Mapping);
239 }
240 };
241
242 namespace bugprone {
243
244 namespace {
245
246 /// Returns if a function is declared inside a system header.
247 /// These functions are considered to be "standard" (system-provided) library
248 /// functions.
isStandardFunction(const FunctionDecl * FD)249 bool isStandardFunction(const FunctionDecl *FD) {
250 // Find a possible redeclaration in system header.
251 // FIXME: Looking at the canonical declaration is not the most exact way
252 // to do this.
253
254 // Most common case will be inclusion directly from a header.
255 // This works fine by using canonical declaration.
256 // a.c
257 // #include <sysheader.h>
258
259 // Next most common case will be extern declaration.
260 // Can't catch this with either approach.
261 // b.c
262 // extern void sysfunc(void);
263
264 // Canonical declaration is the first found declaration, so this works.
265 // c.c
266 // #include <sysheader.h>
267 // extern void sysfunc(void); // redecl won't matter
268
269 // This does not work with canonical declaration.
270 // Probably this is not a frequently used case but may happen (the first
271 // declaration can be in a non-system header for example).
272 // d.c
273 // extern void sysfunc(void); // Canonical declaration, not in system header.
274 // #include <sysheader.h>
275
276 return FD->getASTContext().getSourceManager().isInSystemHeader(
277 FD->getCanonicalDecl()->getLocation());
278 }
279
280 /// Given a call graph node of a \p Caller function and a \p Callee that is
281 /// called from \p Caller, get a \c CallExpr of the corresponding function call.
282 /// It is unspecified which call is found if multiple calls exist, but the order
283 /// should be deterministic (depend only on the AST).
findCallExpr(const CallGraphNode * Caller,const CallGraphNode * Callee)284 Expr *findCallExpr(const CallGraphNode *Caller, const CallGraphNode *Callee) {
285 auto FoundCallee = llvm::find_if(
286 Caller->callees(), [Callee](const CallGraphNode::CallRecord &Call) {
287 return Call.Callee == Callee;
288 });
289 assert(FoundCallee != Caller->end() &&
290 "Callee should be called from the caller function here.");
291 return FoundCallee->CallExpr;
292 }
293
294 } // namespace
295
AST_MATCHER(FunctionDecl,isStandardFunction)296 AST_MATCHER(FunctionDecl, isStandardFunction) {
297 return isStandardFunction(&Node);
298 }
299
SignalHandlerCheck(StringRef Name,ClangTidyContext * Context)300 SignalHandlerCheck::SignalHandlerCheck(StringRef Name,
301 ClangTidyContext *Context)
302 : ClangTidyCheck(Name, Context),
303 AsyncSafeFunctionSet(
304 Options.get("AsyncSafeFunctionSet", AsyncSafeFunctionSetKind::POSIX)),
305 ConformingFunctions(AsyncSafeFunctionSet ==
306 AsyncSafeFunctionSetKind::Minimal
307 ? MinimalConformingFunctions
308 : POSIXConformingFunctions) {}
309
storeOptions(ClangTidyOptions::OptionMap & Opts)310 void SignalHandlerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
311 Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet);
312 }
313
isLanguageVersionSupported(const LangOptions & LangOpts) const314 bool SignalHandlerCheck::isLanguageVersionSupported(
315 const LangOptions &LangOpts) const {
316 // FIXME: Make the checker useful on C++ code.
317 if (LangOpts.CPlusPlus)
318 return false;
319
320 return true;
321 }
322
registerMatchers(MatchFinder * Finder)323 void SignalHandlerCheck::registerMatchers(MatchFinder *Finder) {
324 auto SignalFunction = functionDecl(hasAnyName("::signal", "::std::signal"),
325 parameterCountIs(2), isStandardFunction());
326 auto HandlerExpr =
327 declRefExpr(hasDeclaration(functionDecl().bind("handler_decl")),
328 unless(isExpandedFromMacro("SIG_IGN")),
329 unless(isExpandedFromMacro("SIG_DFL")))
330 .bind("handler_expr");
331 Finder->addMatcher(
332 callExpr(callee(SignalFunction), hasArgument(1, HandlerExpr))
333 .bind("register_call"),
334 this);
335 }
336
check(const MatchFinder::MatchResult & Result)337 void SignalHandlerCheck::check(const MatchFinder::MatchResult &Result) {
338 const auto *HandlerDecl =
339 Result.Nodes.getNodeAs<FunctionDecl>("handler_decl");
340 const auto *HandlerExpr = Result.Nodes.getNodeAs<DeclRefExpr>("handler_expr");
341 assert(Result.Nodes.getNodeAs<CallExpr>("register_call") && HandlerDecl &&
342 HandlerExpr && "All of these should exist in a match here.");
343
344 if (CG.size() <= 1) {
345 // Call graph must be populated with the entire TU at the beginning.
346 // (It is possible to add a single function but the functions called from it
347 // are not analysed in this case.)
348 CG.addToCallGraph(const_cast<TranslationUnitDecl *>(
349 HandlerDecl->getTranslationUnitDecl()));
350 assert(CG.size() > 1 &&
351 "There should be at least one function added to call graph.");
352 }
353
354 if (!HandlerDecl->hasBody()) {
355 (void)checkFunction(HandlerDecl, HandlerExpr);
356 // Here checkFunction will put the messages to HandlerExpr.
357 // No need to show a call chain.
358 // Without code body there is nothing more to check.
359 return;
360 }
361
362 CallGraphNode *HandlerNode = CG.getNode(HandlerDecl->getCanonicalDecl());
363 assert(HandlerNode &&
364 "Handler with body should be present in the call graph.");
365 // Start from signal handler and visit every function call.
366 for (auto Itr = llvm::df_begin(HandlerNode), ItrE = llvm::df_end(HandlerNode);
367 Itr != ItrE; ++Itr) {
368 if (const auto *CallF = dyn_cast<FunctionDecl>((*Itr)->getDecl())) {
369 unsigned int PathL = Itr.getPathLength();
370 const Expr *CallOrRef = (PathL == 1)
371 ? HandlerExpr
372 : findCallExpr(Itr.getPath(PathL - 2), *Itr);
373 if (checkFunction(CallF, CallOrRef))
374 reportHandlerChain(Itr, HandlerExpr);
375 }
376 }
377 }
378
checkFunction(const FunctionDecl * FD,const Expr * CallOrRef)379 bool SignalHandlerCheck::checkFunction(const FunctionDecl *FD,
380 const Expr *CallOrRef) {
381 bool FunctionIsCalled = isa<CallExpr>(CallOrRef);
382
383 if (isStandardFunction(FD)) {
384 if (!isStandardFunctionAsyncSafe(FD)) {
385 diag(CallOrRef->getBeginLoc(), "standard function %0 may not be "
386 "asynchronous-safe; "
387 "%select{using it as|calling it from}1 "
388 "a signal handler may be dangerous")
389 << FD << FunctionIsCalled;
390 return true;
391 }
392 return false;
393 }
394
395 if (!FD->hasBody()) {
396 diag(CallOrRef->getBeginLoc(), "cannot verify that external function %0 is "
397 "asynchronous-safe; "
398 "%select{using it as|calling it from}1 "
399 "a signal handler may be dangerous")
400 << FD << FunctionIsCalled;
401 return true;
402 }
403
404 return false;
405 }
406
isStandardFunctionAsyncSafe(const FunctionDecl * FD) const407 bool SignalHandlerCheck::isStandardFunctionAsyncSafe(
408 const FunctionDecl *FD) const {
409 assert(isStandardFunction(FD));
410
411 const IdentifierInfo *II = FD->getIdentifier();
412 // Unnamed functions are not explicitly allowed.
413 if (!II)
414 return false;
415
416 // FIXME: Improve for C++ (check for namespace).
417 if (ConformingFunctions.count(II->getName()))
418 return true;
419
420 return false;
421 }
422
reportHandlerChain(const llvm::df_iterator<clang::CallGraphNode * > & Itr,const DeclRefExpr * HandlerRef)423 void SignalHandlerCheck::reportHandlerChain(
424 const llvm::df_iterator<clang::CallGraphNode *> &Itr,
425 const DeclRefExpr *HandlerRef) {
426 int CallLevel = Itr.getPathLength() - 2;
427 assert(CallLevel >= -1 && "Empty iterator?");
428
429 const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee = nullptr;
430 while (CallLevel >= 0) {
431 Callee = Caller;
432 Caller = Itr.getPath(CallLevel);
433 const Expr *CE = findCallExpr(Caller, Callee);
434 diag(CE->getBeginLoc(), "function %0 called here from %1",
435 DiagnosticIDs::Note)
436 << cast<FunctionDecl>(Callee->getDecl())
437 << cast<FunctionDecl>(Caller->getDecl());
438 --CallLevel;
439 }
440
441 diag(HandlerRef->getBeginLoc(),
442 "function %0 registered here as signal handler", DiagnosticIDs::Note)
443 << cast<FunctionDecl>(Caller->getDecl()) << HandlerRef->getSourceRange();
444 }
445
446 } // namespace bugprone
447 } // namespace tidy
448 } // namespace clang
449