1 //===--- SignalHandlerCheck.cpp - clang-tidy ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "SignalHandlerCheck.h"
10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "llvm/ADT/DepthFirstIterator.h"
12 #include "llvm/ADT/STLExtras.h"
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"};
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
[] = {
220 using namespace clang::ast_matchers
;
222 namespace clang::tidy
{
225 struct OptionEnumMapping
<
226 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind
> {
227 static llvm::ArrayRef
<std::pair
<
228 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind
, StringRef
>>
230 static constexpr std::pair
<
231 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind
, StringRef
>
233 {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind::Minimal
,
235 {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind::POSIX
,
246 /// Returns if a function is declared inside a system header.
247 /// These functions are considered to be "standard" (system-provided) library
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
254 // Most common case will be inclusion directly from a header.
255 // This works fine by using canonical declaration.
257 // #include <sysheader.h>
259 // Next most common case will be extern declaration.
260 // Can't catch this with either approach.
262 // extern void sysfunc(void);
264 // Canonical declaration is the first found declaration, so this works.
266 // #include <sysheader.h>
267 // extern void sysfunc(void); // redecl won't matter
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).
273 // extern void sysfunc(void); // Canonical declaration, not in system header.
274 // #include <sysheader.h>
276 return FD
->getASTContext().getSourceManager().isInSystemHeader(
277 FD
->getCanonicalDecl()->getLocation());
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.
283 bool isCXXOnlyStmt(const Stmt
*S
) {
284 StringRef Name
= S
->getStmtClassName();
285 if (Name
.startswith("CXX"))
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
);
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).
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
;
308 assert(FoundCallee
!= Caller
->end() &&
309 "Callee should be called from the caller function here.");
310 return FoundCallee
->CallExpr
;
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
);
322 return P
.getSourceRange();
327 AST_MATCHER(FunctionDecl
, isStandardFunction
) {
328 return isStandardFunction(&Node
);
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
);
340 for (StringRef v
: POSIXConformingFunctions
)
341 ConformingFunctions
.insert(v
);
345 void SignalHandlerCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
346 Options
.store(Opts
, "AsyncSafeFunctionSet", AsyncSafeFunctionSet
);
349 bool SignalHandlerCheck::isLanguageVersionSupported(
350 const LangOptions
&LangOpts
) const {
351 return !LangOpts
.CPlusPlus17
;
354 void SignalHandlerCheck::registerMatchers(MatchFinder
*Finder
) {
355 auto SignalFunction
= functionDecl(hasAnyName("::signal", "::std::signal"),
356 parameterCountIs(2), isStandardFunction());
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"),
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();
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.");
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.");
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
, {});
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();
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
417 const Expr
*CallOrRef
= (PathL
> 1)
418 ? findCallExpr(Itr
.getPath(PathL
- 2), *Itr
)
420 auto ChainReporter
= [this, &Itr
, HandlerExpr
](bool SkipPathEnd
) {
421 reportHandlerChain(Itr
, HandlerExpr
, SkipPathEnd
);
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
))
435 bool SignalHandlerCheck::checkFunction(
436 const FunctionDecl
*FD
, const Expr
*CallOrRef
,
437 std::function
<void(bool)> ChainReporter
) {
438 bool FunctionIsCalled
= isa
<CallExpr
>(CallOrRef
);
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();
448 ChainReporter(/*SkipPathEnd=*/true);
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();
461 ChainReporter(/*SkipPathEnd=*/true);
465 if (getLangOpts().CPlusPlus
)
466 return checkFunctionCPP14(FD
, CallOrRef
, 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)");
479 ChainReporter(/*SkipPathEnd=*/true);
483 const FunctionDecl
*FBody
= nullptr;
484 const Stmt
*BodyS
= FD
->getBody(FBody
);
488 bool StmtProblemsFound
= false;
489 ASTContext
&Ctx
= FBody
->getASTContext();
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
);
499 "C++-only construct is not allowed in signal handler (until C++17)")
501 diag(R
.getBegin(), "internally, the statement is parsed as a '%0'",
502 DiagnosticIDs::Remark
)
503 << FoundS
->getStmtClassName();
505 ChainReporter(/*SkipPathEnd=*/false);
506 StmtProblemsFound
= true;
510 return StmtProblemsFound
;
513 bool SignalHandlerCheck::isStandardFunctionAsyncSafe(
514 const FunctionDecl
*FD
) const {
515 assert(isStandardFunction(FD
));
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++".
524 if (!FD
->isInStdNamespace() && !FD
->isGlobal())
527 if (ConformingFunctions
.count(II
->getName()))
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?");
539 const CallGraphNode
*Caller
= Itr
.getPath(CallLevel
+ 1), *Callee
= nullptr;
540 while (CallLevel
>= 0) {
542 Caller
= Itr
.getPath(CallLevel
);
543 const Expr
*CE
= findCallExpr(Caller
, Callee
);
547 diag(CE
->getBeginLoc(), "function %0 called here from %1",
549 << cast
<FunctionDecl
>(Callee
->getDecl())
550 << cast
<FunctionDecl
>(Caller
->getDecl());
555 diag(HandlerRef
->getBeginLoc(),
556 "function %0 registered here as signal handler", DiagnosticIDs::Note
)
557 << cast
<FunctionDecl
>(Caller
->getDecl())
558 << HandlerRef
->getSourceRange();
561 } // namespace bugprone
562 } // namespace clang::tidy