10#include "clang/ASTMatchers/ASTMatchFinder.h"
11#include "llvm/ADT/DepthFirstIterator.h"
12#include "llvm/ADT/STLExtras.h"
17 "signal",
"abort",
"_Exit",
"quick_exit"};
230 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind> {
231 static llvm::ArrayRef<std::pair<
234 static constexpr std::pair<
253bool isStandardFunction(
const FunctionDecl *FD) {
280 return FD->getASTContext().getSourceManager().isInSystemHeader(
281 FD->getCanonicalDecl()->getLocation());
287bool isCXXOnlyStmt(
const Stmt *S) {
288 StringRef Name = S->getStmtClassName();
289 if (Name.starts_with(
"CXX"))
292 return isa<ArrayTypeTraitExpr, BuiltinBitCastExpr, CUDAKernelCallExpr,
293 CoawaitExpr, CoreturnStmt, CoroutineBodyStmt, CoroutineSuspendExpr,
294 CoyieldExpr, DependentCoawaitExpr, DependentScopeDeclRefExpr,
295 ExprWithCleanups, ExpressionTraitExpr, FunctionParmPackExpr,
296 LambdaExpr, MSDependentExistsStmt, MSPropertyRefExpr,
297 MSPropertySubscriptExpr, MaterializeTemporaryExpr, OverloadExpr,
298 PackExpansionExpr, SizeOfPackExpr, SubstNonTypeTemplateParmExpr,
299 SubstNonTypeTemplateParmPackExpr, TypeTraitExpr,
300 UserDefinedLiteral>(S);
307Expr *findCallExpr(
const CallGraphNode *Caller,
const CallGraphNode *Callee) {
308 const auto *FoundCallee = llvm::find_if(
309 Caller->callees(), [Callee](
const CallGraphNode::CallRecord &Call) {
310 return Call.Callee == Callee;
312 assert(FoundCallee != Caller->end() &&
313 "Callee should be called from the caller function here.");
314 return FoundCallee->CallExpr;
317SourceRange getSourceRangeOfStmt(
const Stmt *S, ASTContext &Ctx) {
318 ParentMapContext &PM = Ctx.getParentMapContext();
319 DynTypedNode
P = DynTypedNode::create(*S);
320 while (
P.getSourceRange().isInvalid()) {
321 DynTypedNodeList PL = PM.getParents(
P);
326 return P.getSourceRange();
330 return isStandardFunction(&Node);
338 AsyncSafeFunctionSet(Options.get(
"AsyncSafeFunctionSet",
347 Options.store(Opts,
"AsyncSafeFunctionSet", AsyncSafeFunctionSet);
351 const LangOptions &LangOpts)
const {
352 return !LangOpts.CPlusPlus17;
356 auto SignalFunction = functionDecl(hasAnyName(
"::signal",
"::std::signal"),
357 parameterCountIs(2), isStandardFunction());
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 CallGraphNode *HandlerNode = CG.getNode(HandlerDecl->getCanonicalDecl());
406 assert(HandlerNode &&
407 "Handler with body should be present in the call graph.");
409 auto Itr = llvm::df_begin(HandlerNode), ItrE = llvm::df_end(HandlerNode);
410 while (Itr != ItrE) {
411 const auto *CallF = dyn_cast<FunctionDecl>((*Itr)->getDecl());
412 unsigned int PathL = Itr.getPathLength();
418 const Expr *CallOrRef = (PathL > 1)
419 ? findCallExpr(Itr.getPath(PathL - 2), *Itr)
421 auto ChainReporter = [
this, &Itr, HandlerExpr](
bool SkipPathEnd) {
422 reportHandlerChain(Itr, HandlerExpr, SkipPathEnd);
426 if (checkFunction(CallF, CallOrRef, ChainReporter))
436bool SignalHandlerCheck::checkFunction(
437 const FunctionDecl *FD,
const Expr *CallOrRef,
438 std::function<
void(
bool)> ChainReporter) {
439 bool FunctionIsCalled = isa<CallExpr>(CallOrRef);
441 if (isStandardFunction(FD)) {
442 if (!isStandardFunctionAsyncSafe(FD)) {
443 diag(CallOrRef->getBeginLoc(),
"standard function %0 may not be "
444 "asynchronous-safe; "
445 "%select{using it as|calling it from}1 "
446 "a signal handler may be dangerous")
447 << FD << FunctionIsCalled << CallOrRef->getSourceRange();
455 if (!FD->hasBody()) {
456 diag(CallOrRef->getBeginLoc(),
"cannot verify that external function %0 is "
457 "asynchronous-safe; "
458 "%select{using it as|calling it from}1 "
459 "a signal handler may be dangerous")
460 << FD << FunctionIsCalled << CallOrRef->getSourceRange();
466 if (getLangOpts().CPlusPlus)
467 return checkFunctionCPP14(FD, CallOrRef, ChainReporter);
472bool SignalHandlerCheck::checkFunctionCPP14(
473 const FunctionDecl *FD,
const Expr *CallOrRef,
474 std::function<
void(
bool)> ChainReporter) {
475 if (!FD->isExternC()) {
476 diag(CallOrRef->getBeginLoc(),
477 "functions without C linkage are not allowed as signal "
478 "handler (until C++17)");
484 const FunctionDecl *FBody =
nullptr;
485 const Stmt *BodyS = FD->getBody(FBody);
489 bool StmtProblemsFound =
false;
490 ASTContext &Ctx = FBody->getASTContext();
492 match(decl(forEachDescendant(stmt().bind(
"stmt"))), *FBody, Ctx);
493 for (
const auto &Match : Matches) {
494 const auto *FoundS = Match.getNodeAs<Stmt>(
"stmt");
495 if (isCXXOnlyStmt(FoundS)) {
496 SourceRange R = getSourceRangeOfStmt(FoundS, Ctx);
500 "C++-only construct is not allowed in signal handler (until C++17)")
502 diag(R.getBegin(),
"internally, the statement is parsed as a '%0'",
503 DiagnosticIDs::Remark)
504 << FoundS->getStmtClassName();
506 ChainReporter(
false);
507 StmtProblemsFound =
true;
511 return StmtProblemsFound;
514bool SignalHandlerCheck::isStandardFunctionAsyncSafe(
515 const FunctionDecl *FD)
const {
516 assert(isStandardFunction(FD));
518 const IdentifierInfo *II = FD->getIdentifier();
525 if (!FD->isInStdNamespace() && !FD->isGlobal())
528 if (ConformingFunctions.contains(II->getName()))
534void SignalHandlerCheck::reportHandlerChain(
535 const llvm::df_iterator<clang::CallGraphNode *> &Itr,
536 const DeclRefExpr *HandlerRef,
bool SkipPathEnd) {
537 int CallLevel = Itr.getPathLength() - 2;
538 assert(CallLevel >= -1 &&
"Empty iterator?");
540 const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee =
nullptr;
541 while (CallLevel >= 0) {
543 Caller = Itr.getPath(CallLevel);
544 const Expr *CE = findCallExpr(Caller, Callee);
548 diag(CE->getBeginLoc(),
"function %0 called here from %1",
550 << cast<FunctionDecl>(Callee->getDecl())
551 << cast<FunctionDecl>(Caller->getDecl());
556 diag(HandlerRef->getBeginLoc(),
557 "function %0 registered here as signal handler", DiagnosticIDs::Note)
558 << cast<FunctionDecl>(Caller->getDecl())
559 << HandlerRef->getSourceRange();
constexpr llvm::StringLiteral MinimalConformingFunctions[]
constexpr llvm::StringLiteral POSIXConformingFunctions[]
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)
AST_MATCHER(BinaryOperator, isRelationalOperator)
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
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...