clang-tools  15.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/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 <>
226  bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind> {
227  static llvm::ArrayRef<std::pair<
230  static constexpr std::pair<
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.
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).
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 
296 AST_MATCHER(FunctionDecl, isStandardFunction) {
297  return isStandardFunction(&Node);
298 }
299 
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
309 
311  Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet);
312 }
313 
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 
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 
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 
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 
407 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 
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
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:337
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:29
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:53
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind
AsyncSafeFunctionSetKind
Definition: SignalHandlerCheck.h:27
POSIXConformingFunctions
constexpr std::initializer_list< llvm::StringRef > POSIXConformingFunctions
Definition: SignalHandlerCheck.cpp:25
MinimalConformingFunctions
constexpr std::initializer_list< llvm::StringRef > MinimalConformingFunctions
Definition: SignalHandlerCheck.cpp:16
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:415
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:67
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
Name
Token Name
Definition: MacroToEnumCheck.cpp:89
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:314
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:310
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:323
LangOpts
const LangOptions * LangOpts
Definition: ExtractFunction.cpp:366
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:129
clang::tidy::OptionEnumMapping< bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind >::getEnumMapping
static llvm::ArrayRef< std::pair< bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef > > getEnumMapping()
Definition: SignalHandlerCheck.cpp:229