You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 9ad8734
Browse filesBrowse the repository at this point in the historyBrowse files
Never destroy module-wide singletons via static teardown (#897)
SoftHSM::instance, MutexFactory::instance, SecureMemoryRegistry::instance,
and OSSLCryptoFactory::instance/BotanCryptoFactory::instance were each a
class-level static std::unique_ptr<T> (or std::auto_ptr<T> pre-C++11).
Their destructors ran as part of the C++ runtime's ordinary static-object
teardown at process exit - an indeterminate time relative to unrelated
code's own cleanup.
Concretely: a PKCS#11 caller (e.g. an OpenSSL provider) that still holds
open sessions at process exit calls C_CloseSession() on each before
calling C_Finalize(), per the PKCS#11 spec. But SoftHSM::instance's own
destructor could fire first, via ordinary static teardown, independently
of and unordered with respect to that caller's explicit cleanup sequence.
Whichever teardown path lost the race operated on an already-destroyed
(or transparently reconstructed-blank, since SoftHSM::i() rebuilds a
fresh instance on next access) singleton, and its stale session-close
calls then dereferenced a NULL member on it - a real, reproducible
SIGSEGV when a pkcs11-provider-based caller runs openssl req against a
PKCS#11 key and exits normally.
This is the same category of bug already fixed once for
OSSLCryptoFactory's own OpenSSL ENGINE cleanup (issue #548, commit
c2cc065), just never applied to these singletons' own top-level
lifecycle - and the gap resurfaces one level down too: OSSLCryptoFactory
can be lazily constructed for the first time from deep inside
SoftHSM::C_Finalize()'s own teardown chain, touching ENGINE_by_id() after
OpenSSL's own OPENSSL_cleanup() has already run.
Each singleton's std::unique_ptr<T>/std::auto_ptr<T> is now heap-allocated
once and deliberately never freed, so nothing destroys the T it owns
except that class's own explicit reset()/C_Finalize() path - never
ordinary static teardown. The smart pointer itself is retained as the
owning type (matching the "Static and Global Variables" guidance in the
Google C++ Style Guide, and the same "leaky singleton" idiom used by
e.g. Drake's never_destroyed<T>), so reset()'s exception-safety and
ownership semantics for the owned object are unchanged.
0 commit comments