clang-tools  14.0.0git
SignalHandlerCheck.cpp
Go to the documentation of this file.
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 <>
27  bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType> {
28  static llvm::ArrayRef<std::pair<
31  static constexpr std::pair<
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 ==
85  ? MinimalConformingFunctions
86  : POSIXConformingFunctions) {}
87 
89  Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet);
90 }
91 
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
clang::tidy::bugprone::SignalHandlerCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SignalHandlerCheck.cpp:115
clang::tidy::ClangTidyOptions::OptionMap
llvm::StringMap< ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:115
SignalHandlerCheck.h
clang::tidy::OptionEnumMapping
This class should be specialized by any enum type that needs to be converted to and from an llvm::Str...
Definition: ClangTidyCheck.h:30
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:54
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::clangd::match
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
Definition: TestIndex.cpp:94
clang::tidy::bugprone::isSystemCall
static bool isSystemCall(const FunctionDecl *FD)
Definition: SignalHandlerCheck.cpp:45
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:416
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:71
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:25
clang::ast_matchers::AST_MATCHER
AST_MATCHER(Expr, isMacroID)
Definition: PreferIsaOrDynCastInConditionalsCheck.cpp:19
CE
CaptureExpr CE
Definition: AvoidBindCheck.cpp:67
clang::tidy::bugprone::SignalHandlerCheck::isLanguageVersionSupported
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override
Override this to disable registering matchers and PP callbacks if an invalid language version is bein...
Definition: SignalHandlerCheck.cpp:92
clang::tidy::bugprone::SignalHandlerCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SignalHandlerCheck.cpp:88
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::SignalHandlerCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SignalHandlerCheck.cpp:101
clang::tidy::bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType
AsyncSafeFunctionSetType
Definition: SignalHandlerCheck.h:25
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:120
clang::tidy::OptionEnumMapping< bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType >::getEnumMapping
static llvm::ArrayRef< std::pair< bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType, StringRef > > getEnumMapping()
Definition: SignalHandlerCheck.cpp:30