xref: /llvm-project/clang-tools-extra/clang-tidy/bugprone/SignalHandlerCheck.cpp (revision 11a411a49b62c129bba551df4587dd446fcdc660)
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 llvm::StringLiteral 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 // The list is repeated in bugprone-signal-handler.rst and should be kept up to date.
26 constexpr llvm::StringLiteral POSIXConformingFunctions[] = {
27     "_Exit",
28     "_exit",
29     "abort",
30     "accept",
31     "access",
32     "aio_error",
33     "aio_return",
34     "aio_suspend",
35     "alarm",
36     "bind",
37     "cfgetispeed",
38     "cfgetospeed",
39     "cfsetispeed",
40     "cfsetospeed",
41     "chdir",
42     "chmod",
43     "chown",
44     "clock_gettime",
45     "close",
46     "connect",
47     "creat",
48     "dup",
49     "dup2",
50     "execl",
51     "execle",
52     "execv",
53     "execve",
54     "faccessat",
55     "fchdir",
56     "fchmod",
57     "fchmodat",
58     "fchown",
59     "fchownat",
60     "fcntl",
61     "fdatasync",
62     "fexecve",
63     "ffs",
64     "fork",
65     "fstat",
66     "fstatat",
67     "fsync",
68     "ftruncate",
69     "futimens",
70     "getegid",
71     "geteuid",
72     "getgid",
73     "getgroups",
74     "getpeername",
75     "getpgrp",
76     "getpid",
77     "getppid",
78     "getsockname",
79     "getsockopt",
80     "getuid",
81     "htonl",
82     "htons",
83     "kill",
84     "link",
85     "linkat",
86     "listen",
87     "longjmp",
88     "lseek",
89     "lstat",
90     "memccpy",
91     "memchr",
92     "memcmp",
93     "memcpy",
94     "memmove",
95     "memset",
96     "mkdir",
97     "mkdirat",
98     "mkfifo",
99     "mkfifoat",
100     "mknod",
101     "mknodat",
102     "ntohl",
103     "ntohs",
104     "open",
105     "openat",
106     "pause",
107     "pipe",
108     "poll",
109     "posix_trace_event",
110     "pselect",
111     "pthread_kill",
112     "pthread_self",
113     "pthread_sigmask",
114     "quick_exit",
115     "raise",
116     "read",
117     "readlink",
118     "readlinkat",
119     "recv",
120     "recvfrom",
121     "recvmsg",
122     "rename",
123     "renameat",
124     "rmdir",
125     "select",
126     "sem_post",
127     "send",
128     "sendmsg",
129     "sendto",
130     "setgid",
131     "setpgid",
132     "setsid",
133     "setsockopt",
134     "setuid",
135     "shutdown",
136     "sigaction",
137     "sigaddset",
138     "sigdelset",
139     "sigemptyset",
140     "sigfillset",
141     "sigismember",
142     "siglongjmp",
143     "signal",
144     "sigpause",
145     "sigpending",
146     "sigprocmask",
147     "sigqueue",
148     "sigset",
149     "sigsuspend",
150     "sleep",
151     "sockatmark",
152     "socket",
153     "socketpair",
154     "stat",
155     "stpcpy",
156     "stpncpy",
157     "strcat",
158     "strchr",
159     "strcmp",
160     "strcpy",
161     "strcspn",
162     "strlen",
163     "strncat",
164     "strncmp",
165     "strncpy",
166     "strnlen",
167     "strpbrk",
168     "strrchr",
169     "strspn",
170     "strstr",
171     "strtok_r",
172     "symlink",
173     "symlinkat",
174     "tcdrain",
175     "tcflow",
176     "tcflush",
177     "tcgetattr",
178     "tcgetpgrp",
179     "tcsendbreak",
180     "tcsetattr",
181     "tcsetpgrp",
182     "time",
183     "timer_getoverrun",
184     "timer_gettime",
185     "timer_settime",
186     "times",
187     "umask",
188     "uname",
189     "unlink",
190     "unlinkat",
191     "utime",
192     "utimensat",
193     "utimes",
194     "wait",
195     "waitpid",
196     "wcpcpy",
197     "wcpncpy",
198     "wcscat",
199     "wcschr",
200     "wcscmp",
201     "wcscpy",
202     "wcscspn",
203     "wcslen",
204     "wcsncat",
205     "wcsncmp",
206     "wcsncpy",
207     "wcsnlen",
208     "wcspbrk",
209     "wcsrchr",
210     "wcsspn",
211     "wcsstr",
212     "wcstok",
213     "wmemchr",
214     "wmemcmp",
215     "wmemcpy",
216     "wmemmove",
217     "wmemset",
218     "write"};
219 
220 using namespace clang::ast_matchers;
221 
222 namespace clang::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 {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 /// Check if a statement is "C++-only".
281 /// This includes all statements that have a class name with "CXX" prefix
282 /// and every other statement that is declared in file ExprCXX.h.
isCXXOnlyStmt(const Stmt * S)283 bool isCXXOnlyStmt(const Stmt *S) {
284   StringRef Name = S->getStmtClassName();
285   if (Name.starts_with("CXX"))
286     return true;
287   // Check for all other class names in ExprCXX.h that have no 'CXX' prefix.
288   return isa<ArrayTypeTraitExpr, BuiltinBitCastExpr, CUDAKernelCallExpr,
289              CoawaitExpr, CoreturnStmt, CoroutineBodyStmt, CoroutineSuspendExpr,
290              CoyieldExpr, DependentCoawaitExpr, DependentScopeDeclRefExpr,
291              ExprWithCleanups, ExpressionTraitExpr, FunctionParmPackExpr,
292              LambdaExpr, MSDependentExistsStmt, MSPropertyRefExpr,
293              MSPropertySubscriptExpr, MaterializeTemporaryExpr, OverloadExpr,
294              PackExpansionExpr, SizeOfPackExpr, SubstNonTypeTemplateParmExpr,
295              SubstNonTypeTemplateParmPackExpr, TypeTraitExpr,
296              UserDefinedLiteral>(S);
297 }
298 
299 /// Given a call graph node of a \p Caller function and a \p Callee that is
300 /// called from \p Caller, get a \c CallExpr of the corresponding function call.
301 /// It is unspecified which call is found if multiple calls exist, but the order
302 /// should be deterministic (depend only on the AST).
findCallExpr(const CallGraphNode * Caller,const CallGraphNode * Callee)303 Expr *findCallExpr(const CallGraphNode *Caller, const CallGraphNode *Callee) {
304   auto FoundCallee = llvm::find_if(
305       Caller->callees(), [Callee](const CallGraphNode::CallRecord &Call) {
306         return Call.Callee == Callee;
307       });
308   assert(FoundCallee != Caller->end() &&
309          "Callee should be called from the caller function here.");
310   return FoundCallee->CallExpr;
311 }
312 
getSourceRangeOfStmt(const Stmt * S,ASTContext & Ctx)313 SourceRange getSourceRangeOfStmt(const Stmt *S, ASTContext &Ctx) {
314   ParentMapContext &PM = Ctx.getParentMapContext();
315   DynTypedNode P = DynTypedNode::create(*S);
316   while (P.getSourceRange().isInvalid()) {
317     DynTypedNodeList PL = PM.getParents(P);
318     if (PL.size() != 1)
319       return {};
320     P = PL[0];
321   }
322   return P.getSourceRange();
323 }
324 
325 } // namespace
326 
AST_MATCHER(FunctionDecl,isStandardFunction)327 AST_MATCHER(FunctionDecl, isStandardFunction) {
328   return isStandardFunction(&Node);
329 }
330 
SignalHandlerCheck(StringRef Name,ClangTidyContext * Context)331 SignalHandlerCheck::SignalHandlerCheck(StringRef Name,
332                                        ClangTidyContext *Context)
333     : ClangTidyCheck(Name, Context),
334       AsyncSafeFunctionSet(Options.get("AsyncSafeFunctionSet",
335                                        AsyncSafeFunctionSetKind::POSIX)) {
336   if (AsyncSafeFunctionSet == AsyncSafeFunctionSetKind::Minimal) {
337     for (StringRef v : MinimalConformingFunctions)
338       ConformingFunctions.insert(v);
339   } else {
340     for (StringRef v : POSIXConformingFunctions)
341       ConformingFunctions.insert(v);
342   }
343 }
344 
storeOptions(ClangTidyOptions::OptionMap & Opts)345 void SignalHandlerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
346   Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet);
347 }
348 
isLanguageVersionSupported(const LangOptions & LangOpts) const349 bool SignalHandlerCheck::isLanguageVersionSupported(
350     const LangOptions &LangOpts) const {
351   return !LangOpts.CPlusPlus17;
352 }
353 
registerMatchers(MatchFinder * Finder)354 void SignalHandlerCheck::registerMatchers(MatchFinder *Finder) {
355   auto SignalFunction = functionDecl(hasAnyName("::signal", "::std::signal"),
356                                      parameterCountIs(2), isStandardFunction());
357   auto HandlerExpr =
358       declRefExpr(hasDeclaration(functionDecl().bind("handler_decl")),
359                   unless(isExpandedFromMacro("SIG_IGN")),
360                   unless(isExpandedFromMacro("SIG_DFL")))
361           .bind("handler_expr");
362   auto HandlerLambda = cxxMemberCallExpr(
363       on(expr(ignoringParenImpCasts(lambdaExpr().bind("handler_lambda")))));
364   Finder->addMatcher(callExpr(callee(SignalFunction),
365                               hasArgument(1, anyOf(HandlerExpr, HandlerLambda)))
366                          .bind("register_call"),
367                      this);
368 }
369 
check(const MatchFinder::MatchResult & Result)370 void SignalHandlerCheck::check(const MatchFinder::MatchResult &Result) {
371   if (const auto *HandlerLambda =
372           Result.Nodes.getNodeAs<LambdaExpr>("handler_lambda")) {
373     diag(HandlerLambda->getBeginLoc(),
374          "lambda function is not allowed as signal handler (until C++17)")
375         << HandlerLambda->getSourceRange();
376     return;
377   }
378 
379   const auto *HandlerDecl =
380       Result.Nodes.getNodeAs<FunctionDecl>("handler_decl");
381   const auto *HandlerExpr = Result.Nodes.getNodeAs<DeclRefExpr>("handler_expr");
382   assert(Result.Nodes.getNodeAs<CallExpr>("register_call") && HandlerDecl &&
383          HandlerExpr && "All of these should exist in a match here.");
384 
385   if (CG.size() <= 1) {
386     // Call graph must be populated with the entire TU at the beginning.
387     // (It is possible to add a single function but the functions called from it
388     // are not analysed in this case.)
389     CG.addToCallGraph(const_cast<TranslationUnitDecl *>(
390         HandlerDecl->getTranslationUnitDecl()));
391     assert(CG.size() > 1 &&
392            "There should be at least one function added to call graph.");
393   }
394 
395   if (!HandlerDecl->hasBody()) {
396     // Check the handler function.
397     // The warning is placed to the signal handler registration.
398     // No need to display a call chain and no need for more checks.
399     (void)checkFunction(HandlerDecl, HandlerExpr, {});
400     return;
401   }
402 
403   // FIXME: Update CallGraph::getNode to use canonical decl?
404   CallGraphNode *HandlerNode = CG.getNode(HandlerDecl->getCanonicalDecl());
405   assert(HandlerNode &&
406          "Handler with body should be present in the call graph.");
407   // Start from signal handler and visit every function call.
408   auto Itr = llvm::df_begin(HandlerNode), ItrE = llvm::df_end(HandlerNode);
409   while (Itr != ItrE) {
410     const auto *CallF = dyn_cast<FunctionDecl>((*Itr)->getDecl());
411     unsigned int PathL = Itr.getPathLength();
412     if (CallF) {
413       // A signal handler or a function transitively reachable from the signal
414       // handler was found to be unsafe.
415       // Generate notes for the whole call chain (including the signal handler
416       // registration).
417       const Expr *CallOrRef = (PathL > 1)
418                                   ? findCallExpr(Itr.getPath(PathL - 2), *Itr)
419                                   : HandlerExpr;
420       auto ChainReporter = [this, &Itr, HandlerExpr](bool SkipPathEnd) {
421         reportHandlerChain(Itr, HandlerExpr, SkipPathEnd);
422       };
423       // If problems were found in a function (`CallF`), skip the analysis of
424       // functions that are called from it.
425       if (checkFunction(CallF, CallOrRef, ChainReporter))
426         Itr.skipChildren();
427       else
428         ++Itr;
429     } else {
430       ++Itr;
431     }
432   }
433 }
434 
checkFunction(const FunctionDecl * FD,const Expr * CallOrRef,std::function<void (bool)> ChainReporter)435 bool SignalHandlerCheck::checkFunction(
436     const FunctionDecl *FD, const Expr *CallOrRef,
437     std::function<void(bool)> ChainReporter) {
438   bool FunctionIsCalled = isa<CallExpr>(CallOrRef);
439 
440   if (isStandardFunction(FD)) {
441     if (!isStandardFunctionAsyncSafe(FD)) {
442       diag(CallOrRef->getBeginLoc(), "standard function %0 may not be "
443                                      "asynchronous-safe; "
444                                      "%select{using it as|calling it from}1 "
445                                      "a signal handler may be dangerous")
446           << FD << FunctionIsCalled << CallOrRef->getSourceRange();
447       if (ChainReporter)
448         ChainReporter(/*SkipPathEnd=*/true);
449       return true;
450     }
451     return false;
452   }
453 
454   if (!FD->hasBody()) {
455     diag(CallOrRef->getBeginLoc(), "cannot verify that external function %0 is "
456                                    "asynchronous-safe; "
457                                    "%select{using it as|calling it from}1 "
458                                    "a signal handler may be dangerous")
459         << FD << FunctionIsCalled << CallOrRef->getSourceRange();
460     if (ChainReporter)
461       ChainReporter(/*SkipPathEnd=*/true);
462     return true;
463   }
464 
465   if (getLangOpts().CPlusPlus)
466     return checkFunctionCPP14(FD, CallOrRef, ChainReporter);
467 
468   return false;
469 }
470 
checkFunctionCPP14(const FunctionDecl * FD,const Expr * CallOrRef,std::function<void (bool)> ChainReporter)471 bool SignalHandlerCheck::checkFunctionCPP14(
472     const FunctionDecl *FD, const Expr *CallOrRef,
473     std::function<void(bool)> ChainReporter) {
474   if (!FD->isExternC()) {
475     diag(CallOrRef->getBeginLoc(),
476          "functions without C linkage are not allowed as signal "
477          "handler (until C++17)");
478     if (ChainReporter)
479       ChainReporter(/*SkipPathEnd=*/true);
480     return true;
481   }
482 
483   const FunctionDecl *FBody = nullptr;
484   const Stmt *BodyS = FD->getBody(FBody);
485   if (!BodyS)
486     return false;
487 
488   bool StmtProblemsFound = false;
489   ASTContext &Ctx = FBody->getASTContext();
490   auto Matches =
491       match(decl(forEachDescendant(stmt().bind("stmt"))), *FBody, Ctx);
492   for (const auto &Match : Matches) {
493     const auto *FoundS = Match.getNodeAs<Stmt>("stmt");
494     if (isCXXOnlyStmt(FoundS)) {
495       SourceRange R = getSourceRangeOfStmt(FoundS, Ctx);
496       if (R.isInvalid())
497         continue;
498       diag(R.getBegin(),
499            "C++-only construct is not allowed in signal handler (until C++17)")
500           << R;
501       diag(R.getBegin(), "internally, the statement is parsed as a '%0'",
502            DiagnosticIDs::Remark)
503           << FoundS->getStmtClassName();
504       if (ChainReporter)
505         ChainReporter(/*SkipPathEnd=*/false);
506       StmtProblemsFound = true;
507     }
508   }
509 
510   return StmtProblemsFound;
511 }
512 
isStandardFunctionAsyncSafe(const FunctionDecl * FD) const513 bool SignalHandlerCheck::isStandardFunctionAsyncSafe(
514     const FunctionDecl *FD) const {
515   assert(isStandardFunction(FD));
516 
517   const IdentifierInfo *II = FD->getIdentifier();
518   // Unnamed functions are not explicitly allowed.
519   // C++ std operators may be unsafe and not within the
520   // "common subset of C and C++".
521   if (!II)
522     return false;
523 
524   if (!FD->isInStdNamespace() && !FD->isGlobal())
525     return false;
526 
527   if (ConformingFunctions.count(II->getName()))
528     return true;
529 
530   return false;
531 }
532 
reportHandlerChain(const llvm::df_iterator<clang::CallGraphNode * > & Itr,const DeclRefExpr * HandlerRef,bool SkipPathEnd)533 void SignalHandlerCheck::reportHandlerChain(
534     const llvm::df_iterator<clang::CallGraphNode *> &Itr,
535     const DeclRefExpr *HandlerRef, bool SkipPathEnd) {
536   int CallLevel = Itr.getPathLength() - 2;
537   assert(CallLevel >= -1 && "Empty iterator?");
538 
539   const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee = nullptr;
540   while (CallLevel >= 0) {
541     Callee = Caller;
542     Caller = Itr.getPath(CallLevel);
543     const Expr *CE = findCallExpr(Caller, Callee);
544     if (SkipPathEnd)
545       SkipPathEnd = false;
546     else
547       diag(CE->getBeginLoc(), "function %0 called here from %1",
548            DiagnosticIDs::Note)
549           << cast<FunctionDecl>(Callee->getDecl())
550           << cast<FunctionDecl>(Caller->getDecl());
551     --CallLevel;
552   }
553 
554   if (!SkipPathEnd)
555     diag(HandlerRef->getBeginLoc(),
556          "function %0 registered here as signal handler", DiagnosticIDs::Note)
557         << cast<FunctionDecl>(Caller->getDecl())
558         << HandlerRef->getSourceRange();
559 }
560 
561 } // namespace bugprone
562 } // namespace clang::tidy
563