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