blob: ed949efc3c047f88736162a8f3137cbba1ff661f [file] [edit]
/*****************************************************************************\
* openssl_helper.c
*****************************************************************************
* Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
*
* This file is part of Slurm, a resource management program.
* For details, see <https://slurm.schedmd.com/>.
* Please also read the included file: DISCLAIMER.
*
* Slurm is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* Slurm is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along
* with Slurm; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\*****************************************************************************/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <pthread.h>
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/openssl_helper.h"
strong_alias(openssl_helper_disable_atexit,
slurm_openssl_helper_disable_atexit);
/*
* This helper disables OpenSSL's atexit() so that OPENSSL_cleanup() is not
* called while another thread is using OpenSSL, potentially causing a segfault.
*
* A deliberate consequence is that libcrypto global state is never freed, and
* may show up as leaked memory in valgrind.
*/
/*
* Defined locally to eliminate build dependency on <openssl/crypto.h>. Whether
* atexit cleanup needs disabling is a property of the runtime libcrypto, not
* the build host. The ABI has been stable since OpenSSL 1.1.0.
*/
#define SLURM_OPENSSL_INIT_NO_ATEXIT 0x00080000UL
/*
* Multiple plugins call this from their own init(), potentially concurrently,
* and may each resolve to a different libcrypto. Skipping a repeat of the last
* handled OPENSSL_init_crypto() avoids redundant work in the common case where
* they all resolve to the same one. This is only an optimization since repeated
* calls for the same libcrypto are harmless.
*/
static pthread_mutex_t disable_atexit_mutex = PTHREAD_MUTEX_INITIALIZER;
static int (*disabled_init_crypto)(uint64_t opts, const void *settings) = NULL;
extern void openssl_helper_disable_atexit(const void *plugin_addr)
{
Dl_info info = { 0 };
void *handle = NULL;
unsigned long (*version_num)(void) = NULL;
int (*init_crypto)(uint64_t opts, const void *settings) = NULL;
/*
* Resolve the caller's shared object from an address it owns, then get a
* handle to it. Plugins are dlopen()ed with RTLD_LOCAL, so their
* libcrypto dependency is not in the global scope and RTLD_DEFAULT can't
* see it. A handle to the caller's own object searches its dependency
* tree, where libcrypto is resolvable.
*/
if (!dladdr(plugin_addr, &info) || !info.dli_fname) {
debug2("%s: dladdr() could not resolve the caller's object, skipping disable of OpenSSL atexit cleanup.",
__func__);
return;
}
/*
* RTLD_NOLOAD returns a handle to the already-loaded object without
* reloading it, and increments its reference count, so dlclose() it
* below.
*/
handle = dlopen(info.dli_fname, RTLD_NOLOAD | RTLD_LAZY);
if (!handle) {
debug2("%s: dlopen(%s, RTLD_NOLOAD) failed, skipping disable of OpenSSL atexit cleanup: %s",
__func__, info.dli_fname, dlerror());
return;
}
/*
* Only OpenSSL 1.1.0 - 3.x install the handler by default, so skip 4.0+
* and any libcrypto whose version can't be confirmed.
*
* This may also fail if the object does not depend on OpenSSL's
* libcrypto and thus dlsym() cannot resolve the symbol address. In that
* case there is no atexit() handler to disable, and this is an
* intentional no-op.
*/
(void) dlerror();
version_num = dlsym(handle, "OpenSSL_version_num");
if (!version_num) {
debug2("%s: OpenSSL_version_num unavailable, skipping disable of OpenSSL atexit cleanup: %s",
__func__, dlerror());
goto done;
}
/* Major version is the top nibble of the packed version number. */
if (((version_num() >> 28) & 0xf) >= 4) {
debug2("%s: OpenSSL >= 4.0 does not install atexit cleanup, skipping disable of OpenSSL atexit cleanup.",
__func__);
goto done;
}
(void) dlerror();
init_crypto = dlsym(handle, "OPENSSL_init_crypto");
if (!init_crypto) {
debug2("%s: OPENSSL_init_crypto unavailable, unable to disable OpenSSL atexit cleanup: %s",
__func__, dlerror());
goto done;
}
slurm_mutex_lock(&disable_atexit_mutex);
if (init_crypto == disabled_init_crypto) {
debug2("%s: OpenSSL atexit cleanup already disabled for this libcrypto",
__func__);
} else if (init_crypto(SLURM_OPENSSL_INIT_NO_ATEXIT, NULL) != 1) {
debug2("%s: OPENSSL_init_crypto() failed, unable to disable OpenSSL atexit cleanup.", __func__);
} else {
disabled_init_crypto = init_crypto;
debug2("%s: disabled OpenSSL atexit cleanup", __func__);
}
slurm_mutex_unlock(&disable_atexit_mutex);
done:
dlclose(handle);
}