gamesh411 updated this revision to Diff 278181.
gamesh411 added a comment.

tidy up release notes, make all new check entries uniform


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D83717/new/

https://reviews.llvm.org/D83717

Files:
  clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
  clang-tools-extra/clang-tidy/cert/CMakeLists.txt
  clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.cpp
  clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/cert-env32-c.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  clang-tools-extra/test/clang-tidy/checkers/cert-env32-c.c
  llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn

Index: llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn
===================================================================
--- llvm/utils/gn/secondary/clang-tools-extra/clang-tidy/cert/BUILD.gn
+++ 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",
Index: clang-tools-extra/test/clang-tidy/checkers/cert-env32-c.c
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/cert-env32-c.c
@@ -0,0 +1,324 @@
+// RUN: %check_clang_tidy %s cert-env32-c %t
+
+// --------------
+// 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);
+
+// --------------------
+// 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
+};
+
+// 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
+}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -102,6 +102,7 @@
    `cert-dcl21-cpp <cert-dcl21-cpp.html>`_,
    `cert-dcl50-cpp <cert-dcl50-cpp.html>`_,
    `cert-dcl58-cpp <cert-dcl58-cpp.html>`_,
+   `cert-env32-c <cert-env32-c.html>`_,
    `cert-env33-c <cert-env33-c.html>`_,
    `cert-err34-c <cert-err34-c.html>`_,
    `cert-err52-cpp <cert-err52-cpp.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/cert-env32-c.rst
===================================================================
--- /dev/null
+++ 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 <stdlib.h>
+  
+  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 <stdlib.h>
+  
+  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 <stdlib.h>
+#include <setjmp.h>
+ 
+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 <stdlib.h>
+ 
+void exit1(void) {
+  return;
+}
+ 
+int main(void) {
+  if (atexit(exit1) != 0) {
+    /* Handle error */
+  }
+  return 0;
+}
+
+Description source: `<https://wiki.sei.cmu.edu/confluence/display/c/ENV32-C.+All+exit+handlers+must+return+normally>`_
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -82,8 +82,15 @@
   Finds ``s.find(...) == string::npos`` comparisons (for various string-like types)
   and suggests replacing with ``absl::StrContains()``.
 
+- New :doc:`cert-env32-c
+  <clang-tidy/checks/cert-env32-c>` check.
+
+  Finds functions registered by ``atexit`` and ``at_quick_exit`` that are calling
+  exit functions ``_Exit``, ``exit``, ``quick_exit`` or ``longjmp``.
+
 - New :doc:`cppcoreguidelines-avoid-non-const-global-variables
   <clang-tidy/checks/cppcoreguidelines-avoid-non-const-global-variables>` check.
+
   Finds non-const global variables as described in check I.2 of C++ Core
   Guidelines.
 
Index: clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.h
@@ -0,0 +1,41 @@
+//===--- 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;
+};
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CERT_EXITHANDLERCHECK_H
Index: clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/cert/ExitHandlerCheck.cpp
@@ -0,0 +1,151 @@
+//===--- Env32CCheck.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/SmallVector.h"
+#include <algorithm>
+#include <deque>
+#include <iterator>
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace cert {
+
+namespace {
+constexpr StringRef RegFunAtexit = "atexit";
+constexpr StringRef RegFunAtQuickExit = "at_quick_exit";
+
+/// The following functions are considered exit functions:
+/// '_Exit'
+/// 'exit'
+/// 'quick_exit'
+bool isExitFunction(StringRef FnName) {
+  return FnName == "_Exit" || FnName == "exit" || FnName == "quick_exit";
+}
+
+/// Only 'longjmp' is considered.
+bool isJumpFunction(StringRef FnName) { return FnName == "longjmp"; }
+
+class CalledFunctionsCollector
+    : public RecursiveASTVisitor<CalledFunctionsCollector> {
+  // The declarations and usages of encountered functions.
+  llvm::SmallVector<std::pair<const FunctionDecl *, const Expr *>, 32>
+      CalledFunctions;
+
+public:
+  bool VisitCallExpr(const CallExpr *CE) {
+    if (const auto *F = dyn_cast<FunctionDecl>(CE->getCalleeDecl()))
+      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(); }
+};
+} // namespace
+
+/// Match register-function calls, that has handler-functions as their first
+/// argument.
+void ExitHandlerCheck::registerMatchers(MatchFinder *Finder) {
+  const auto IsRegisterFunction =
+      callee(functionDecl(hasAnyName(RegFunAtexit, RegFunAtQuickExit)));
+  const auto HasHandlerAsFirstArg = hasArgument(
+      0, declRefExpr(hasDeclaration(functionDecl().bind("handler_decl")))
+             .bind("handler_expr"));
+  Finder->addMatcher(
+      callExpr(IsRegisterFunction, HasHandlerAsFirstArg).bind("register_call"),
+      this);
+}
+
+/// 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<CallExpr>("register_call");
+  const auto *HandlerDecl =
+      Result.Nodes.getNodeAs<FunctionDecl>("handler_decl");
+  const auto *HandlerExpr = Result.Nodes.getNodeAs<DeclRefExpr>("handler_expr");
+
+  // Visit each function encountered in the callgraph only once.
+  llvm::DenseSet<const FunctionDecl *> SeenFunctions;
+
+  // Reuse the ASTVistor instance that collects the called functions.
+  CalledFunctionsCollector Collector;
+
+  // The worklist of the callgraph visitation algorithm.
+  std::deque<std::pair<const FunctionDecl *, const Expr *>> 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;
+
+    // Check the name of the current function.
+    const StringRef CurrentName = Current->getName();
+    if (isExitFunction(CurrentName)) {
+      // 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(HandlerDecl->getBeginLoc(), "handler function declared here",
+           DiagnosticIDs::Note);
+      diag(CurrentUsage->getBeginLoc(), "exit function called here",
+           DiagnosticIDs::Note);
+      break;
+    }
+    if (isJumpFunction(CurrentName)) {
+      // 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(HandlerDecl->getBeginLoc(), "handler function declared here",
+           DiagnosticIDs::Note);
+      diag(CurrentUsage->getBeginLoc(), "jump function called here",
+           DiagnosticIDs::Note);
+      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.
+    std::move(Collector.begin(), Collector.end(),
+              std::back_inserter(CalledFunctions));
+  }
+}
+
+} // namespace cert
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/cert/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cert/CMakeLists.txt
+++ 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
Index: clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ 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<bugprone::ReservedIdentifierCheck>(
         "cert-dcl37-c");
     // ENV
+    CheckFactories.registerCheck<ExitHandlerCheck>("cert-env32-c");
     CheckFactories.registerCheck<CommandProcessorCheck>("cert-env33-c");
     // FLP
     CheckFactories.registerCheck<FloatLoopCounter>("cert-flp30-c");
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to