mirror of
https://github.com/zeromq/libzmq.git
synced 2025-11-07 05:58:45 +01:00
Problem: fd leak in tweetnacl with one ctx per thread
Solution: add a crypto [de-]initialiser, refcounted and serialised through critical sections. This is necessary as utility APIs such as zmq_curve_keypair also call into the sodium/tweetnacl libraries and need the initialisation outside of the zmq context. Also the libsodium documentation explicitly says that sodium_init must not be called concurrently from multiple threads, which could have happened until now. Also the randombytes_close function does not appear to be thread safe either. This change guarantees that the library is initialised only once at any given time across the whole program. Fixes #2632
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright (c) 2007-2016 Contributors as noted in the AUTHORS file
|
||||
Copyright (c) 2007-2017 Contributors as noted in the AUTHORS file
|
||||
|
||||
This file is part of libzmq, the ZeroMQ core engine in C++.
|
||||
|
||||
@@ -37,6 +37,14 @@
|
||||
#include "random.hpp"
|
||||
#include "stdint.hpp"
|
||||
#include "clock.hpp"
|
||||
#include "mutex.hpp"
|
||||
#include "macros.hpp"
|
||||
|
||||
#if defined (ZMQ_USE_TWEETNACL)
|
||||
#include "tweetnacl.h"
|
||||
#elif defined (ZMQ_USE_LIBSODIUM)
|
||||
#include "sodium.h"
|
||||
#endif
|
||||
|
||||
void zmq::seed_random ()
|
||||
{
|
||||
@@ -57,3 +65,54 @@ uint32_t zmq::generate_random ()
|
||||
return high | low;
|
||||
}
|
||||
|
||||
// When different threads have their own context the file descriptor
|
||||
// variable is shared and is subject to race conditions in tweetnacl,
|
||||
// that lead to file descriptors leaks. In long-running programs with
|
||||
// ephemeral threads this is a problem as it accumulates.
|
||||
// thread-local storage cannot be used to initialise the file descriptor
|
||||
// as it is perfectly legal to share a context among many threads, each
|
||||
// of which might call curve APIs.
|
||||
// Also libsodium documentation specifically states that sodium_init
|
||||
// must not be called concurrently from multiple threads, for the
|
||||
// same reason. Inspecting the code also reveals that the close API is
|
||||
// not thread safe.
|
||||
// The context class cannot be used with static variables as the curve
|
||||
// utility APIs like zmq_curve_keypair also call into the crypto
|
||||
// library.
|
||||
// The safest solution for all use cases therefore is to have a global,
|
||||
// static lock to serialize calls into an initialiser and a finaliser,
|
||||
// using refcounts to make sure that a thread does not close the library
|
||||
// while another is still using it.
|
||||
static unsigned int random_refcount = 0;
|
||||
static zmq::mutex_t random_sync;
|
||||
|
||||
void zmq::random_open (void)
|
||||
{
|
||||
#if defined (ZMQ_USE_LIBSODIUM) || \
|
||||
(defined (ZMQ_USE_TWEETNACL) && !defined (ZMQ_HAVE_WINDOWS))
|
||||
scoped_lock_t locker (random_sync);
|
||||
|
||||
if (random_refcount == 0) {
|
||||
int rc = sodium_init ();
|
||||
zmq_assert (rc != -1);
|
||||
}
|
||||
|
||||
++random_refcount;
|
||||
#else
|
||||
LIBZMQ_UNUSED (random_refcount);
|
||||
#endif
|
||||
}
|
||||
|
||||
void zmq::random_close (void)
|
||||
{
|
||||
#if defined (ZMQ_USE_LIBSODIUM) || \
|
||||
(defined (ZMQ_USE_TWEETNACL) && !defined (ZMQ_HAVE_WINDOWS))
|
||||
scoped_lock_t locker (random_sync);
|
||||
--random_refcount;
|
||||
|
||||
if (random_refcount == 0) {
|
||||
randombytes_close ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user