Skip to content

Commit 9ad8734

Browse 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.
1 parent 884cb38 commit 9ad8734

17 files changed

Lines changed: 132 additions & 98 deletions

‎src/bin/util/softhsm2-util.cpp‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,22 +74,22 @@
7474

7575
#ifdef HAVE_CXX11
7676

77-
std::unique_ptr<MutexFactory> MutexFactory::instance(nullptr);
78-
std::unique_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(nullptr);
77+
std::unique_ptr<MutexFactory>* MutexFactory::instance = new std::unique_ptr<MutexFactory>();
78+
std::unique_ptr<SecureMemoryRegistry>* SecureMemoryRegistry::instance = new std::unique_ptr<SecureMemoryRegistry>();
7979
#if defined(WITH_OPENSSL)
80-
std::unique_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(nullptr);
80+
std::unique_ptr<OSSLCryptoFactory>* OSSLCryptoFactory::instance = new std::unique_ptr<OSSLCryptoFactory>();
8181
#else
82-
std::unique_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(nullptr);
82+
std::unique_ptr<BotanCryptoFactory>* BotanCryptoFactory::instance = new std::unique_ptr<BotanCryptoFactory>();
8383
#endif
8484

8585
#else
8686

87-
std::auto_ptr<MutexFactory> MutexFactory::instance(NULL);
88-
std::auto_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(NULL);
87+
std::auto_ptr<MutexFactory>* MutexFactory::instance = new std::auto_ptr<MutexFactory>();
88+
std::auto_ptr<SecureMemoryRegistry>* SecureMemoryRegistry::instance = new std::auto_ptr<SecureMemoryRegistry>();
8989
#if defined(WITH_OPENSSL)
90-
std::auto_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(NULL);
90+
std::auto_ptr<OSSLCryptoFactory>* OSSLCryptoFactory::instance = new std::auto_ptr<OSSLCryptoFactory>();
9191
#else
92-
std::auto_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(NULL);
92+
std::auto_ptr<BotanCryptoFactory>* BotanCryptoFactory::instance = new std::auto_ptr<BotanCryptoFactory>();
9393
#endif
9494

9595
#endif

‎src/lib/SoftHSM.cpp‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -105,25 +105,25 @@
105105

106106
#ifdef HAVE_CXX11
107107

108-
std::unique_ptr<MutexFactory> MutexFactory::instance(nullptr);
109-
std::unique_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(nullptr);
108+
std::unique_ptr<MutexFactory>* MutexFactory::instance = new std::unique_ptr<MutexFactory>();
109+
std::unique_ptr<SecureMemoryRegistry>* SecureMemoryRegistry::instance = new std::unique_ptr<SecureMemoryRegistry>();
110110
#if defined(WITH_OPENSSL)
111-
std::unique_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(nullptr);
111+
std::unique_ptr<OSSLCryptoFactory>* OSSLCryptoFactory::instance = new std::unique_ptr<OSSLCryptoFactory>();
112112
#else
113-
std::unique_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(nullptr);
113+
std::unique_ptr<BotanCryptoFactory>* BotanCryptoFactory::instance = new std::unique_ptr<BotanCryptoFactory>();
114114
#endif
115-
std::unique_ptr<SoftHSM> SoftHSM::instance(nullptr);
115+
std::unique_ptr<SoftHSM>* SoftHSM::instance = new std::unique_ptr<SoftHSM>();
116116

117117
#else
118118

119-
std::auto_ptr<MutexFactory> MutexFactory::instance(NULL);
120-
std::auto_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(NULL);
119+
std::auto_ptr<MutexFactory>* MutexFactory::instance = new std::auto_ptr<MutexFactory>();
120+
std::auto_ptr<SecureMemoryRegistry>* SecureMemoryRegistry::instance = new std::auto_ptr<SecureMemoryRegistry>();
121121
#if defined(WITH_OPENSSL)
122-
std::auto_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(NULL);
122+
std::auto_ptr<OSSLCryptoFactory>* OSSLCryptoFactory::instance = new std::auto_ptr<OSSLCryptoFactory>();
123123
#else
124-
std::auto_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(NULL);
124+
std::auto_ptr<BotanCryptoFactory>* BotanCryptoFactory::instance = new std::auto_ptr<BotanCryptoFactory>();
125125
#endif
126-
std::auto_ptr<SoftHSM> SoftHSM::instance(NULL);
126+
std::auto_ptr<SoftHSM>* SoftHSM::instance = new std::auto_ptr<SoftHSM>();
127127

128128
#endif
129129

