diff --git a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp @@ -24,6 +24,7 @@ #include "CommandProcessorCheck.h" #include "DefaultOperatorNewAlignmentCheck.h" #include "DontModifyStdNamespaceCheck.h" +#include "ExitHandlerCheck.h" #include "FloatLoopCounter.h" #include "LimitedRandomnessCheck.h" #include "MutatingCopyCheck.h" @@ -95,6 +96,7 @@ CheckFactories.registerCheck( "cert-dcl37-c"); // ENV + CheckFactories.registerCheck("cert-env32-c"); CheckFactories.registerCheck("cert-env33-c"); // FLP CheckFactories.registerCheck("cert-flp30-c"); diff --git a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cert/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cert/CMakeLists.txt @@ -8,6 +8,7 @@ CommandProcessorCheck.cpp DefaultOperatorNewAlignmentCheck.cpp DontModifyStdNamespaceCheck.cpp + ExitHandlerCheck.cpp FloatLoopCounter.cpp LimitedRandomnessCheck.cpp MutatingCopyCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.h b/clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.h @@ -0,0 +1,45 @@ +//===--- ExitHandlerCheck.h - clang-tidy ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_EXITHANDLERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_EXITHANDLERCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace cert { + +/// Checker for SEI CERT rule ENV32-C +/// All exit handlers must return normally. +/// Exit handlers must terminate by returning. It is important and potentially +/// safety-critical for all exit handlers to be allowed to perform their cleanup +/// actions. This is particularly true because the application programmer does +/// not always know about handlers that may have been installed by support +/// libraries. Two specific issues include nested calls to an exit function and +/// terminating a call to an exit handler by invoking longjmp. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cert-exit-handler-check.html +class ExitHandlerCheck : public ClangTidyCheck { +public: + ExitHandlerCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void reportExitFunction(const CallExpr *RegisterCall, + const FunctionDecl *Handler, const Expr *Usage); + void reportJumpFunction(const CallExpr *RegisterCall, + const FunctionDecl *Handler, const Expr *Usage); +}; + +} // namespace cert +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_EXITHANDLERCHECK_H diff --git a/clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.cpp b/clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.cpp @@ -0,0 +1,179 @@ +//===--- ExitHandlerCheck.cpp - clang-tidy --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ExitHandlerCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cert { + +namespace { + +/// The following functions are considered exit functions: +/// '_Exit' +/// 'exit' +/// 'quick_exit' +/// 'abort' +/// 'terminate' +/// But only if they are in the global or ::std namespace, with the exception of +/// terminate, which only exists in the std namespace. +bool isExitFunction(const FunctionDecl *FD) { + const StringRef FN = FD->getName(); + const bool InStdNS = FD->isInStdNamespace(); + if (FN == "terminate" && InStdNS) + return true; + + constexpr StringRef GlobalAndStdFunctions[] = {"_Exit", "exit", "quick_exit", + "abort"}; + const bool InGlobalNS = FD->isGlobal(); + return llvm::is_contained(GlobalAndStdFunctions, FN) && + (InGlobalNS || InStdNS); +} + +/// Only global and ::std namespaced 'longjmp' functions are considered. +bool isJumpFunction(const FunctionDecl *FD) { + return FD->getName() == "longjmp" && + (FD->isGlobal() || FD->isInStdNamespace()); +} + +class CalledFunctionsCollector + : public RecursiveASTVisitor { + // The declarations and usages of encountered functions. + llvm::SmallVector, 32> + CalledFunctions; + +public: + bool VisitCallExpr(const CallExpr *CE) { + if (const auto *F = CE->getDirectCallee()) + CalledFunctions.emplace_back(F, CE); + return true; + } + + void clear() { CalledFunctions.clear(); } + + /// Iteration over the collector is iteration over the found FunctionDecls. + /// In order to allow moving from the underlying container, non-const + /// interators are allowed. + auto begin() { return CalledFunctions.begin(); } + auto end() { return CalledFunctions.end(); } +}; + +constexpr char HandlerDeclLabel[] = "handler_decl"; +constexpr char HandlerExprLabel[] = "handler_expr"; +constexpr char RegisterCallLabel[] = "register_call"; + +} // namespace + +/// Match register-function calls, that has handler-functions as their first +/// argument. +void ExitHandlerCheck::registerMatchers(MatchFinder *Finder) { + const auto IsRegisterFunction = callee(functionDecl(hasAnyName( + "::atexit", "std::atexit", "::at_quick_exit", "std::at_quick_exit"))); + const auto HasHandlerAsFirstArg = hasArgument( + 0, declRefExpr(hasDeclaration(functionDecl().bind(HandlerDeclLabel))) + .bind(HandlerExprLabel)); + Finder->addMatcher(callExpr(IsRegisterFunction, HasHandlerAsFirstArg) + .bind(RegisterCallLabel), + this); +} + +void ExitHandlerCheck::reportExitFunction(const CallExpr *RegisterCall, + const FunctionDecl *Handler, + const Expr *Usage) { + // An exit-function is encountered somewhere in the callgraph of the + // handler. + diag(RegisterCall->getBeginLoc(), + "exit-handler potentially calls an exit function. Handlers should " + "terminate by returning"); + diag(Handler->getBeginLoc(), "handler function declared here", + DiagnosticIDs::Note); + diag(Usage->getBeginLoc(), "exit function called here", DiagnosticIDs::Note); +} + +void ExitHandlerCheck::reportJumpFunction(const CallExpr *RegisterCall, + const FunctionDecl *Handler, + const Expr *Usage) { + // A jump function is encountered somewhere in the callgraph of the + // handler. + diag(RegisterCall->getSourceRange().getBegin(), + "exit-handler potentially calls a jump function. Handlers should " + "terminate by returning"); + diag(Handler->getBeginLoc(), "handler function declared here", + DiagnosticIDs::Note); + diag(Usage->getBeginLoc(), "jump function called here", DiagnosticIDs::Note); +} + +/// Check if the callgraph of the handler-function contains any exit functions +/// or jump functions. +void ExitHandlerCheck::check(const MatchFinder::MatchResult &Result) { + const auto *RegisterCall = + Result.Nodes.getNodeAs(RegisterCallLabel); + const auto *HandlerDecl = + Result.Nodes.getNodeAs(HandlerDeclLabel); + const auto *HandlerExpr = + Result.Nodes.getNodeAs(HandlerExprLabel); + + // Visit each function encountered in the callgraph only once. + llvm::DenseSet SeenFunctions; + + // Reuse the ASTVistor instance that collects the called functions. + CalledFunctionsCollector Collector; + + // The worklist of the callgraph visitation algorithm. + std::deque> CalledFunctions{ + {HandlerDecl, HandlerExpr}}; + + // Visit the definition of every function referenced by the handler function, + // and look for exit-functions and jump calls. + while (!CalledFunctions.empty()) { + // Use the canonical declaration for uniquing. + const FunctionDecl *Current = + CalledFunctions.front().first->getCanonicalDecl(); + const Expr *CurrentUsage = CalledFunctions.front().second; + CalledFunctions.pop_front(); + + // Do not visit functions with same canonical declaration twice. + if (!SeenFunctions.insert(Current).second) + continue; + + if (isExitFunction(Current)) { + reportExitFunction(RegisterCall, HandlerDecl, CurrentUsage); + break; + } + if (isJumpFunction(Current)) { + reportJumpFunction(RegisterCall, HandlerDecl, CurrentUsage); + break; + } + + // Get the body of the encountered non-exit and non-longjmp function. + const FunctionDecl *CurrentDefWithBody; + if (!Current->hasBody(CurrentDefWithBody)) + continue; + + // Reset the ASTVisitor instance results. + Collector.clear(); + // Collect all the referenced FunctionDecls. + Collector.TraverseStmt(CurrentDefWithBody->getBody()); + // Move the called functions to the worklist. + llvm::move(Collector, std::back_inserter(CalledFunctions)); + } +} + +} // namespace cert +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -103,6 +103,15 @@ Added an option `GetConfigPerFile` to support including files which use different naming styles. +New checks +^^^^^^^^^^ + +- New :doc:`cert-env32-c + ` check. + + Finds functions registered by ``atexit`` and ``at_quick_exit`` that are calling + exit functions ``_Exit``, ``exit``, ``quick_exit`` or ``longjmp``. + Improvements to include-fixer ----------------------------- diff --git a/clang-tools-extra/docs/clang-tidy/checks/cert-env32-c.rst b/clang-tools-extra/docs/clang-tidy/checks/cert-env32-c.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cert-env32-c.rst @@ -0,0 +1,158 @@ +.. title:: clang-tidy - cert-env32-c + +cert-env32-c +============ + +Finds functions registered by ``atexit`` and ``at_quick_exit`` that are calling +exit functions ``_Exit``, ``exit``, ``quick_exit`` or ``longjmp``. + +All exit handlers must return normally +-------------------------------------- + +The C Standard provides three functions that cause an application to terminate +normally: ``_Exit``, ``exit``, and ``quick_exit``. These are collectively called +exit functions. When the ``exit`` function is called, or control transfers out +of the ``main`` entry point function, functions registered with ``atexit`` are +called (but not ``at_quick_exit``). When the ``quick_exit`` function is called, +functions registered with ``at_quick_exit`` (but not ``atexit``) are called. +These functions are collectively called exit handlers. When the ``_Exit`` +function is called, no exit handlers or signal handlers are called. + +Exit handlers must terminate by returning. It is important and potentially +safety-critical for all exit handlers to be allowed to perform their cleanup +actions. This is particularly true because the application programmer does not +always know about handlers that may have been installed by support libraries. +Two specific issues include nested calls to an exit function and terminating a +call to an exit handler by invoking ``longjmp``. + +A nested call to an exit function is undefined behavior. (See undefined behavior +182.) This behavior can occur only when an exit function is invoked from an exit +handler or when an exit function is called from within a signal handler. + +If a call to the ``longjmp`` function is made that would terminate the call to a +function registered with ``atexit``, the behavior is undefined. + +Noncompliant Code Example + +In this noncompliant code example, the ``exit1`` and ``exit2`` functions are +registered by ``atexit`` to perform required cleanup upon program termination. +However, if ``some_condition`` evaluates to true, ``exit`` is called a second +time, resulting in undefined behavior. + +.. code-block:: c + + #include + + void exit1(void) { + /* ... Cleanup code ... */ + return; + } + + void exit2(void) { + extern int some_condition; + if (some_condition) { + /* ... More cleanup code ... */ + exit(0); + } + return; + } + + int main(void) { + if (atexit(exit1) != 0) { + /* Handle error */ + } + if (atexit(exit2) != 0) { + /* Handle error */ + } + /* ... Program code ... */ + return 0; + } + +Functions registered by the ``atexit`` function are called in the reverse order +from which they were registered. Consequently, if ``exit2`` exits in any way +other than by returning, ``exit1`` will not be executed. The same may also be +true for ``atexit`` handlers installed by support libraries. + +Compliant Solution + +A function that is registered as an exit handler by ``atexit`` must exit by +returning, as in this compliant solution: + +.. code-block:: c + + #include + + void exit1(void) { + /* ... Cleanup code ... */ + return; + } + + void exit2(void) { + extern int some_condition; + if (some_condition) { + /* ... More cleanup code ... */ + } + return; + } + + int main(void) { + if (atexit(exit1) != 0) { + /* Handle error */ + } + if (atexit(exit2) != 0) { + /* Handle error */ + } + /* ... Program code ... */ + return 0; + } + +Noncompliant Code Example + +In this noncompliant code example, ``exit1`` is registered by ``atexit`` so that +upon program termination, ``exit1`` is called. The ``exit1`` function jumps back +to ``main`` to return, with undefined results. + +.. code-block:: c + +#include +#include + +jmp_buf env; +int val; + +void exit1(void) { + longjmp(env, 1); +} + +int main(void) { + if (atexit(exit1) != 0) { + /* Handle error */ + } + if (setjmp(env) == 0) { + exit(0); + } else { + return 0; + } +} + +Compliant Solution + +This compliant solution does not call ``longjmp`` but instead returns from the +exit handler normally: + +.. code-block:: c + +#include + +void exit1(void) { + return; +} + +int main(void) { + if (atexit(exit1) != 0) { + /* Handle error */ + } + return 0; +} + +Description source: ``_ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -104,6 +104,7 @@ `cert-dcl21-cpp `_, `cert-dcl50-cpp `_, `cert-dcl58-cpp `_, + `cert-env32-c `_, `cert-env33-c `_, `cert-err34-c `_, `cert-err52-cpp `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/cert-env32-c-teminate.cpp b/clang-tools-extra/test/clang-tidy/checkers/cert-env32-c-teminate.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cert-env32-c-teminate.cpp @@ -0,0 +1,43 @@ +// Test that terminate is only detected in std namespace. +// RUN: %check_clang_tidy %s cert-env32-c %t + +// Register handlers to run when exit is called. +int atexit(void (*)(void)); + +namespace std { +void terminate(); + +void callTerminate() { + terminate(); +} + +void callCallTerminate() { + callTerminate(); +} + +void testStdTerminate() { + (void)atexit(callTerminate); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-11]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-11]]:3: note: exit function called here + (void)atexit(callCallTerminate); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-11]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-15]]:3: note: exit function called here +} +} //namespace std + +void terminate(); + +void callTerminate() { + terminate(); +} + +void callCallTerminate() { + callTerminate(); +} + +void testGlobalTerminate() { + (void)atexit(callTerminate); + (void)atexit(callCallTerminate); +} diff --git a/clang-tools-extra/test/clang-tidy/checkers/cert-env32-c.c b/clang-tools-extra/test/clang-tidy/checkers/cert-env32-c.c new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cert-env32-c.c @@ -0,0 +1,412 @@ +// Test as a C file. +// RUN: %check_clang_tidy %s cert-env32-c %t -- -- -DCMODE +// +// Test as a C++ file. +// +// Test functions in global namespace. +// RUN: %check_clang_tidy -assume-filename=%s.cpp %s cert-env32-c %t \ +// RUN: -- -- -DCPPMODE +// +// Test functions in std namespace. +// RUN: %check_clang_tidy -assume-filename=%s.cpp %s cert-env32-c %t \ +// RUN: -- -- -DCPPMODE -DTEST_NS_NAME=std + +#if defined(CPPMODE) && defined(TEST_NS_NAME) +namespace TEST_NS_NAME { +#endif + +// -------------- +// EXIT FUNCTIONS +// -------------- + +// No handlers are invoked when _Exit is called. +void _Exit(int __status); + +// Handlers registered by atexit are invoked in reverse order when exit is +// called. +void exit(int __status); + +// Handlers registered by at_quick_exit are invoked in reverse order when +// quick_exit is called. +void quick_exit(int __status); + +// The program is terminated without destroying any object and without calling +// any of the functions passed to atexit or at_quick_exit. +void abort(); + +// -------------------- +// HANDLER REGISTRATION +// -------------------- + +// Register handlers to run when exit is called. +int atexit(void (*__func)(void)); + +// Register handlers to run when exit is called. +int at_quick_exit(void (*__func)(void)); + +// -------------- +// Setjmp/longjmp +// -------------- +// C99 requires jmp_buf to be an array type. +typedef int jmp_buf[1]; +int setjmp(jmp_buf); +void longjmp(jmp_buf, int); + +// Compliant solutions + +void cleanup1() { + // do cleanup +} + +void cleanup2() { + // do cleanup +} + +void test_atexit_single_compliant() { + (void)atexit(cleanup1); +} + +void test_atexit_multiple_compliant() { + (void)atexit(cleanup1); + (void)atexit(cleanup2); +} + +void test_at_quick_exit_single_compliant() { + (void)at_quick_exit(cleanup1); +} + +void test_at_quick_exit_multiple_compliant() { + (void)at_quick_exit(cleanup1); + (void)at_quick_exit(cleanup2); +} + +// Non-compliant solutions calling _Exit + +void call__Exit() { + _Exit(0); +} + +void call_call__Exit() { + call__Exit(); +} + +extern int unknown__Exit_flag; + +void call__Exit_conditionally() { + if (unknown__Exit_flag) + call__Exit(); +} + +void call_call__Exit_conditionally() { + call__Exit_conditionally(); +} + +void test__Exit_called_directly() { + (void)atexit(call__Exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-22]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-22]]:3: note: exit function called here + (void)at_quick_exit(call__Exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-26]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-26]]:3: note: exit function called here +}; + +void test__Exit_called_indirectly() { + (void)atexit(call_call__Exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-29]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-33]]:3: note: exit function called here + (void)at_quick_exit(call_call__Exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-33]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-37]]:3: note: exit function called here +}; + +void test_conditional__Exit_called_directly() { + (void)atexit(call__Exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-34]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-44]]:3: note: exit function called here + (void)at_quick_exit(call__Exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-38]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-48]]:3: note: exit function called here +}; + +void test_conditional__Exit_called_indirectly() { + (void)atexit(call_call__Exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-40]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-55]]:3: note: exit function called here + (void)at_quick_exit(call_call__Exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-44]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-59]]:3: note: exit function called here +}; + +// Non-compliant solutions calling exit + +void call_exit() { + exit(0); +} + +void call_call_exit() { + call_exit(); +} + +extern int unknown_exit_flag; + +void call_exit_conditionally() { + if (unknown_exit_flag) + call_exit(); +} + +void call_call_exit_conditionally() { + call_exit_conditionally(); +} + +void test_exit_called_directly() { + (void)atexit(call_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-22]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-22]]:3: note: exit function called here + (void)at_quick_exit(call_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-26]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-26]]:3: note: exit function called here +}; + +void test_exit_called_indirectly() { + (void)atexit(call_call_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-29]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-33]]:3: note: exit function called here + (void)at_quick_exit(call_call_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-33]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-37]]:3: note: exit function called here +}; + +void test_conditional_exit_called_directly() { + (void)atexit(call_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-34]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-44]]:3: note: exit function called here + (void)at_quick_exit(call_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-38]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-48]]:3: note: exit function called here +}; + +void test_conditional_exit_called_indirectly() { + (void)atexit(call_call_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-40]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-55]]:3: note: exit function called here + (void)at_quick_exit(call_call_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-44]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-59]]:3: note: exit function called here +}; + +// Non-compliant solutions calling quick_exit + +void call_quick_exit() { + quick_exit(0); +} + +void call_call_quick_exit() { + call_quick_exit(); +} + +extern int unknown_quick_exit_flag; + +void call_quick_exit_conditionally() { + if (unknown_quick_exit_flag) + call_quick_exit(); +} + +void call_call_quick_exit_conditionally() { + call_quick_exit_conditionally(); +} + +void test_quick_exit_called_directly() { + (void)atexit(call_quick_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-22]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-22]]:3: note: exit function called here + (void)at_quick_exit(call_quick_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-26]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-26]]:3: note: exit function called here +}; + +void test_quick_exit_called_indirectly() { + (void)atexit(call_call_quick_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-29]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-33]]:3: note: exit function called here + (void)at_quick_exit(call_call_quick_exit); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-33]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-37]]:3: note: exit function called here +}; + +void test_conditional_quick_exit_called_directly() { + (void)atexit(call_quick_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-34]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-44]]:3: note: exit function called here + (void)at_quick_exit(call_quick_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-38]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-48]]:3: note: exit function called here +}; + +void test_conditional_quick_exit_called_indirectly() { + (void)atexit(call_call_quick_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-40]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-55]]:3: note: exit function called here + (void)at_quick_exit(call_call_quick_exit_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-44]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-59]]:3: note: exit function called here +}; + +// Non-compliant solutions calling abort + +void call_abort() { + abort(); +} + +void call_call_abort() { + call_abort(); +} + +extern int unknown_abort_flag; + +void call_abort_conditionally() { + if (unknown_abort_flag) + call_abort(); +} + +void call_call_abort_conditionally() { + call_abort_conditionally(); +} + +void test_abort_called_directly() { + (void)atexit(call_abort); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-22]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-22]]:3: note: exit function called here + (void)at_quick_exit(call_abort); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-26]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-26]]:3: note: exit function called here +}; + +void test_abort_called_indirectly() { + (void)atexit(call_call_abort); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-29]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-33]]:3: note: exit function called here + (void)at_quick_exit(call_call_abort); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-33]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-37]]:3: note: exit function called here +}; + +void test_conditional_abort_called_directly() { + (void)atexit(call_abort_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-34]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-44]]:3: note: exit function called here + (void)at_quick_exit(call_abort_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-38]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-48]]:3: note: exit function called here +}; + +void test_conditional_abort_called_indirectly() { + (void)atexit(call_call_abort_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-40]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-55]]:3: note: exit function called here + (void)at_quick_exit(call_call_abort_conditionally); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-44]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-59]]:3: note: exit function called here +}; + +// Mixed compliant and non-compliant solutions. + +void call_exit2() { + exit(0); +} + +void test_compliant_and_noncompliant_atexits() { + (void)atexit(cleanup1); + (void)atexit(call_exit2); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-8]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-8]]:3: note: exit function called here + (void)at_quick_exit(call_exit2); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-12]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-12]]:3: note: exit function called here +} + +// Non-compliant solution using recursion. + +extern int unknown_recursion_flag; + +void recursive_hander() { + if (unknown_recursion_flag > 0) { + --unknown_recursion_flag; + recursive_hander(); + } + exit(0); +} + +void test_recursive_handler() { + (void)atexit(recursive_hander); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-11]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-7]]:3: note: exit function called here + (void)at_quick_exit(recursive_hander); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls an exit function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-15]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-11]]:3: note: exit function called here +} + +// Non-compliant solution using jumps. + +jmp_buf env; +extern int unknown_error_flag; + +void longjmp_handler() { + if (setjmp(env)) { + // error handling + } + + if (unknown_error_flag) { + longjmp(env, 255); + } +} + +void test_longjmp_handler() { + (void)atexit(longjmp_handler); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls a jump function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-13]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-8]]:5: note: jump function called here + (void)at_quick_exit(longjmp_handler); + // CHECK-NOTES: :[[@LINE-1]]:9: warning: exit-handler potentially calls a jump function. Handlers should terminate by returning [cert-env32-c] + // CHECK-NOTES: :[[@LINE-17]]:1: note: handler function declared here + // CHECK-NOTES: :[[@LINE-12]]:5: note: jump function called here +} + +#if defined(CPPMODE) && defined(TEST_NS_NAME) +} +#endif diff --git a/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn b/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn --- a/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn +++ b/llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn @@ -19,6 +19,7 @@ "CommandProcessorCheck.cpp", "DefaultOperatorNewAlignmentCheck.cpp", "DontModifyStdNamespaceCheck.cpp", + "ExitHandlerCheck.cpp", "FloatLoopCounter.cpp", "LimitedRandomnessCheck.cpp", "MutatingCopyCheck.cpp",