Skip to content

Commit aa9f7db

Browse files
daverigbytrondn
authored andcommitted
Fix static initialization order fiasco in audit / connections
When running memcached_testapp under TSan (macOS), an exception is thrown attempting to lock an invalid mutex: libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument With the following backtrace: (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 * frame #0: 0x00007fff6585e0f8 libc++abi.dylib` __cxa_throw frame #1: 0x00007fff6583855b libc++.1.dylib` std::__1::__throw_system_error(int, char const*) + 77 frame #2: 0x00007fff6582f54d libc++.1.dylib` std::__1::mutex::lock() + 29 frame #3: 0x00000001014139f1 memcached_testapp` std::__1::unique_lock<std::__1::mutex>::unique_lock(this=0x00007ffeefbff3e8, __m=<unavailable>) + 65 at __mutex_base:130 frame #4: 0x0000000101410811 memcached_testapp` std::__1::unique_lock<std::__1::mutex>::unique_lock(this=<unavailable>, __m=<unavailable>) + 33 at __mutex_base:130 frame #5: 0x000000010141053e memcached_testapp` folly::shared_mutex_detail::annotationGuard(ptr=<unavailable>) + 110 at SharedMutex.cpp:33 frame #6: 0x0000000101410445 memcached_testapp` folly::SharedMutexImpl<...>::annotateLazyCreate(this=0x0000000101984640) + 53 at SharedMutex.h:719 frame #7: 0x000000010140f6ca memcached_testapp` folly::SharedMutexImpl<...>::annotateDestroy(this=0x0000000101984640) + 26 at SharedMutex.h:732 frame #8: 0x000000010140f5af memcached_testapp` folly::SharedMutexImpl<...>::~SharedMutexImpl(this=0x0000000101984640) + 63 at SharedMutex.h:338 frame #9: 0x000000010140f71a memcached_testapp` folly::SharedMutexImpl<...>::~SharedMutexImpl(this=<unavailable>) + 26 at SharedMutex.h:312 frame #10: 0x0000000100d5549a memcached_testapp` folly::Synchronized<std::__1::unique_ptr<cb::audit::Audit, ...>, folly::SharedMutexImpl<...> >::~Synchronized(this=0x0000000101984638) + 58 at Synchronized.h:489 frame #11: 0x0000000100d511a9 memcached_testapp` folly::Synchronized<std::__1::unique_ptr<cb::audit::Audit, ...>, folly::SharedMutexImpl<...> >::~Synchronized(this=0x0000000101984638) + 41 at Synchronized.h:489 frame #12: 0x000000010ad4c721 libclang_rt.tsan_osx_dynamic.dylib` cxa_at_exit_wrapper(void*) + 33 frame #13: 0x00007fff685d813c libsystem_c.dylib` __cxa_finalize_ranges + 319 frame #14: 0x00007fff685d8412 libsystem_c.dylib` exit + 55 frame #15: 0x00007fff6852ecd0 libdyld.dylib` start + 8 The crash is caused when destructing the static variable `auditHandle`, the Synchronized dtor attempts to access another static variable (kAnnotationMutexes) which has already been destructed. The problem is that auditHandle is a static at file scope, so the order it it initiailised (and destructed) relative to other statics is undefined. Switch to using a C++11 "magic static" via a getAuditHandle() function, which defers construction until the type is used. The same problem exists for `connections`, fix in the same way. Change-Id: I58c925f2c71907ab22581bf462ce3c7d092cfefa Reviewed-on: http://review.couchbase.org/c/kv_engine/+/138051 Tested-by: Build Bot <[email protected]> Reviewed-by: Trond Norbye <[email protected]>
1 parent a235230 commit aa9f7db

File tree

2 files changed

+18
-11
lines changed

2 files changed

+18
-11
lines changed

daemon/connections.cc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929

3030
/// List of all of the connections created in the system (so that we can
3131
/// iterate over them)
32-
folly::Synchronized<std::deque<Connection*>> connections;
32+
folly::Synchronized<std::deque<Connection*>>& getConnections() {
33+
static folly::Synchronized<std::deque<Connection*>> connections;
34+
return connections;
35+
}
3336

3437
int signal_idle_clients(FrontEndThread& me, bool dumpConnection) {
3538
int connected = 0;
@@ -50,7 +53,7 @@ void iterate_thread_connections(FrontEndThread* thread,
5053
std::function<void(Connection&)> callback) {
5154
// Deny modifications to the connection map while we're iterating
5255
// over it
53-
auto locked = connections.rlock();
56+
auto locked = getConnections().rlock();
5457
for (auto* c : *locked) {
5558
if (&c->getThread() == thread) {
5659
callback(*c);
@@ -67,7 +70,7 @@ Connection* conn_new(SOCKET sfd,
6770

6871
try {
6972
ret = std::make_unique<Connection>(sfd, base, interface, thread);
70-
connections.wlock()->push_back(ret.get());
73+
getConnections().wlock()->push_back(ret.get());
7174
c = ret.release();
7275
stats.total_conns++;
7376
} catch (const std::bad_alloc&) {
@@ -100,7 +103,7 @@ Connection* conn_new(SOCKET sfd,
100103
* and freeing the Connection object.
101104
*/
102105
void conn_destroy(Connection* c) {
103-
connections.withWLock([c](auto& conns) {
106+
getConnections().withWLock([c](auto& conns) {
104107
auto iter = std::find(conns.begin(), conns.end(), c);
105108
// The connection should be one I know about
106109
cb_assert(iter != conns.end());

daemon/mcaudit.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@
3636

3737
#include <sstream>
3838

39-
static folly::Synchronized<cb::audit::UniqueAuditPtr> auditHandle;
39+
/// @returns the singleton audit handle.
40+
folly::Synchronized<cb::audit::UniqueAuditPtr>& getAuditHandle() {
41+
static folly::Synchronized<cb::audit::UniqueAuditPtr> handle;
42+
return handle;
43+
}
4044

4145
static std::atomic_bool audit_enabled{false};
4246

@@ -117,7 +121,7 @@ static void do_audit(uint32_t id,
117121
const nlohmann::json& event,
118122
const char* warn) {
119123
auto text = event.dump();
120-
auditHandle.withRLock([id, warn, &text](auto& handle) {
124+
getAuditHandle().withRLock([id, warn, &text](auto& handle) {
121125
if (handle) {
122126
if (!handle->put_event(id, text)) {
123127
LOG_WARNING("{}: {}", warn, text);
@@ -272,7 +276,7 @@ bool mc_audit_event(uint32_t audit_eventid, cb::const_byte_buffer payload) {
272276

273277
std::string_view buffer{reinterpret_cast<const char*>(payload.data()),
274278
payload.size()};
275-
return auditHandle.withRLock([audit_eventid, buffer](auto& handle) {
279+
return getAuditHandle().withRLock([audit_eventid, buffer](auto& handle) {
276280
if (!handle) {
277281
return false;
278282
}
@@ -372,15 +376,15 @@ void initialize_audit() {
372376
}
373377
audit->add_event_state_listener(event_state_listener);
374378
audit->notify_all_event_states();
375-
*auditHandle.wlock() = std::move(audit);
379+
*getAuditHandle().wlock() = std::move(audit);
376380
}
377381

378382
void shutdown_audit() {
379-
auditHandle.wlock()->reset();
383+
getAuditHandle().wlock()->reset();
380384
}
381385

382386
ENGINE_ERROR_CODE reconfigure_audit(Cookie& cookie) {
383-
return auditHandle.withRLock([&cookie](auto& handle) {
387+
return getAuditHandle().withRLock([&cookie](auto& handle) {
384388
if (!handle) {
385389
return ENGINE_FAILED;
386390
}
@@ -393,7 +397,7 @@ ENGINE_ERROR_CODE reconfigure_audit(Cookie& cookie) {
393397
}
394398

395399
void stats_audit(StatCollector& collector) {
396-
auditHandle.withRLock([&collector](auto& handle) {
400+
getAuditHandle().withRLock([&collector](auto& handle) {
397401
if (handle) {
398402
handle->stats(collector);
399403
}

0 commit comments

Comments
 (0)