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 std::initializer_list<llvm::StringRef> 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 constexpr std::initializer_list<llvm::StringRef> POSIXConformingFunctions = { 26 "_Exit", 27 "_exit", 28 "abort", 29 "accept", 30 "access", 31 "aio_error", 32 "aio_return", 33 "aio_suspend", 34 "alarm", 35 "bind", 36 "cfgetispeed", 37 "cfgetospeed", 38 "cfsetispeed", 39 "cfsetospeed", 40 "chdir", 41 "chmod", 42 "chown", 43 "clock_gettime", 44 "close", 45 "connect", 46 "creat", 47 "dup", 48 "dup2", 49 "execl", 50 "execle", 51 "execv", 52 "execve", 53 "faccessat", 54 "fchdir", 55 "fchmod", 56 "fchmodat", 57 "fchown", 58 "fchownat", 59 "fcntl", 60 "fdatasync", 61 "fexecve", 62 "ffs", 63 "fork", 64 "fstat", 65 "fstatat", 66 "fsync", 67 "ftruncate", 68 "futimens", 69 "getegid", 70 "geteuid", 71 "getgid", 72 "getgroups", 73 "getpeername", 74 "getpgrp", 75 "getpid", 76 "getppid", 77 "getsockname", 78 "getsockopt", 79 "getuid", 80 "htonl", 81 "htons", 82 "kill", 83 "link", 84 "linkat", 85 "listen", 86 "longjmp", 87 "lseek", 88 "lstat", 89 "memccpy", 90 "memchr", 91 "memcmp", 92 "memcpy", 93 "memmove", 94 "memset", 95 "mkdir", 96 "mkdirat", 97 "mkfifo", 98 "mkfifoat", 99 "mknod", 100 "mknodat", 101 "ntohl", 102 "ntohs", 103 "open", 104 "openat", 105 "pause", 106 "pipe", 107 "poll", 108 "posix_trace_event", 109 "pselect", 110 "pthread_kill", 111 "pthread_self", 112 "pthread_sigmask", 113 "quick_exit", 114 "raise", 115 "read", 116 "readlink", 117 "readlinkat", 118 "recv", 119 "recvfrom", 120 "recvmsg", 121 "rename", 122 "renameat", 123 "rmdir", 124 "select", 125 "sem_post", 126 "send", 127 "sendmsg", 128 "sendto", 129 "setgid", 130 "setpgid", 131 "setsid", 132 "setsockopt", 133 "setuid", 134 "shutdown", 135 "sigaction", 136 "sigaddset", 137 "sigdelset", 138 "sigemptyset", 139 "sigfillset", 140 "sigismember", 141 "siglongjmp", 142 "signal", 143 "sigpause", 144 "sigpending", 145 "sigprocmask", 146 "sigqueue", 147 "sigset", 148 "sigsuspend", 149 "sleep", 150 "sockatmark", 151 "socket", 152 "socketpair", 153 "stat", 154 "stpcpy", 155 "stpncpy", 156 "strcat", 157 "strchr", 158 "strcmp", 159 "strcpy", 160 "strcspn", 161 "strlen", 162 "strncat", 163 "strncmp", 164 "strncpy", 165 "strnlen", 166 "strpbrk", 167 "strrchr", 168 "strspn", 169 "strstr", 170 "strtok_r", 171 "symlink", 172 "symlinkat", 173 "tcdrain", 174 "tcflow", 175 "tcflush", 176 "tcgetattr", 177 "tcgetpgrp", 178 "tcsendbreak", 179 "tcsetattr", 180 "tcsetpgrp", 181 "time", 182 "timer_getoverrun", 183 "timer_gettime", 184 "timer_settime", 185 "times", 186 "umask", 187 "uname", 188 "unlink", 189 "unlinkat", 190 "utime", 191 "utimensat", 192 "utimes", 193 "wait", 194 "waitpid", 195 "wcpcpy", 196 "wcpncpy", 197 "wcscat", 198 "wcschr", 199 "wcscmp", 200 "wcscpy", 201 "wcscspn", 202 "wcslen", 203 "wcsncat", 204 "wcsncmp", 205 "wcsncpy", 206 "wcsnlen", 207 "wcspbrk", 208 "wcsrchr", 209 "wcsspn", 210 "wcsstr", 211 "wcstok", 212 "wmemchr", 213 "wmemcmp", 214 "wmemcpy", 215 "wmemmove", 216 "wmemset", 217 "write"}; 218 219 using namespace clang::ast_matchers; 220 221 namespace clang { 222 namespace tidy { 223 224 template <> 225 struct OptionEnumMapping< 226 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind> { 227 static llvm::ArrayRef<std::pair< 228 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetKind, StringRef>> 229 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 makeArrayRef(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. 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 /// Given a call graph node of a \p Caller function and a \p Callee that is 281 /// called from \p Caller, get a \c CallExpr of the corresponding function call. 282 /// It is unspecified which call is found if multiple calls exist, but the order 283 /// should be deterministic (depend only on the AST). 284 Expr *findCallExpr(const CallGraphNode *Caller, const CallGraphNode *Callee) { 285 auto FoundCallee = llvm::find_if( 286 Caller->callees(), [Callee](const CallGraphNode::CallRecord &Call) { 287 return Call.Callee == Callee; 288 }); 289 assert(FoundCallee != Caller->end() && 290 "Callee should be called from the caller function here."); 291 return FoundCallee->CallExpr; 292 } 293 294 } // namespace 295 296 AST_MATCHER(FunctionDecl, isStandardFunction) { 297 return isStandardFunction(&Node); 298 } 299 300 SignalHandlerCheck::SignalHandlerCheck(StringRef Name, 301 ClangTidyContext *Context) 302 : ClangTidyCheck(Name, Context), 303 AsyncSafeFunctionSet( 304 Options.get("AsyncSafeFunctionSet", AsyncSafeFunctionSetKind::POSIX)), 305 ConformingFunctions(AsyncSafeFunctionSet == 306 AsyncSafeFunctionSetKind::Minimal 307 ? MinimalConformingFunctions 308 : POSIXConformingFunctions) {} 309 310 void SignalHandlerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 311 Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet); 312 } 313 314 bool SignalHandlerCheck::isLanguageVersionSupported( 315 const LangOptions &LangOpts) const { 316 // FIXME: Make the checker useful on C++ code. 317 if (LangOpts.CPlusPlus) 318 return false; 319 320 return true; 321 } 322 323 void SignalHandlerCheck::registerMatchers(MatchFinder *Finder) { 324 auto SignalFunction = functionDecl(hasAnyName("::signal", "::std::signal"), 325 parameterCountIs(2), isStandardFunction()); 326 auto HandlerExpr = 327 declRefExpr(hasDeclaration(functionDecl().bind("handler_decl")), 328 unless(isExpandedFromMacro("SIG_IGN")), 329 unless(isExpandedFromMacro("SIG_DFL"))) 330 .bind("handler_expr"); 331 Finder->addMatcher( 332 callExpr(callee(SignalFunction), hasArgument(1, HandlerExpr)) 333 .bind("register_call"), 334 this); 335 } 336 337 void SignalHandlerCheck::check(const MatchFinder::MatchResult &Result) { 338 const auto *HandlerDecl = 339 Result.Nodes.getNodeAs<FunctionDecl>("handler_decl"); 340 const auto *HandlerExpr = Result.Nodes.getNodeAs<DeclRefExpr>("handler_expr"); 341 assert(Result.Nodes.getNodeAs<CallExpr>("register_call") && HandlerDecl && 342 HandlerExpr && "All of these should exist in a match here."); 343 344 if (CG.size() <= 1) { 345 // Call graph must be populated with the entire TU at the beginning. 346 // (It is possible to add a single function but the functions called from it 347 // are not analysed in this case.) 348 CG.addToCallGraph(const_cast<TranslationUnitDecl *>( 349 HandlerDecl->getTranslationUnitDecl())); 350 assert(CG.size() > 1 && 351 "There should be at least one function added to call graph."); 352 } 353 354 if (!HandlerDecl->hasBody()) { 355 (void)checkFunction(HandlerDecl, HandlerExpr); 356 // Here checkFunction will put the messages to HandlerExpr. 357 // No need to show a call chain. 358 // Without code body there is nothing more to check. 359 return; 360 } 361 362 CallGraphNode *HandlerNode = CG.getNode(HandlerDecl->getCanonicalDecl()); 363 assert(HandlerNode && 364 "Handler with body should be present in the call graph."); 365 // Start from signal handler and visit every function call. 366 for (auto Itr = llvm::df_begin(HandlerNode), ItrE = llvm::df_end(HandlerNode); 367 Itr != ItrE; ++Itr) { 368 if (const auto *CallF = dyn_cast<FunctionDecl>((*Itr)->getDecl())) { 369 unsigned int PathL = Itr.getPathLength(); 370 const Expr *CallOrRef = (PathL == 1) 371 ? HandlerExpr 372 : findCallExpr(Itr.getPath(PathL - 2), *Itr); 373 if (checkFunction(CallF, CallOrRef)) 374 reportHandlerChain(Itr, HandlerExpr); 375 } 376 } 377 } 378 379 bool SignalHandlerCheck::checkFunction(const FunctionDecl *FD, 380 const Expr *CallOrRef) { 381 bool FunctionIsCalled = isa<CallExpr>(CallOrRef); 382 383 if (isStandardFunction(FD)) { 384 if (!isStandardFunctionAsyncSafe(FD)) { 385 diag(CallOrRef->getBeginLoc(), "standard function %0 may not be " 386 "asynchronous-safe; " 387 "%select{using it as|calling it from}1 " 388 "a signal handler may be dangerous") 389 << FD << FunctionIsCalled; 390 return true; 391 } 392 return false; 393 } 394 395 if (!FD->hasBody()) { 396 diag(CallOrRef->getBeginLoc(), "cannot verify that external function %0 is " 397 "asynchronous-safe; " 398 "%select{using it as|calling it from}1 " 399 "a signal handler may be dangerous") 400 << FD << FunctionIsCalled; 401 return true; 402 } 403 404 return false; 405 } 406 407 bool SignalHandlerCheck::isStandardFunctionAsyncSafe( 408 const FunctionDecl *FD) const { 409 assert(isStandardFunction(FD)); 410 411 const IdentifierInfo *II = FD->getIdentifier(); 412 // Unnamed functions are not explicitly allowed. 413 if (!II) 414 return false; 415 416 // FIXME: Improve for C++ (check for namespace). 417 if (ConformingFunctions.count(II->getName())) 418 return true; 419 420 return false; 421 } 422 423 void SignalHandlerCheck::reportHandlerChain( 424 const llvm::df_iterator<clang::CallGraphNode *> &Itr, 425 const DeclRefExpr *HandlerRef) { 426 int CallLevel = Itr.getPathLength() - 2; 427 assert(CallLevel >= -1 && "Empty iterator?"); 428 429 const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee = nullptr; 430 while (CallLevel >= 0) { 431 Callee = Caller; 432 Caller = Itr.getPath(CallLevel); 433 const Expr *CE = findCallExpr(Caller, Callee); 434 diag(CE->getBeginLoc(), "function %0 called here from %1", 435 DiagnosticIDs::Note) 436 << cast<FunctionDecl>(Callee->getDecl()) 437 << cast<FunctionDecl>(Caller->getDecl()); 438 --CallLevel; 439 } 440 441 diag(HandlerRef->getBeginLoc(), 442 "function %0 registered here as signal handler", DiagnosticIDs::Note) 443 << cast<FunctionDecl>(Caller->getDecl()) << HandlerRef->getSourceRange(); 444 } 445 446 } // namespace bugprone 447 } // namespace tidy 448 } // namespace clang 449