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 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 19 template <> 20 struct OptionEnumMapping< 21 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType> { 22 static llvm::ArrayRef<std::pair< 23 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType, StringRef>> 24 getEnumMapping() { 25 static constexpr std::pair< 26 bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType, StringRef> 27 Mapping[] = { 28 {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType::Minimal, 29 "minimal"}, 30 {bugprone::SignalHandlerCheck::AsyncSafeFunctionSetType::POSIX, 31 "POSIX"}, 32 }; 33 return makeArrayRef(Mapping); 34 } 35 }; 36 37 namespace bugprone { 38 39 namespace { 40 41 bool isSystemCall(const FunctionDecl *FD) { 42 // Find a possible redeclaration in system header. 43 // FIXME: Looking at the canonical declaration is not the most exact way 44 // to do this. 45 46 // Most common case will be inclusion directly from a header. 47 // This works fine by using canonical declaration. 48 // a.c 49 // #include <sysheader.h> 50 51 // Next most common case will be extern declaration. 52 // Can't catch this with either approach. 53 // b.c 54 // extern void sysfunc(void); 55 56 // Canonical declaration is the first found declaration, so this works. 57 // c.c 58 // #include <sysheader.h> 59 // extern void sysfunc(void); // redecl won't matter 60 61 // This does not work with canonical declaration. 62 // Probably this is not a frequently used case but may happen (the first 63 // declaration can be in a non-system header for example). 64 // d.c 65 // extern void sysfunc(void); // Canonical declaration, not in system header. 66 // #include <sysheader.h> 67 68 return FD->getASTContext().getSourceManager().isInSystemHeader( 69 FD->getCanonicalDecl()->getLocation()); 70 } 71 72 /// Given a call graph node of a function and another one that is called from 73 /// this function, get a CallExpr of the corresponding function call. 74 /// It is unspecified which call is found if multiple calls exist, but the order 75 /// should be deterministic (depend only on the AST). 76 Expr *findCallExpr(const CallGraphNode *Caller, const CallGraphNode *Callee) { 77 auto FoundCallee = llvm::find_if( 78 Caller->callees(), [Callee](const CallGraphNode::CallRecord &Call) { 79 return Call.Callee == Callee; 80 }); 81 assert(FoundCallee != Caller->end() && 82 "Callee should be called from the caller function here."); 83 return FoundCallee->CallExpr; 84 } 85 86 } // namespace 87 88 AST_MATCHER(FunctionDecl, isSystemCall) { return isSystemCall(&Node); } 89 90 SignalHandlerCheck::SignalHandlerCheck(StringRef Name, 91 ClangTidyContext *Context) 92 : ClangTidyCheck(Name, Context), 93 AsyncSafeFunctionSet( 94 Options.get("AsyncSafeFunctionSet", AsyncSafeFunctionSetType::POSIX)), 95 ConformingFunctions(AsyncSafeFunctionSet == 96 AsyncSafeFunctionSetType::Minimal 97 ? MinimalConformingFunctions 98 : POSIXConformingFunctions) {} 99 100 void SignalHandlerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 101 Options.store(Opts, "AsyncSafeFunctionSet", AsyncSafeFunctionSet); 102 } 103 104 bool SignalHandlerCheck::isLanguageVersionSupported( 105 const LangOptions &LangOpts) const { 106 // FIXME: Make the checker useful on C++ code. 107 if (LangOpts.CPlusPlus) 108 return false; 109 110 return true; 111 } 112 113 void SignalHandlerCheck::registerMatchers(MatchFinder *Finder) { 114 auto SignalFunction = functionDecl(hasAnyName("::signal", "::std::signal"), 115 parameterCountIs(2), isSystemCall()); 116 auto HandlerExpr = 117 declRefExpr(hasDeclaration(functionDecl().bind("handler_decl")), 118 unless(isExpandedFromMacro("SIG_IGN")), 119 unless(isExpandedFromMacro("SIG_DFL"))) 120 .bind("handler_expr"); 121 Finder->addMatcher( 122 callExpr(callee(SignalFunction), hasArgument(1, HandlerExpr)) 123 .bind("register_call"), 124 this); 125 } 126 127 void SignalHandlerCheck::check(const MatchFinder::MatchResult &Result) { 128 const auto *SignalCall = Result.Nodes.getNodeAs<CallExpr>("register_call"); 129 const auto *HandlerDecl = 130 Result.Nodes.getNodeAs<FunctionDecl>("handler_decl"); 131 const auto *HandlerExpr = Result.Nodes.getNodeAs<DeclRefExpr>("handler_expr"); 132 assert(SignalCall && HandlerDecl && HandlerExpr && 133 "All of these should exist in a match here."); 134 135 if (CG.size() <= 1) { 136 // Call graph must be populated with the entire TU at the beginning. 137 // (It is possible to add a single function but the functions called from it 138 // are not analysed in this case.) 139 CG.addToCallGraph(const_cast<TranslationUnitDecl *>( 140 HandlerDecl->getTranslationUnitDecl())); 141 assert(CG.size() > 1 && 142 "There should be at least one function added to call graph."); 143 } 144 145 // Check for special case when the signal handler itself is an unsafe external 146 // function. 147 if (!isFunctionAsyncSafe(HandlerDecl)) { 148 reportBug(HandlerDecl, HandlerExpr, /*DirectHandler=*/true); 149 return; 150 } 151 152 CallGraphNode *HandlerNode = CG.getNode(HandlerDecl); 153 // Signal handler can be external but not unsafe, no call graph in this case. 154 if (!HandlerNode) 155 return; 156 // Start from signal handler and visit every function call. 157 for (auto Itr = llvm::df_begin(HandlerNode), ItrE = llvm::df_end(HandlerNode); 158 Itr != ItrE; ++Itr) { 159 const auto *CallF = dyn_cast<FunctionDecl>((*Itr)->getDecl()); 160 if (CallF && !isFunctionAsyncSafe(CallF)) { 161 assert(Itr.getPathLength() >= 2); 162 reportBug(CallF, findCallExpr(Itr.getPath(Itr.getPathLength() - 2), *Itr), 163 /*DirectHandler=*/false); 164 reportHandlerCommon(Itr, SignalCall, HandlerDecl, HandlerExpr); 165 } 166 } 167 } 168 169 bool SignalHandlerCheck::isFunctionAsyncSafe(const FunctionDecl *FD) const { 170 if (isSystemCall(FD)) 171 return isSystemCallAsyncSafe(FD); 172 // For external (not checkable) functions assume that these are unsafe. 173 return FD->hasBody(); 174 } 175 176 bool SignalHandlerCheck::isSystemCallAsyncSafe(const FunctionDecl *FD) const { 177 const IdentifierInfo *II = FD->getIdentifier(); 178 // Unnamed functions are not explicitly allowed. 179 if (!II) 180 return false; 181 182 // FIXME: Improve for C++ (check for namespace). 183 if (ConformingFunctions.count(II->getName())) 184 return true; 185 186 return false; 187 } 188 189 void SignalHandlerCheck::reportBug(const FunctionDecl *CalledFunction, 190 const Expr *CallOrRef, bool DirectHandler) { 191 diag(CallOrRef->getBeginLoc(), 192 "%0 may not be asynchronous-safe; %select{calling it from|using it as}1 " 193 "a signal handler may be dangerous") 194 << CalledFunction << DirectHandler; 195 } 196 197 void SignalHandlerCheck::reportHandlerCommon( 198 llvm::df_iterator<clang::CallGraphNode *> Itr, const CallExpr *SignalCall, 199 const FunctionDecl *HandlerDecl, const Expr *HandlerRef) { 200 int CallLevel = Itr.getPathLength() - 2; 201 assert(CallLevel >= -1 && "Empty iterator?"); 202 203 const CallGraphNode *Caller = Itr.getPath(CallLevel + 1), *Callee = nullptr; 204 while (CallLevel >= 0) { 205 Callee = Caller; 206 Caller = Itr.getPath(CallLevel); 207 const Expr *CE = findCallExpr(Caller, Callee); 208 diag(CE->getBeginLoc(), "function %0 called here from %1", 209 DiagnosticIDs::Note) 210 << cast<FunctionDecl>(Callee->getDecl()) 211 << cast<FunctionDecl>(Caller->getDecl()); 212 --CallLevel; 213 } 214 215 diag(HandlerRef->getBeginLoc(), 216 "function %0 registered here as signal handler", DiagnosticIDs::Note) 217 << HandlerDecl; 218 } 219 220 // This is the minimal set of safe functions. 221 // https://wiki.sei.cmu.edu/confluence/display/c/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers 222 llvm::StringSet<> SignalHandlerCheck::MinimalConformingFunctions{ 223 "signal", "abort", "_Exit", "quick_exit"}; 224 225 // The POSIX-defined set of safe functions. 226 // https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04_03 227 // 'quick_exit' is added to the set additionally because it looks like the 228 // mentioned POSIX specification was not updated after 'quick_exit' appeared 229 // in the C11 standard. 230 // Also, we want to keep the "minimal set" a subset of the "POSIX set". 231 llvm::StringSet<> SignalHandlerCheck::POSIXConformingFunctions{ 232 "_Exit", 233 "_exit", 234 "abort", 235 "accept", 236 "access", 237 "aio_error", 238 "aio_return", 239 "aio_suspend", 240 "alarm", 241 "bind", 242 "cfgetispeed", 243 "cfgetospeed", 244 "cfsetispeed", 245 "cfsetospeed", 246 "chdir", 247 "chmod", 248 "chown", 249 "clock_gettime", 250 "close", 251 "connect", 252 "creat", 253 "dup", 254 "dup2", 255 "execl", 256 "execle", 257 "execv", 258 "execve", 259 "faccessat", 260 "fchdir", 261 "fchmod", 262 "fchmodat", 263 "fchown", 264 "fchownat", 265 "fcntl", 266 "fdatasync", 267 "fexecve", 268 "ffs", 269 "fork", 270 "fstat", 271 "fstatat", 272 "fsync", 273 "ftruncate", 274 "futimens", 275 "getegid", 276 "geteuid", 277 "getgid", 278 "getgroups", 279 "getpeername", 280 "getpgrp", 281 "getpid", 282 "getppid", 283 "getsockname", 284 "getsockopt", 285 "getuid", 286 "htonl", 287 "htons", 288 "kill", 289 "link", 290 "linkat", 291 "listen", 292 "longjmp", 293 "lseek", 294 "lstat", 295 "memccpy", 296 "memchr", 297 "memcmp", 298 "memcpy", 299 "memmove", 300 "memset", 301 "mkdir", 302 "mkdirat", 303 "mkfifo", 304 "mkfifoat", 305 "mknod", 306 "mknodat", 307 "ntohl", 308 "ntohs", 309 "open", 310 "openat", 311 "pause", 312 "pipe", 313 "poll", 314 "posix_trace_event", 315 "pselect", 316 "pthread_kill", 317 "pthread_self", 318 "pthread_sigmask", 319 "quick_exit", 320 "raise", 321 "read", 322 "readlink", 323 "readlinkat", 324 "recv", 325 "recvfrom", 326 "recvmsg", 327 "rename", 328 "renameat", 329 "rmdir", 330 "select", 331 "sem_post", 332 "send", 333 "sendmsg", 334 "sendto", 335 "setgid", 336 "setpgid", 337 "setsid", 338 "setsockopt", 339 "setuid", 340 "shutdown", 341 "sigaction", 342 "sigaddset", 343 "sigdelset", 344 "sigemptyset", 345 "sigfillset", 346 "sigismember", 347 "siglongjmp", 348 "signal", 349 "sigpause", 350 "sigpending", 351 "sigprocmask", 352 "sigqueue", 353 "sigset", 354 "sigsuspend", 355 "sleep", 356 "sockatmark", 357 "socket", 358 "socketpair", 359 "stat", 360 "stpcpy", 361 "stpncpy", 362 "strcat", 363 "strchr", 364 "strcmp", 365 "strcpy", 366 "strcspn", 367 "strlen", 368 "strncat", 369 "strncmp", 370 "strncpy", 371 "strnlen", 372 "strpbrk", 373 "strrchr", 374 "strspn", 375 "strstr", 376 "strtok_r", 377 "symlink", 378 "symlinkat", 379 "tcdrain", 380 "tcflow", 381 "tcflush", 382 "tcgetattr", 383 "tcgetpgrp", 384 "tcsendbreak", 385 "tcsetattr", 386 "tcsetpgrp", 387 "time", 388 "timer_getoverrun", 389 "timer_gettime", 390 "timer_settime", 391 "times", 392 "umask", 393 "uname", 394 "unlink", 395 "unlinkat", 396 "utime", 397 "utimensat", 398 "utimes", 399 "wait", 400 "waitpid", 401 "wcpcpy", 402 "wcpncpy", 403 "wcscat", 404 "wcschr", 405 "wcscmp", 406 "wcscpy", 407 "wcscspn", 408 "wcslen", 409 "wcsncat", 410 "wcsncmp", 411 "wcsncpy", 412 "wcsnlen", 413 "wcspbrk", 414 "wcsrchr", 415 "wcsspn", 416 "wcsstr", 417 "wcstok", 418 "wmemchr", 419 "wmemcmp", 420 "wmemcpy", 421 "wmemmove", 422 "wmemset", 423 "write"}; 424 425 } // namespace bugprone 426 } // namespace tidy 427 } // namespace clang 428