@@ -415,11 +415,11 @@ static void resetMutexFactoryCallbacks()
415415
// Return the one-and-only instance
416416
SoftHSM* SoftHSM::i()
417417
{
418-
if (!instance.get())
418+
if (!instance->get())
419419
{
420-
instance.reset(new SoftHSM());
420+
instance->reset(new SoftHSM());
421421
}
422-
else if(instance->detectFork())
422+
else if((*instance)->detectFork())
423423
{
424424
if (Configuration::i()->getBool("library.reset_on_fork", false))
425425
{
@@ -428,18 +428,18 @@ SoftHSM* SoftHSM::i()
428428
* the old instance is first destroyed as some
429429
* static structures are erased in the destructor.
430430
*/
431-
instance.reset(NULL);
432-
instance.reset(new SoftHSM());
431+
instance->reset(NULL);
432+
instance->reset(new SoftHSM());
433433
}
434434
}
435435

436-
return instance.get();
436+
return instance->get();
437437
}
438438

439439
void SoftHSM::reset()
440440
{
441-
if (instance.get())
442-
instance.reset();
441+
if (instance->get())
442+
instance->reset();
443443
}
444444

445445
// Constructor

‎src/lib/SoftHSM.h‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,30 @@ class SoftHSM
201201
// Constructor
202202
SoftHSM();
203203

204-
// The one-and-only instance
204+
// The one-and-only instance.
205+
//
206+
// Smart pointers must not be used directly as class-level
207+
// static storage: their destructor runs as part of the C++
208+
// runtime's ordinary static-object teardown at process exit, an
209+
// indeterminate time relative to unrelated code's own cleanup
210+
// (see the "Static and Global Variables" section of the Google
211+
// C++ Style Guide, which explicitly recommends "a pointer that
212+
// is never freed" for exactly this situation). Concretely here:
213+
// a PKCS#11 caller (e.g. a provider) may still be closing its
214+
// own open sessions via C_CloseSession() when this destructor
215+
// fires, and would then be operating on an already-destroyed
216+
// singleton.
217+
//
218+
// The smart pointer itself is therefore heap-allocated once and
219+
// deliberately never freed - the same "leaky singleton" idiom
220+
// used by e.g. Drake's never_destroyed<T> - while still using it
221+
// (not a raw pointer) to own and manage the SoftHSM object's own
222+
// lifetime, which is destroyed exactly once, only via the
223+
// explicit C_Finalize()/reset() path below.
205224
#ifdef HAVE_CXX11
206-
static std::unique_ptr<SoftHSM> instance;
225+
static std::unique_ptr<SoftHSM>* instance;
207226
#else
208-
static std::auto_ptr<SoftHSM> instance;
227+
static std::auto_ptr<SoftHSM>* instance;
209228
#endif
210229

211230
// Is the SoftHSM PKCS #11 library initialised?

‎src/lib/common/MutexFactory.cpp‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ MutexFactory::~MutexFactory()
111111
// Return the one-and-only instance
112112
MutexFactory* MutexFactory::i()
113113
{
114-
if (!instance.get())
114+
if (!instance->get())
115115
{
116-
instance.reset(new MutexFactory());
116+
instance->reset(new MutexFactory());
117117
}
118118

119-
return instance.get();
119+
return instance->get();
120120
}
121121

122122
// Get a mutex instance

‎src/lib/common/MutexFactory.h‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,15 @@ class MutexFactory
112112
CK_RV LockMutex(CK_VOID_PTR mutex);
113113
CK_RV UnlockMutex(CK_VOID_PTR mutex);
114114

115-
// The one-and-only instance
115+
// The one-and-only instance.
116+
//
117+
// A pointer to a heap-allocated, deliberately never-freed smart
118+
// pointer, not a smart pointer directly: see the rationale
119+
// beside SoftHSM::instance in SoftHSM.h.
116120
#ifdef HAVE_CXX11
117-
static std::unique_ptr<MutexFactory> instance;
121+
static std::unique_ptr<MutexFactory>* instance;
118122
#else
119-
static std::auto_ptr<MutexFactory> instance;
123+
static std::auto_ptr<MutexFactory>* instance;
120124
#endif
121125

122126
// The function pointers

‎src/lib/crypto/BotanCryptoFactory.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,18 @@ BotanCryptoFactory::~BotanCryptoFactory()
9595
// Return the one-and-only instance
9696
BotanCryptoFactory* BotanCryptoFactory::i()
9797
{
98-
if (!instance.get())
98+
if (!instance->get())
9999
{
100-
instance.reset(new BotanCryptoFactory());
100+
instance->reset(new BotanCryptoFactory());
101101
}
102102

103-
return instance.get();
103+
return instance->get();
104104
}
105105

106106
// This will destroy the one-and-only instance.
107107
void BotanCryptoFactory::reset()
108108
{
109-
instance.reset();
109+
instance->reset();
110110
}
111111

112112
// Create a concrete instance of a symmetric algorithm

