mumble-voip_mumble/src/SSLLocks.cpp

89 lines
2.6 KiB
C++

// Copyright 2017-2023 The Mumble Developers. All rights reserved.
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file at the root of the
// Mumble source tree or at <https://www.mumble.info/LICENSE>.
#include "SSLLocks.h"
#include <QtCore/QMutex>
#include <QtCore/QThread>
#include <openssl/crypto.h>
static QMutex **locks = nullptr;
void locking_callback(int mode, int type, const char *, int) {
if (mode & CRYPTO_LOCK) {
locks[type]->lock();
} else {
locks[type]->unlock();
}
}
unsigned long id_callback() {
// OpenSSL's thread ID callback must return an unsigned long.
// It's therefore important that we make sure the value from
// Qt's QThread::currentThreadId actually makes sense to return
// in this case.
//
// On Windows, the return type is a DWORD. This is excellent,
// because on an LLP64 like 64-bit Windows, unsigned long is
// only 32-bits wide, just like DWORD.
//
// On macOS and embedded Linux, it's void *. And when Qt targets
// X11, it's unsigned long.
//
// So, in all cases, it's safe to use unsigned long.
//
// The trouble is, since the return type of QThread::currentThreadId()
// differs between platforms, we have to be clever about how we convert
// that type to an unsigned long. The way we do it here is to first convert
// it to an uintptr_t, since both pointer types and integer types will
// convert to that without the compiler complaining. Then, we convert
// *that* to an unsigned long. (Note that for LLP64 platforms, such as
// 64-bit Windows, this conversion is from a 64-bit type to a 32-bit type)
uintptr_t val = reinterpret_cast< uintptr_t >(QThread::currentThreadId());
return static_cast< unsigned long >(val);
}
void SSLLocks::initialize() {
unsigned int nlocks = CRYPTO_num_locks();
locks = reinterpret_cast< QMutex ** >(calloc(nlocks, sizeof(void *)));
if (!locks) {
qFatal("SSLLocks: unable to allocate locks array");
// This initializer is called early during program
// execution, and the message handler that causes
// qFatal to terminate execution might not be registered
// yet. So, do exit(1) just in case.
exit(1);
}
for (unsigned int i = 0; i < nlocks; i++) {
locks[i] = new QMutex;
}
CRYPTO_set_locking_callback(locking_callback);
CRYPTO_set_id_callback(id_callback);
}
void SSLLocks::destroy() {
// If SSLLocks was never initialized, or has been destroyed already,
// don't try to do it again.
if (!locks) {
return;
}
CRYPTO_set_locking_callback(nullptr);
CRYPTO_set_id_callback(nullptr);
int nlocks = CRYPTO_num_locks();
for (int i = 0; i < nlocks; i++) {
delete locks[i];
}
free(locks);
locks = nullptr;
}