This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch branch-1.18.x
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/branch-1.18.x by this push:
     new d181955a3 KUDU-3635 call OPENSSL_cleanup() explicitly
d181955a3 is described below

commit d181955a338729b9310fd587a2b6374be5624340
Author: Alexey Serbin <[email protected]>
AuthorDate: Mon Jul 28 16:24:44 2025 -0700

    KUDU-3635 call OPENSSL_cleanup() explicitly
    
    With this changelist, the global state of the OpenSSL library is now
    explicitly cleaned up when shutting down a process.  The rationale
    for this is outlined in [1].
    
    This is applicable to the code linked against OpenSSL library of
    versions 1.1.1 and newer. IIUC, that covers all the contemporary Linux
    distributions at the time of writing.  It worked for me at EOL Ubuntu
    18.04 LTS as well (in particular, I tested it on Ubuntu 18.04.1 LTS).
    
    As for testing, I verified that the issue is gone after observing its
    manifestation with the frequency of about 1 in 10 runs of the kudu CLI
    tool with RELEASE bits on RHEL8.8 and RHEL9.2 (both of x86_64 arch)
    without the patch.  For reproduction and verification, I ran the
    following 100rep test run multiple times (and 1000rep just for
    verification):
    
      ./bin/kudu-tool-test --gtest_repeat=100
    
    Without the patch, I saw many core files left by the kudu CLI binary
    during every 100rep run, where many of the core files had stack traces
    similar to the one described in the JIRA item.  With this patch,
    no such core files were observed when running Kudu RELEASE bits.
    However, there were still crashes with core files having stack traces
    attributable to KUDU-2439.  That's addressed in a follow-up patch.
    
    [1] 
https://developers.redhat.com/articles/2022/10/31/best-practices-application-shutdown-openssl
    
    Change-Id: Ib08310d66a7eabb1996bde901f39f36f54bff483
    Reviewed-on: http://gerrit.cloudera.org:8080/23222
    Tested-by: Alexey Serbin <[email protected]>
    Reviewed-by: Abhishek Chennaka <[email protected]>
    (cherry picked from commit bda99a38bb7da12959a99ffc9a79de02acdacf2a)
    Reviewed-on: http://gerrit.cloudera.org:8080/23265
    Reviewed-by: Alexey Serbin <[email protected]>
---
 src/kudu/master/master_main.cc         |   4 +
 src/kudu/tools/tool_main.cc            |   4 +
 src/kudu/tserver/tablet_server_main.cc |   4 +
 src/kudu/util/before_and_after_main.h  |  99 +++++++++++++++++++++
 src/kudu/util/entry_exit_wrapper.h     |  57 +++++++++++++
 src/kudu/util/openssl_util.cc          | 152 ++++++++++++++++++++++++++++-----
 src/kudu/util/openssl_util.h           |  16 ++++
 src/kudu/util/process_memory.cc        |   5 +-
 src/kudu/util/test_main.cc             |   6 ++
 thirdparty/build-definitions.sh        |   4 +
 10 files changed, 326 insertions(+), 25 deletions(-)

diff --git a/src/kudu/master/master_main.cc b/src/kudu/master/master_main.cc
index c01455a0e..f93b37d2d 100644
--- a/src/kudu/master/master_main.cc
+++ b/src/kudu/master/master_main.cc
@@ -25,6 +25,10 @@
 #include "kudu/util/logging.h"
 #include "kudu/util/status.h"
 
