brintos

brintos / llvm-project-archived public Read only

0
0
Text · 16.5 KiB · 282a3b2 Raw
564 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//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+handlers16constexpr 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_0321// 'quick_exit' is added to the set additionally because it looks like the22// mentioned POSIX specification was not updated after 'quick_exit' appeared23// 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 to26// date.27// clang-format off28constexpr llvm::StringLiteral POSIXConformingFunctions[] = {29    "_Exit",30    "_exit",31    "abort",32    "accept",33    "access",34    "aio_error",35    "aio_return",36    "aio_suspend",37    "alarm",38    "bind",39    "cfgetispeed",40    "cfgetospeed",41    "cfsetispeed",42    "cfsetospeed",43    "chdir",44    "chmod",45    "chown",46    "clock_gettime",47    "close",48    "connect",49    "creat",50    "dup",51    "dup2",52    "execl",53    "execle",54    "execv",55    "execve",56    "faccessat",57    "fchdir",58    "fchmod",59    "fchmodat",60    "fchown",61    "fchownat",62    "fcntl",63    "fdatasync",64    "fexecve",65    "ffs",66    "fork",67    "fstat",68    "fstatat",69    "fsync",70    "ftruncate",71    "futimens",72    "getegid",73    "geteuid",74    "getgid",75    "getgroups",76    "getpeername",77    "getpgrp",78    "getpid",79    "getppid",80    "getsockname",81    "getsockopt",82    "getuid",83    "htonl",84    "htons",85    "kill",86    "link",87    "linkat",88    "listen",89    "longjmp",90    "lseek",91    "lstat",92    "memccpy",93    "memchr",94    "memcmp",95    "memcpy",96    "memmove",97    "memset",98    "mkdir",99    "mkdirat",100    "mkfifo",101    "mkfifoat",102    "mknod",103    "mknodat",104    "ntohl",105    "ntohs",106    "open",107    "openat",108    "pause",109    "pipe",110    "poll",111    "posix_trace_event",112    "pselect",113    "pthread_kill",114    "pthread_self",115    "pthread_sigmask",116    "quick_exit",117    "raise",118    "read",119    "readlink",120    "readlinkat",121    "recv",122    "recvfrom",123    "recvmsg",124    "rename",125    "renameat",126    "rmdir",127    "select",128    "sem_post",129    "send",130    "sendmsg",131    "sendto",132    "setgid",133    "setpgid",134    "setsid",135    "setsockopt",136    "setuid",137    "shutdown",138    "sigaction",139    "sigaddset",140    "sigdelset",141    "sigemptyset",142    "sigfillset",143    "sigismember",144    "siglongjmp",145    "signal",146    "sigpause",147    "sigpending",148    "sigprocmask",149    "sigqueue",150    "sigset",151    "sigsuspend",152    "sleep",153    "sockatmark",154    "socket",155    "socketpair",156    "stat",157    "stpcpy",158    "stpncpy",159    "strcat",160    "strchr",161    "strcmp",162    "strcpy",163    "strcspn",164    "strlen",165    "strncat",166    "strncmp",167    "strncpy",168    "strnlen",169    "strpbrk",170    "strrchr",171    "strspn",172    "strstr",173    "strtok_r",174    "symlink",175    "symlinkat",176    "tcdrain",177    "tcflow",178    "tcflush",179    "tcgetattr",180    "tcgetpgrp",181    "tcsendbreak",182    "tcsetattr",183    "tcsetpgrp",184    "time",185    "timer_getoverrun",186    "timer_gettime",187    "timer_settime",188    "times",189    "umask",190    "uname",191    "unlink",192    "unlinkat",193    "utime",194    "utimensat",195    "utimes",196    "wait",197    "waitpid",198    "wcpcpy",199    "wcpncpy",200    "wcscat",201    "wcschr",202    "wcscmp",203    "wcscpy",204    "wcscspn",205    "wcslen",206    "wcsncat",207    "wcsncmp",208    "wcsncpy",209    "wcsnlen",210    "wcspbrk",211    "wcsrchr",212    "wcsspn",213    "wcsstr",214    "wcstok",215    "wmemchr",216    "wmemcmp",217    "wmemcpy",218    "wmemmove",219    "wmemset",220    "write"221};222// clang-format on223 224using namespace clang::ast_matchers;225 226namespace clang::tidy {227 228template <>229struct OptionEnumMapping<230    bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind> {231  static llvm::ArrayRef<std::pair<232      bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef>>233  getEnumMapping() {234    static constexpr std::pair<235        bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef>236        Mapping[] = {237            {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind::Minimal,238             "minimal"},239            {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind::POSIX,240             "POSIX"},241        };242    return {Mapping};243  }244};245 246namespace bugprone {247 248/// Returns if a function is declared inside a system header.249/// These functions are considered to be "standard" (system-provided) library250/// functions.251static bool isStandardFunction(const FunctionDecl *FD) {252  // Find a possible redeclaration in system header.253  // FIXME: Looking at the canonical declaration is not the most exact way254  // to do this.255 256  // Most common case will be inclusion directly from a header.257  // This works fine by using canonical declaration.258  // a.c259  // #include <sysheader.h>260 261  // Next most common case will be extern declaration.262  // Can't catch this with either approach.263  // b.c264  // extern void sysfunc(void);265 266  // Canonical declaration is the first found declaration, so this works.267  // c.c268  // #include <sysheader.h>269  // extern void sysfunc(void); // redecl won't matter270 271  // This does not work with canonical declaration.272  // Probably this is not a frequently used case but may happen (the first273  // declaration can be in a non-system header for example).274  // d.c275  // extern void sysfunc(void); // Canonical declaration, not in system header.276  // #include <sysheader.h>277 278  return FD->getASTContext().getSourceManager().isInSystemHeader(279      FD->getCanonicalDecl()->getLocation());280}281 282/// Check if a statement is "C++-only".283/// This includes all statements that have a class name with "CXX" prefix284/// and every other statement that is declared in file ExprCXX.h.285static bool isCXXOnlyStmt(const Stmt *S) {286  const StringRef Name = S->getStmtClassName();287  if (Name.starts_with("CXX"))288    return true;289  // Check for all other class names in ExprCXX.h that have no 'CXX' prefix.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);299}300 301/// Given a call graph node of a \p Caller function and a \p Callee that is302/// called from \p Caller, get a \c CallExpr of the corresponding function call.303/// It is unspecified which call is found if multiple calls exist, but the order304/// should be deterministic (depend only on the AST).305static Expr *findCallExpr(const CallGraphNode *Caller,306                          const CallGraphNode *Callee) {307  const auto *FoundCallee = llvm::find_if(308      Caller->callees(), [Callee](const CallGraphNode::CallRecord &Call) {309        return Call.Callee == Callee;310      });311  assert(FoundCallee != Caller->end() &&312         "Callee should be called from the caller function here.");313  return FoundCallee->CallExpr;314}315 316static SourceRange getSourceRangeOfStmt(const Stmt *S, ASTContext &Ctx) {317  ParentMapContext &PM = Ctx.getParentMapContext();318  DynTypedNode P = DynTypedNode::create(*S);319  while (P.getSourceRange().isInvalid()) {320    const DynTypedNodeList PL = PM.getParents(P);321    if (PL.size() != 1)322      return {};323    P = PL[0];324  }325  return P.getSourceRange();326}327 328namespace {329 330AST_MATCHER(FunctionDecl, isStandard) { return isStandardFunction(&Node); }331 332} // namespace333 334SignalHandlerCheck::SignalHandlerCheck(StringRef Name,335                                       ClangTidyContext *Context)336    : ClangTidyCheck(Name, Context),337      AsyncSafeFunctionSet(Options.get("AsyncSafeFunctionSet",338                                       AsyncSafeFunctionSetKind::POSIX)) {339  if (AsyncSafeFunctionSet == AsyncSafeFunctionSetKind::Minimal)340    ConformingFunctions.insert_range(MinimalConformingFunctions);341  else342    ConformingFunctions.insert_range(POSIXConformingFunctions);343}344 345void SignalHandlerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {346  Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet);347}348 349bool SignalHandlerCheck::isLanguageVersionSupported(350    const LangOptions &LangOpts) const {351  return !LangOpts.CPlusPlus17;352}353 354void SignalHandlerCheck::registerMatchers(MatchFinder *Finder) {355  auto SignalFunction = functionDecl(hasAnyName("::signal", "::std::signal"),356                                     parameterCountIs(2), isStandard());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 370void 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 it388    // 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  const CallGraphNode *HandlerNode =405      CG.getNode(HandlerDecl->getCanonicalDecl());406  assert(HandlerNode &&407         "Handler with body should be present in the call graph.");408  // Start from signal handler and visit every function call.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    const unsigned int PathL = Itr.getPathLength();413    if (CallF) {414      // A signal handler or a function transitively reachable from the signal415      // handler was found to be unsafe.416      // Generate notes for the whole call chain (including the signal handler417      // registration).418      const Expr *CallOrRef = (PathL > 1)419                                  ? findCallExpr(Itr.getPath(PathL - 2), *Itr)420                                  : HandlerExpr;421      auto ChainReporter = [this, &Itr, HandlerExpr](bool SkipPathEnd) {422        reportHandlerChain(Itr, HandlerExpr, SkipPathEnd);423      };424      // If problems were found in a function (`CallF`), skip the analysis of425      // functions that are called from it.426      if (checkFunction(CallF, CallOrRef, ChainReporter))427        Itr.skipChildren();428      else429        ++Itr;430    } else {431      ++Itr;432    }433  }434}435 436bool SignalHandlerCheck::checkFunction(437    const FunctionDecl *FD, const Expr *CallOrRef,438    llvm::function_ref<void(bool)> ChainReporter) {439  const bool FunctionIsCalled = isa<CallExpr>(CallOrRef);440 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();448      if (ChainReporter)449        ChainReporter(/*SkipPathEnd=*/true);450      return true;451    }452    return false;453  }454 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();461    if (ChainReporter)462      ChainReporter(/*SkipPathEnd=*/true);463    return true;464  }465 466  if (getLangOpts().CPlusPlus)467    return checkFunctionCPP14(FD, CallOrRef, ChainReporter);468 469  return false;470}471 472bool SignalHandlerCheck::checkFunctionCPP14(473    const FunctionDecl *FD, const Expr *CallOrRef,474    llvm::function_ref<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)");479    if (ChainReporter)480      ChainReporter(/*SkipPathEnd=*/true);481    return true;482  }483 484  const FunctionDecl *FBody = nullptr;485  const Stmt *BodyS = FD->getBody(FBody);486  if (!BodyS)487    return false;488 489  bool StmtProblemsFound = false;490  ASTContext &Ctx = FBody->getASTContext();491  auto Matches =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      const SourceRange R = getSourceRangeOfStmt(FoundS, Ctx);497      if (R.isInvalid())498        continue;499      diag(R.getBegin(),500           "C++-only construct is not allowed in signal handler (until C++17)")501          << R;502      diag(R.getBegin(), "internally, the statement is parsed as a '%0'",503           DiagnosticIDs::Remark)504          << FoundS->getStmtClassName();505      if (ChainReporter)506        ChainReporter(/*SkipPathEnd=*/false);507      StmtProblemsFound = true;508    }509  }510 511  return StmtProblemsFound;512}513 514bool SignalHandlerCheck::isStandardFunctionAsyncSafe(515    const FunctionDecl *FD) const {516  assert(isStandardFunction(FD));517 518  const IdentifierInfo *II = FD->getIdentifier();519  // Unnamed functions are not explicitly allowed.520  // C++ std operators may be unsafe and not within the521  // "common subset of C and C++".522  if (!II)523    return false;524 525  if (!FD->isInStdNamespace() && !FD->isGlobal())526    return false;527 528  if (ConformingFunctions.contains(II->getName()))529    return true;530 531  return false;532}533 534void SignalHandlerCheck::reportHandlerChain(535    const llvm::df_iterator<const clang::CallGraphNode *> &Itr,536    const DeclRefExpr *HandlerRef, bool SkipPathEnd) {537  int CallLevel = Itr.getPathLength() - 2;538  assert(CallLevel >= -1 && "Empty iterator?");539 540  const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee = nullptr;541  while (CallLevel >= 0) {542    Callee = Caller;543    Caller = Itr.getPath(CallLevel);544    const Expr *CE = findCallExpr(Caller, Callee);545    if (SkipPathEnd)546      SkipPathEnd = false;547    else548      diag(CE->getBeginLoc(), "function %0 called here from %1",549           DiagnosticIDs::Note)550          << cast<FunctionDecl>(Callee->getDecl())551          << cast<FunctionDecl>(Caller->getDecl());552    --CallLevel;553  }554 555  if (!SkipPathEnd)556    diag(HandlerRef->getBeginLoc(),557         "function %0 registered here as signal handler", DiagnosticIDs::Note)558        << cast<FunctionDecl>(Caller->getDecl())559        << HandlerRef->getSourceRange();560}561 562} // namespace bugprone563} // namespace clang::tidy564