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