+// Code for proper sequencing of tcmalloc and OpenSSL initialization/shutdown
+// that runs before/after main().
+#include "kudu/util/before_and_after_main.h"  // IWYU pragma: keep
+
 namespace kudu {
 namespace master {
 
diff --git a/src/kudu/tools/tool_main.cc b/src/kudu/tools/tool_main.cc
index dc9055efa..779f41c89 100644
--- a/src/kudu/tools/tool_main.cc
+++ b/src/kudu/tools/tool_main.cc
@@ -39,6 +39,10 @@
 #include "kudu/util/process_memory.h"
 #include "kudu/util/status.h"
 
+// Code for proper sequencing of tcmalloc and OpenSSL initialization/shutdown
+// that runs before/after main().
+#include "kudu/util/before_and_after_main.h"  // IWYU pragma: keep
+
 DECLARE_bool(help);
 DECLARE_bool(helppackage);
 DECLARE_bool(helpshort);
diff --git a/src/kudu/tserver/tablet_server_main.cc 
b/src/kudu/tserver/tablet_server_main.cc
index 38a9d5f58..b277a102e 100644
--- a/src/kudu/tserver/tablet_server_main.cc
+++ b/src/kudu/tserver/tablet_server_main.cc
@@ -25,6 +25,10 @@
 #include "kudu/util/logging.h"
 #include "kudu/util/status.h"
 
+// Code for proper sequencing of tcmalloc and OpenSSL initialization/shutdown
+// that runs before/after main().
+#include "kudu/util/before_and_after_main.h"  // IWYU pragma: keep
+
 namespace kudu {
 namespace tserver {
 
diff --git a/src/kudu/util/before_and_after_main.h 
b/src/kudu/util/before_and_after_main.h
new file mode 100644
index 000000000..c83924f77
--- /dev/null
+++ b/src/kudu/util/before_and_after_main.h
@@ -0,0 +1,99 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+// This file is intended for direct inclusion into .cc files where the main()
+// function is defined, so no 'pragma once' or include guard is needed.
+//
+// This approach addresses KUDU-3635 for OpenSSL versions 1.1.1 and newer
+// and serves as a stop-gap for KUDU-2439.
+
+#include <functional>
+
+#include <glog/logging.h>
+#include <glog/raw_logging.h>
+#if defined(TCMALLOC_ENABLED)
+#include <gperftools/tcmalloc_guard.h>
+#endif
+
+#if !defined(KUDU_TEST_MAIN)
+#include "kudu/rpc/messenger.h"
+#endif
+#include "kudu/util/entry_exit_wrapper.h"
+#if !defined(KUDU_TEST_MAIN)
+#include "kudu/util/monotime.h"
+#endif
+#include "kudu/util/openssl_util.h"
+#include "kudu/util/process_memory.h"
+
+static void module_init_tcmalloc() {
+#if defined(TCMALLOC_ENABLED) && !defined(NDEBUG)
+  // Just a sanity check for non-release builds: make sure tcmalloc has already
+  // installed its shims and isn't barfing after TCMallocGuard's constructor.
+  RAW_DCHECK(kudu::process_memory::GetTCMallocCurrentAllocatedBytes() != 0,
+             "tcmalloc should be fully functional at this point");
+#endif
+}
+
+static void module_fini_tcmalloc() {
+#if defined(TCMALLOC_ENABLED) && !defined(NDEBUG)
+  // Non-release builds only: as an extra sanity check, poke tcmalloc a bit
+  // to make sure it's still operational and not barfing at this point.
+  RAW_VLOG(2, "invoking tcmalloc GC");
+  kudu::process_memory::GcTcmalloc();
+#endif
+}
+
+static void module_init_openssl() {
+  // Make sure OpenSSL hasn't been yet initialized by Kudu at this point.
+  RAW_DCHECK(!kudu::security::IsOpenSSLInitialized(),
+             "OpenSSL shouldn't be initialized yet");
+
+  // Mark the OpenSSL to be initialized with the OPENSSL_INIT_NO_ATEXIT option,
+  // i.e. instruct the OpenSSL not to register its atexit() hook because
+  // the application will call OPENSSL_cleanup() itself.
+  kudu::security::SetStandaloneInit(true);
+#if !defined(KUDU_TEST_MAIN)
+  // For kudu-{master,tserver} and kudu CLI binary, explicitly initialize
+  // the OpenSSL library before the main() function. As for the Kudu's tests,
+  // they implicitly initialize the library on-demand from the context of their
+  // main() function in test_main.cc, if needed.
+  kudu::security::InitializeOpenSSL();
+#endif
+}
+
+static void module_fini_openssl() {
+  // Call OPENSSL_cleanup() to release resources and clean up the global state
+  // of the library: it's applicable to OpenSSL 1.1.1 and newer versions.
+  // At this point, tcmalloc must still be operational.
+  RAW_VLOG(2, "cleaning up OpenSSL runtime");
+  kudu::security::FinalizeOpenSSL();
+}
+
+#if defined(TCMALLOC_ENABLED)
+// Make sure tcmalloc is up and running, and it has already installed shims
+// for all the necessary symbols. Maybe, that's too much and just having 
symbols
+// set by the linker when linking and loading libtcmalloc should be good 
enough,
+// but this approach with TCMallocGuard is guaranteed bulletproof.
+static TCMallocGuard g_tcmalloc_initializer;
+#endif
+// Make sure tcmalloc is opened and initialized prior to initializing OpenSSL's
+// runtime before entering main().
+static kudu::util::EntryExitWrapper g_wrapper_tcmalloc(module_init_tcmalloc,
+                                                       module_fini_tcmalloc);
+// After exiting from main(), clean up the OpenSSL's library global state.
+static kudu::util::EntryExitWrapper g_wrapper_openssl(module_init_openssl,
+                                                      module_fini_openssl);
diff --git a/src/kudu/util/entry_exit_wrapper.h 
b/src/kudu/util/entry_exit_wrapper.h
new file mode 100644
index 000000000..550e4081e
--- /dev/null
+++ b/src/kudu/util/entry_exit_wrapper.h
@@ -0,0 +1,57 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#pragma once
+
+#include <functional>
+#include <utility>
+
+namespace kudu {
+namespace util {
+
+// This class allows for calling of the specified void functions in its
+// constructor and destructor correspondingly, which makes it helpful
+// in building initialization and wrap-up sequencing within a namespace
+// scope of the same translation unit. By the C++ standard, objects defined
+// at namespace scope are guaranteed to be initialized in an order in strict
+// accordance with that of their definitions in a given translation unit,
+// and the destructors are invoked in the reverse order. This applies
+// to the global namespace scope of a single translation unit as well.
+class EntryExitWrapper final {
+ public:
+  EntryExitWrapper(std::function<void()> init_func,
+                   std::function<void()> fini_func)
+      : init_func_(std::move(init_func)),
+        fini_func_(std::move(fini_func)) {
+    if (init_func_) {
+      init_func_();
+    }
+  }
+
+  ~EntryExitWrapper() {
+    if (fini_func_) {
+      fini_func_();
+    }
+  }
+
+ private:
+   const std::function<void()> init_func_;
+   const std::function<void()> fini_func_;
+};
+
+} // namespace util
+} // namespace kudu
diff --git a/src/kudu/util/openssl_util.cc b/src/kudu/util/openssl_util.cc
index 33e985b62..85a07d5cb 100644
--- a/src/kudu/util/openssl_util.cc
+++ b/src/kudu/util/openssl_util.cc
@@ -29,6 +29,7 @@
 #include <vector>
 
 #include <glog/logging.h>
+#include <glog/raw_logging.h>
 
 #include "kudu/gutil/strings/split.h"
 #include "kudu/gutil/strings/strip.h"
@@ -48,6 +49,17 @@
 #include "kudu/util/thread.h"
 #endif
 
+// Some ancient Linux distros have a strange arrangement with the actual 
version
+// of the OpenSSL library reported both by the library runtime and the actual
+// contents of the header files under /usr/include/openssl. In particular,
+// Ubuntu 18.04.1 LTS has OpenSSL of version 1.1.1-1ubuntu2.1~18.04.13 which
+// openssl's binary outputs 'OpenSSL 1.1.1  11 Sep 2018' when running
+// 'openssl version', but the header files do not have OPENSSL_INIT_NO_ATEXIT
+// macro defined.
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined(OPENSSL_INIT_NO_ATEXIT)
+#define OPENSSL_INIT_NO_ATEXIT  0x00080000L
+#endif
+
 using std::ostringstream;
 using std::string;
 using std::vector;
@@ -61,7 +73,7 @@ namespace {
 //
 // Thread safety:
 // - written by DoInitializeOpenSSL (single-threaded, due to std::call_once)
-// - read by DisableOpenSSLInitialization (must not be concurrent with above)
+// - read by IsOpenSSLInitialized (must not be concurrent with above)
 bool g_ssl_is_initialized = false;
 
 // If true, then we expect someone else has initialized SSL.
@@ -71,6 +83,14 @@ bool g_ssl_is_initialized = false;
 // - written by DisableOpenSSLInitialization (must not be concurrent with 
above)
 bool g_disable_ssl_init = false;
 
+// Whether the OpenSSL library is used in the context of a standalone
+// application that controls what happens before and after its main() function.
+// Kudu servers and the 'kudu' CLI utility are examples of such. This affects
+// how the initialization and shutdown of the OpenSSL library are performed.
+// Essentially, this is to decide whether to add OPENSSL_INIT_NO_ATEXIT option
+// for OPENSSL_init_ssl(), and call OPENSSL_cleanup() in DoFinalizeOpenSSL().
+bool g_is_standalone_init = false;
+
 // Array of locks used by OpenSSL.
 // We use an intentionally-leaked C-style array here to avoid non-POD static 
data.
 //
@@ -104,10 +124,10 @@ void CheckFIPSMode() {
   // check if FIPS approved mode is enabled. If not, we crash the process.
   // As this is used in clients as well, we can't use gflags to set this.
   if (GetBooleanEnvironmentVariable("KUDU_REQUIRE_FIPS_MODE")) {
-    CHECK(fips_mode) << "FIPS mode required by environment variable "
-                        "KUDU_REQUIRE_FIPS_MODE, but it is not enabled.";
+    // NOTE: using RAW_CHECK() because this might be called before main()
+    RAW_CHECK(fips_mode, "FIPS mode required by environment variable "
+                         "KUDU_REQUIRE_FIPS_MODE, but it is not enabled");
   }
-  VLOG(2) << "FIPS mode is " << (fips_mode ? "enabled" : "disabled.");
 }
 
 Status CheckOpenSSLInitialized() {
@@ -146,14 +166,58 @@ Status CheckOpenSSLInitialized() {
 }
 
 void DoInitializeOpenSSL() {
-  // In case the user's thread has left some error around, clear it.
-  ERR_clear_error();
-  SCOPED_OPENSSL_NO_PENDING_ERRORS;
+  // If running in standalone mode, do not call _anything_ from the OpenSSL
+  // library prior to initializing it with the necessary custom options below.
+  // Otherwise, that might lead to OPENSSL_init_crypto()/OPENSSL_init_ssl()
+  // being called from within the library itself, initializing it with
+  // the default or other undesirable set of options. Doing so might result
+  // in registering unnecessary atexit() handlers and other unexpected 
behavior.
+  // See [1] and [2] for more details:
+  //
+  //   Numerous internal OpenSSL functions call OPENSSL_init_crypto().
+  //   Therefore, in order to perform nondefault initialisation,
+  //   OPENSSL_init_crypto() MUST be called by application code
+  //   prior to any other OpenSSL function calls.
+  //
+  //   Numerous internal OpenSSL functions call OPENSSL_init_ssl().
+  //   Therefore, in order to perform nondefault initialisation,
+  //   OPENSSL_init_ssl() MUST be called by application code
+  //   prior to any other OpenSSL function calls.
+  //
+  // [1] https://docs.openssl.org/1.1.1/man3/OPENSSL_init_crypto/
+  // [2] https://docs.openssl.org/1.1.1/man3/OPENSSL_init_ssl/
+  if (!g_is_standalone_init) {
+    // In non-standalone mode (e.g., running as the Kudu C++ client library
+    // in a user application), if the user's thread has left some error
+    // around, clear it to avoid reporting unrelated 'ghost' errors later on.
+    ERR_clear_error();
+    SCOPED_OPENSSL_NO_PENDING_ERRORS;
+  }
   if (g_disable_ssl_init) {
-    VLOG(2) << "Not initializing OpenSSL (disabled by application)";
     return;
   }
+
 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
+  // Set custom options for the OpenSSL library:
+  // automatic loading of the libcrypto and the libssl error strings. That are
+  // the default options, but their presence guarantees that any (unexpected)
+  // follow-up requests to change the behavior by specifying
+  // OPENSSL_INIT_NO_LOAD_CRYPTO_STRINGS and/or 
OPENSSL_INIT_NO_LOAD_SSL_STRINGS
+  // will be ignored.
+  int init_opt =
+      OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
+      OPENSSL_INIT_LOAD_SSL_STRINGS;
+
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+  // KUDU-3635: add OPENSSL_INIT_NO_ATEXIT option for fine-grained control
+  // over the OpenSSL library's lifecycle to prevent races with finalization
+  // of the tcmalloc library's runtime. The OPENSSL_INIT_NO_ATEXIT option was
+  // introduced in OpenSSL 1.1.1.
+  if (g_is_standalone_init) {
+    init_opt |= OPENSSL_INIT_NO_ATEXIT;
+  }
+#endif  // #if OPENSSL_VERSION_NUMBER >= 0x10101000L ...
+
   // The OPENSSL_init_ssl manpage [1] says "As of version 1.1.0 OpenSSL will
   // automatically allocate all resources it needs so no explicit 
initialisation
   // is required." However, eliding library initialization leads to a memory
@@ -166,10 +230,13 @@ void DoInitializeOpenSSL() {
   // Rather than determine whether this particular OpenSSL instance is
   // leak-free, we'll initialize the library explicitly.
   //
-  // 1. https://www.openssl.org/docs/man1.1.0/ssl/OPENSSL_init_ssl.html
+  // 1. https://docs.openssl.org/1.1.1/man3/OPENSSL_init_ssl/
   // 2. https://github.com/openssl/openssl/issues/5899
-  CHECK_EQ(1, OPENSSL_init_ssl(0, nullptr));
-#else
+  //
+  // NOTE: using RAW_CHECK() because this might be called before main().
+  RAW_CHECK(1 == OPENSSL_init_ssl(init_opt, nullptr),
+            "failed to initialize OpenSSL");
+#else   // #if OPENSSL_VERSION_NUMBER >= 0x10100000L ...
   // Check that OpenSSL isn't already initialized. If it is, it's likely
   // we are embedded in (or embedding) another application/library which
   // initializes OpenSSL, and we risk installing conflicting callbacks
@@ -177,12 +244,12 @@ void DoInitializeOpenSSL() {
   // log a warning.
   auto ctx = ssl_make_unique(SSL_CTX_new(SSLv23_method()));
   if (ctx) {
-    LOG(WARNING) << "It appears that OpenSSL has been previously initialized 
by "
-                    "code outside of Kudu. Please first properly initialize "
-                    "OpenSSL for multi-threaded usage (setting thread callback 
"
-                    "functions for OpenSSL of versions earlier than 1.1.0) and 
"
-                    "then call kudu::client::DisableOpenSSLInitialization() "
-                    "to avoid potential crashes due to conflicting 
initialization.";
+    RAW_LOG(WARNING) << "It appears that OpenSSL has been previously 
initialized by "
+                        "code outside of Kudu. Please first properly 
initialize "
+                        "OpenSSL for multi-threaded usage (setting thread 
callback "
+                        "functions for OpenSSL of versions earlier than 1.1.0) 
and "
+                        "then call 
kudu::client::DisableOpenSSLInitialization() "
+                        "to avoid potential crashes due to conflicting 
initialization.";
     // Continue anyway; all of the below is idempotent, except for the locking 
callback,
     // which we check before overriding. They aren't thread-safe, however -- 
that's why
     // we try to get embedding applications to do the right thing here rather 
than risk a
@@ -203,7 +270,7 @@ void DoInitializeOpenSSL() {
     // LSAN warnings.
     debug::ScopedLeakCheckDisabler d;
     int num_locks = CRYPTO_num_locks();
-    CHECK(!kCryptoLocks);
+    RAW_CHECK(!kCryptoLocks);
     kCryptoLocks = new Mutex[num_locks];
 
     // Callbacks used by OpenSSL required in a multi-threaded setting.
@@ -211,11 +278,32 @@ void DoInitializeOpenSSL() {
 
     CRYPTO_THREADID_set_callback(ThreadIdCB);
   }
-#endif
+#endif // #if OPENSSL_VERSION_NUMBER >= 0x10100000L ... #else ...
   CheckFIPSMode();
   g_ssl_is_initialized = true;
 }
 
+void DoFinalizeOpenSSL() {
+  if (!g_ssl_is_initialized) {
+    // If we haven't yet initialized the library, don't try to finalize it.
+    return;
+  }
+#ifdef NDEBUG
+  // In case the user's thread has left some error around, clear it.
+  // Do so only in release builds, but catch corresponding programming errors
+  // if anything is left on the error stack otherwise.
+  ERR_clear_error();
+#endif
+  SCOPED_OPENSSL_NO_PENDING_ERRORS;
+#if OPENSSL_VERSION_NUMBER >= 0x10101000L
+  if (g_is_standalone_init) {
+    // If OPENSSL_INIT_NO_ATEXIT option was used for OPENSSL_init_ssl() or
+    // OPENSSL_init_crypto(), it's time to perform the necessary cleanup.
+    OPENSSL_cleanup();
+  }
+#endif
+}
+
 } // anonymous namespace
 
 // Reads a STACK_OF(X509) from the BIO and returns it.
@@ -283,21 +371,39 @@ void free_STACK_OF_X509(STACK_OF(X509)* sk) {
 }
 
 Status DisableOpenSSLInitialization() {
-  if (g_disable_ssl_init) return Status::OK();
-  if (g_ssl_is_initialized) {
-    return Status::IllegalState("SSL already initialized. Initialization can 
only be disabled "
-                                "before first usage.");
+  if (g_disable_ssl_init) {
+    return Status::OK();
+  }
+  if (IsOpenSSLInitialized()) {
+    return Status::IllegalState(
+        "OpenSSL is already initialized. Initialization can only be disabled "
+        "before first usage.");
   }
   RETURN_NOT_OK(CheckOpenSSLInitialized());
   g_disable_ssl_init = true;
   return Status::OK();
 }
 
+void SetStandaloneInit(bool is_standalone) {
+  // No synchronization is necessary since this function is should be called
+  // only during static initialization phase.
+  g_is_standalone_init = is_standalone;
+}
+
 void InitializeOpenSSL() {
   static std::once_flag ssl_once;
   std::call_once(ssl_once, DoInitializeOpenSSL);
 }
 
+void FinalizeOpenSSL() {
+  static std::once_flag flag;
+  std::call_once(flag, DoFinalizeOpenSSL);
+}
+
+bool IsOpenSSLInitialized() {
+  return g_ssl_is_initialized;
+}
+
 string GetOpenSSLErrors() {
   ostringstream serr;
   uint32_t l;
diff --git a/src/kudu/util/openssl_util.h b/src/kudu/util/openssl_util.h
index 1b186f23e..f2a3cd9aa 100644
--- a/src/kudu/util/openssl_util.h
+++ b/src/kudu/util/openssl_util.h
@@ -95,12 +95,28 @@ using PasswordCallback = 
std::function<Status(std::string*)>;
 // any call to InitializeOpenSSL().
 Status DisableOpenSSLInitialization() WARN_UNUSED_RESULT;
 
+// Set whether the OpenSSL library is initialized in the context of a 
standalone
+// application. This should be called only once during static initialization.
+void SetStandaloneInit(bool is_standalone);
+
 // Initializes static state required by the OpenSSL library.
 // This is a no-op if DisableOpenSSLInitialization() has been called.
 //
 // Safe to call multiple times.
 void InitializeOpenSSL();
 
+// Clean up the OpenSSL's library global state. This function essentially calls
+// OPENSSL_cleanup() with OpenSSL 1.1.1 and newer versions. This function
+// should be called called only by a standalone application right after exiting
+// its main() function.
+void FinalizeOpenSSL();
+
+// Check if the Kudu's runtime has already initialized the OpenSSL library.
+// This function is not thread safe, and its only expected use cases are:
+//   ** calls from DisableOpenSSLInitialization()
+//   ** calls from global's initializers before main()
+bool IsOpenSSLInitialized();
+
 // Fetches errors from the OpenSSL error error queue, and stringifies them.
 //
 // The error queue will be empty after this method returns.
diff --git a/src/kudu/util/process_memory.cc b/src/kudu/util/process_memory.cc
index c379f9413..4df689b18 100644
--- a/src/kudu/util/process_memory.cc
+++ b/src/kudu/util/process_memory.cc
@@ -22,6 +22,7 @@
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
+#include <glog/raw_logging.h>
 #ifdef TCMALLOC_ENABLED
 #include <gperftools/malloc_extension.h>  // IWYU pragma: keep
 #endif
@@ -134,9 +135,9 @@ DEFINE_validator(tcmalloc_max_free_bytes_percentage, 
&ValidatePercentage);
 // ------------------------------------------------------------
 #ifdef TCMALLOC_ENABLED
 static int64_t GetTCMallocProperty(const char* prop) {
-  size_t value;
+  size_t value = 0;
   if (!MallocExtension::instance()->GetNumericProperty(prop, &value)) {
-    LOG(DFATAL) << "Failed to get tcmalloc property " << prop;
+    RAW_LOG(ERROR, "failed to get tcmalloc property '%s'; returning 0", prop);
   }
   return value;
 }
diff --git a/src/kudu/util/test_main.cc b/src/kudu/util/test_main.cc
index ccdc4188e..61662b16e 100644
--- a/src/kudu/util/test_main.cc
+++ b/src/kudu/util/test_main.cc
@@ -32,6 +32,12 @@
 #include "kudu/util/status.h"
 #include "kudu/util/test_util.h"
 
+#define KUDU_TEST_MAIN
+
+// Code for proper sequencing of tcmalloc and OpenSSL initialization/shutdown
+// that runs before/after main().
+#include "kudu/util/before_and_after_main.h"  // IWYU pragma: keep
+
 DEFINE_int32(test_timeout_after, 0,
              "Maximum total seconds allowed for all unit tests in the suite. 
Default: disabled");
 
diff --git a/thirdparty/build-definitions.sh b/thirdparty/build-definitions.sh
index b6f39e308..ee544ef67 100644
--- a/thirdparty/build-definitions.sh
+++ b/thirdparty/build-definitions.sh
@@ -513,6 +513,10 @@ build_gperftools() {
     $GPERFTOOLS_SOURCE/configure $cfg_options
   fixup_libtool
   make -j$PARALLEL $EXTRA_MAKEFLAGS install
+  # Add tcmalloc_guard.h because TCMallocGuard is useful for fine-grained
+  # control over the initialization of libtcmalloc internals, and that's
+  # helpful in the context of addressing KUDU-2439 and KUDU-3635.
+  install --backup=none --mode=0444 $GPERFTOOLS_SOURCE/src/tcmalloc_guard.h 
$PREFIX/include/gperftools
   popd
 }
 

Reply via email to