10#include "clang/ASTMatchers/ASTMatchFinder.h"
11#include "llvm/ADT/DepthFirstIterator.h"
12#include "llvm/ADT/STLExtras.h"
230 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind> {
231 static llvm::ArrayRef<std::pair<
234 static constexpr std::pair<
278 return FD->getASTContext().getSourceManager().isInSystemHeader(
279 FD->getCanonicalDecl()->getLocation());
286 const StringRef Name = S->getStmtClassName();
287 if (Name.starts_with(
"CXX"))
290 return isa<ArrayTypeTraitExpr, BuiltinBitCastExpr, CUDAKernelCallExpr,
291 CoawaitExpr, CoreturnStmt, CoroutineBodyStmt, CoroutineSuspendExpr,
292 CoyieldExpr, DependentCoawaitExpr, DependentScopeDeclRefExpr,
293 ExprWithCleanups, ExpressionTraitExpr, FunctionParmPackExpr,
294 LambdaExpr, MSDependentExistsStmt, MSPropertyRefExpr,
295 MSPropertySubscriptExpr, MaterializeTemporaryExpr, OverloadExpr,
296 PackExpansionExpr, SizeOfPackExpr, SubstNonTypeTemplateParmExpr,
297 SubstNonTypeTemplateParmPackExpr, TypeTraitExpr,
298 UserDefinedLiteral>(S);
306 const CallGraphNode *Callee) {
307 const auto *FoundCallee = llvm::find_if(
308 Caller->callees(), [Callee](
const CallGraphNode::CallRecord &Call) {
309 return Call.Callee == Callee;
311 assert(FoundCallee != Caller->end() &&
312 "Callee should be called from the caller function here.");
313 return FoundCallee->CallExpr;
317 ParentMapContext &PM = Ctx.getParentMapContext();
318 DynTypedNode
P = DynTypedNode::create(*S);
319 while (
P.getSourceRange().isInvalid()) {
320 const DynTypedNodeList PL = PM.getParents(
P);
325 return P.getSourceRange();
337 AsyncSafeFunctionSet(Options.get(
"AsyncSafeFunctionSet",
346 Options.store(Opts,
"AsyncSafeFunctionSet", AsyncSafeFunctionSet);
350 const LangOptions &LangOpts)
const {
351 return !LangOpts.CPlusPlus17;
355 const auto SignalFunction =
356 functionDecl(hasAnyName(
"::signal",
"::std::signal"), parameterCountIs(2),
359 declRefExpr(hasDeclaration(functionDecl().bind(
"handler_decl")),
360 unless(isExpandedFromMacro(
"SIG_IGN")),
361 unless(isExpandedFromMacro(
"SIG_DFL")))
362 .bind(
"handler_expr");
363 auto HandlerLambda = cxxMemberCallExpr(
364 on(expr(ignoringParenImpCasts(lambdaExpr().bind(
"handler_lambda")))));
365 Finder->addMatcher(callExpr(callee(SignalFunction),
366 hasArgument(1, anyOf(HandlerExpr, HandlerLambda)))
367 .bind(
"register_call"),
372 if (
const auto *HandlerLambda =
373 Result.Nodes.getNodeAs<LambdaExpr>(
"handler_lambda")) {
374 diag(HandlerLambda->getBeginLoc(),
375 "lambda function is not allowed as signal handler (until C++17)")
376 << HandlerLambda->getSourceRange();
380 const auto *HandlerDecl =
381 Result.Nodes.getNodeAs<FunctionDecl>(
"handler_decl");
382 const auto *HandlerExpr = Result.Nodes.getNodeAs<DeclRefExpr>(
"handler_expr");
383 assert(Result.Nodes.getNodeAs<CallExpr>(
"register_call") && HandlerDecl &&
384 HandlerExpr &&
"All of these should exist in a match here.");
386 if (CG.size() <= 1) {
390 CG.addToCallGraph(
const_cast<TranslationUnitDecl *
>(
391 HandlerDecl->getTranslationUnitDecl()));
392 assert(CG.size() > 1 &&
393 "There should be at least one function added to call graph.");
396 if (!HandlerDecl->hasBody()) {
400 (void)checkFunction(HandlerDecl, HandlerExpr, {});
405 const CallGraphNode *HandlerNode =
406 CG.getNode(HandlerDecl->getCanonicalDecl());
407 assert(HandlerNode &&
408 "Handler with body should be present in the call graph.");
410 auto Itr = llvm::df_begin(HandlerNode), ItrE = llvm::df_end(HandlerNode);
411 while (Itr != ItrE) {
412 const auto *CallF = dyn_cast<FunctionDecl>((*Itr)->getDecl());
413 const unsigned int PathL = Itr.getPathLength();
419 const Expr *CallOrRef = (PathL > 1)
422 auto ChainReporter = [
this, &Itr, HandlerExpr](
bool SkipPathEnd) {
423 reportHandlerChain(Itr, HandlerExpr, SkipPathEnd);
427 if (checkFunction(CallF, CallOrRef, ChainReporter))
437bool SignalHandlerCheck::checkFunction(
438 const FunctionDecl *FD,
const Expr *CallOrRef,
439 llvm::function_ref<
void(
bool)> ChainReporter) {
440 const bool FunctionIsCalled = isa<CallExpr>(CallOrRef);
443 if (!isStandardFunctionAsyncSafe(FD)) {
444 diag(CallOrRef->getBeginLoc(),
"standard function %0 may not be "
445 "asynchronous-safe; "
446 "%select{using it as|calling it from}1 "
447 "a signal handler may be dangerous")
448 << FD << FunctionIsCalled << CallOrRef->getSourceRange();
456 if (!FD->hasBody()) {
457 diag(CallOrRef->getBeginLoc(),
"cannot verify that external function %0 is "
458 "asynchronous-safe; "
459 "%select{using it as|calling it from}1 "
460 "a signal handler may be dangerous")
461 << FD << FunctionIsCalled << CallOrRef->getSourceRange();
467 if (getLangOpts().CPlusPlus)
468 return checkFunctionCPP14(FD, CallOrRef, ChainReporter);
473bool SignalHandlerCheck::checkFunctionCPP14(
474 const FunctionDecl *FD,
const Expr *CallOrRef,
475 llvm::function_ref<
void(
bool)> ChainReporter) {
476 if (!FD->isExternC()) {
477 diag(CallOrRef->getBeginLoc(),
478 "functions without C linkage are not allowed as signal "
479 "handler (until C++17)");
485 const FunctionDecl *FBody =
nullptr;
486 const Stmt *BodyS = FD->getBody(FBody);
490 bool StmtProblemsFound =
false;
491 ASTContext &Ctx = FBody->getASTContext();
493 match(decl(forEachDescendant(stmt().bind(
"stmt"))), *FBody, Ctx);
494 for (
const auto &Match : Matches) {
495 const auto *FoundS = Match.getNodeAs<Stmt>(
"stmt");
501 "C++-only construct is not allowed in signal handler (until C++17)")
503 diag(R.getBegin(),
"internally, the statement is parsed as a '%0'",
504 DiagnosticIDs::Remark)
505 << FoundS->getStmtClassName();
507 ChainReporter(
false);
508 StmtProblemsFound =
true;
512 return StmtProblemsFound;
515bool SignalHandlerCheck::isStandardFunctionAsyncSafe(
516 const FunctionDecl *FD)
const {
519 const IdentifierInfo *II = FD->getIdentifier();
526 if (!FD->isInStdNamespace() && !FD->isGlobal())
529 if (ConformingFunctions.contains(II->getName()))
535void SignalHandlerCheck::reportHandlerChain(
536 const llvm::df_iterator<const CallGraphNode *> &Itr,
537 const DeclRefExpr *HandlerRef,
bool SkipPathEnd) {
538 int CallLevel = Itr.getPathLength() - 2;
539 assert(CallLevel >= -1 &&
"Empty iterator?");
541 const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee =
nullptr;
542 while (CallLevel >= 0) {
544 Caller = Itr.getPath(CallLevel);
549 diag(CE->getBeginLoc(),
"function %0 called here from %1",
551 << cast<FunctionDecl>(Callee->getDecl())
552 << cast<FunctionDecl>(Caller->getDecl());
557 diag(HandlerRef->getBeginLoc(),
558 "function %0 registered here as signal handler", DiagnosticIDs::Note)
559 << cast<FunctionDecl>(Caller->getDecl())
560 << HandlerRef->getSourceRange();
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
SignalHandlerCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
std::vector< std::string > match(const SymbolIndex &I, const FuzzyFindRequest &Req, bool *Incomplete)
static Expr * findCallExpr(const CallGraphNode *Caller, const CallGraphNode *Callee)
Given a call graph node of a Caller function and a Callee that is called from Caller,...
static SourceRange getSourceRangeOfStmt(const Stmt *S, ASTContext &Ctx)
static bool isCXXOnlyStmt(const Stmt *S)
Check if a statement is "C++-only".
static bool isStandardFunction(const FunctionDecl *FD)
Returns if a function is declared inside a system header.
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
constexpr StringRef MinimalConformingFunctions[]
constexpr StringRef POSIXConformingFunctions[]
llvm::StringMap< ClangTidyValue > OptionMap
static llvm::ArrayRef< std::pair< bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef > > getEnumMapping()
This class should be specialized by any enum type that needs to be converted to and from an llvm::Str...