‎src/lib/crypto/BotanCryptoFactory.h‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,15 @@ class BotanCryptoFactory : public CryptoFactory
8181
// Constructor
8282
BotanCryptoFactory();
8383

84-
// The one-and-only instance
84+
// The one-and-only instance.
85+
//
86+
// A pointer to a heap-allocated, deliberately never-freed smart
87+
// pointer, not a smart pointer directly: see the rationale
88+
// beside SoftHSM::instance in SoftHSM.h.
8589
#ifdef HAVE_CXX11
86-
static std::unique_ptr<BotanCryptoFactory> instance;
90+
static std::unique_ptr<BotanCryptoFactory>* instance;
8791
#else
88-
static std::auto_ptr<BotanCryptoFactory> instance;
92+
static std::auto_ptr<BotanCryptoFactory>* instance;
8993
#endif
9094

9195
// Thread specific RNG

‎src/lib/crypto/OSSLCryptoFactory.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,18 +300,18 @@ OSSLCryptoFactory::~OSSLCryptoFactory()
300300
// Return the one-and-only instance
301301
OSSLCryptoFactory* OSSLCryptoFactory::i()
302302
{
303-
if (!instance.get())
303+
if (!instance->get())
304304
{
305-
instance.reset(new OSSLCryptoFactory());
305+
instance->reset(new OSSLCryptoFactory());
306306
}
307307

308-
return instance.get();
308+
return instance->get();
309309
}
310310

311311
// This will destroy the one-and-only instance.
312312
void OSSLCryptoFactory::reset()
313313
{
314-
instance.reset();
314+
instance->reset();
315315
}
316316

317317
#ifdef WITH_FIPS

‎src/lib/crypto/OSSLCryptoFactory.h‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,15 @@ class OSSLCryptoFactory : public CryptoFactory
8989
// Constructor
9090
OSSLCryptoFactory();
9191

92-
// The one-and-only instance
92+
// The one-and-only instance.
93+
//
94+
// A pointer to a heap-allocated, deliberately never-freed smart
95+
// pointer, not a smart pointer directly: see the rationale
96+
// beside SoftHSM::instance in SoftHSM.h.
9397
#ifdef HAVE_CXX11
94-
static std::unique_ptr<OSSLCryptoFactory> instance;
98+
static std::unique_ptr<OSSLCryptoFactory>* instance;
9599
#else
96-
static std::auto_ptr<OSSLCryptoFactory> instance;
100+
static std::auto_ptr<OSSLCryptoFactory>* instance;
97101
#endif
98102

99103
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)

‎src/lib/crypto/test/cryptotest.cpp‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,22 +56,22 @@
5656
// Initialise the one-and-only instance
5757
#ifdef HAVE_CXX11
5858

59-
std::unique_ptr<MutexFactory> MutexFactory::instance(nullptr);
60-
std::unique_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(nullptr);
59+
std::unique_ptr<MutexFactory>* MutexFactory::instance = new std::unique_ptr<MutexFactory>();
60+
std::unique_ptr<SecureMemoryRegistry>* SecureMemoryRegistry::instance = new std::unique_ptr<SecureMemoryRegistry>();
6161
#if defined(WITH_OPENSSL)
62-
std::unique_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(nullptr);
62+
std::unique_ptr<OSSLCryptoFactory>* OSSLCryptoFactory::instance = new std::unique_ptr<OSSLCryptoFactory>();
6363
#else
64-
std::unique_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(nullptr);
64+
std::unique_ptr<BotanCryptoFactory>* BotanCryptoFactory::instance = new std::unique_ptr<BotanCryptoFactory>();
6565
#endif
6666

6767
#else
6868

69-
std::auto_ptr<MutexFactory> MutexFactory::instance(NULL);
70-
std::auto_ptr<SecureMemoryRegistry> SecureMemoryRegistry::instance(NULL);
69+
std::auto_ptr<MutexFactory>* MutexFactory::instance = new std::auto_ptr<MutexFactory>();
70+
std::auto_ptr<SecureMemoryRegistry>* SecureMemoryRegistry::instance = new std::auto_ptr<SecureMemoryRegistry>();
7171
#if defined(WITH_OPENSSL)
72-
std::auto_ptr<OSSLCryptoFactory> OSSLCryptoFactory::instance(NULL);
72+
std::auto_ptr<OSSLCryptoFactory>* OSSLCryptoFactory::instance = new std::auto_ptr<OSSLCryptoFactory>();
7373
#else
74-
std::auto_ptr<BotanCryptoFactory> BotanCryptoFactory::instance(NULL);
74+
std::auto_ptr<BotanCryptoFactory>* BotanCryptoFactory::instance = new std::auto_ptr<BotanCryptoFactory>();
7575
#endif
7676

7777
#endif

0 commit comments

Comments
 (0)