QCryptographicHash: add a mutex to writing to the results
QCryptographicHash::result() and resultView() are const, therefore two threads can call them on the same object. Given that the finalization of the hash is not a trivial operation but doesn't modify the state, let's do it without a locked mutex, onto a temporary stack buffer. Then we lock the mutex to copy said result to our cached value. Pick-to: 6.5 6.4 6.2 5.15 Fixes: QTBUG-110058 Change-Id: Ide4dbd0777a44ed0870efffd17394bf72785eabb Reviewed-by: Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
2d7a27b918
commit
ccad719d2e
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <qcryptographichash.h>
|
#include <qcryptographichash.h>
|
||||||
#include <qiodevice.h>
|
#include <qiodevice.h>
|
||||||
|
#include <qmutex.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
@ -255,16 +256,7 @@ public:
|
|||||||
blake2s_state blake2sContext;
|
blake2s_state blake2sContext;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
|
||||||
#ifndef USING_OPENSSL30
|
|
||||||
enum class Sha3Variant
|
|
||||||
{
|
|
||||||
Sha3,
|
|
||||||
Keccak
|
|
||||||
};
|
|
||||||
void sha3Finish(int bitCount, Sha3Variant sha3Variant);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
class SmallByteArray {
|
class SmallByteArray {
|
||||||
std::array<quint8, MaxHashLength> m_data;
|
std::array<quint8, MaxHashLength> m_data;
|
||||||
static_assert(MaxHashLength <= std::numeric_limits<std::uint8_t>::max());
|
static_assert(MaxHashLength <= std::numeric_limits<std::uint8_t>::max());
|
||||||
@ -282,12 +274,25 @@ public:
|
|||||||
QByteArrayView toByteArrayView() const noexcept
|
QByteArrayView toByteArrayView() const noexcept
|
||||||
{ return QByteArrayView{m_data.data(), size()}; }
|
{ return QByteArrayView{m_data.data(), size()}; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only write to the result under this mutex
|
||||||
|
QBasicMutex finalizeMutex;
|
||||||
SmallByteArray result;
|
SmallByteArray result;
|
||||||
|
|
||||||
|
#if !defined(QT_CRYPTOGRAPHICHASH_ONLY_SHA1) && !defined(USING_OPENSSL30)
|
||||||
|
enum class Sha3Variant
|
||||||
|
{
|
||||||
|
Sha3,
|
||||||
|
Keccak
|
||||||
|
};
|
||||||
|
void sha3Finish(SmallByteArray *tmpresult, int bitCount, Sha3Variant sha3Variant);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
||||||
#ifndef USING_OPENSSL30
|
#ifndef USING_OPENSSL30
|
||||||
void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant)
|
void QCryptographicHashPrivate::sha3Finish(SmallByteArray *tmpresult, int bitCount,
|
||||||
|
Sha3Variant sha3Variant)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
FIPS 202 §6.1 defines SHA-3 in terms of calculating the Keccak function
|
FIPS 202 §6.1 defines SHA-3 in terms of calculating the Keccak function
|
||||||
@ -311,7 +316,7 @@ void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant
|
|||||||
*/
|
*/
|
||||||
static const unsigned char sha3FinalSuffix = 0x80;
|
static const unsigned char sha3FinalSuffix = 0x80;
|
||||||
|
|
||||||
result.resizeForOverwrite(bitCount / 8);
|
tmpresult->resizeForOverwrite(bitCount / 8);
|
||||||
|
|
||||||
SHA3Context copy = sha3Context;
|
SHA3Context copy = sha3Context;
|
||||||
|
|
||||||
@ -323,7 +328,7 @@ void QCryptographicHashPrivate::sha3Finish(int bitCount, Sha3Variant sha3Variant
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sha3Final(©, reinterpret_cast<BitSequence *>(result.data()));
|
sha3Final(©, tmpresult->data());
|
||||||
}
|
}
|
||||||
#endif // !QT_CONFIG(opensslv30)
|
#endif // !QT_CONFIG(opensslv30)
|
||||||
#endif
|
#endif
|
||||||
@ -730,33 +735,34 @@ void QCryptographicHashPrivate::finalize() noexcept
|
|||||||
if (!result.isEmpty())
|
if (!result.isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
SmallByteArray tmpresult;
|
||||||
#ifdef USING_OPENSSL30
|
#ifdef USING_OPENSSL30
|
||||||
if (method == QCryptographicHash::Blake2b_160 ||
|
if (method == QCryptographicHash::Blake2b_160 ||
|
||||||
method == QCryptographicHash::Blake2b_256 ||
|
method == QCryptographicHash::Blake2b_256 ||
|
||||||
method == QCryptographicHash::Blake2b_384) {
|
method == QCryptographicHash::Blake2b_384) {
|
||||||
const auto length = hashLengthInternal(method);
|
const auto length = hashLengthInternal(method);
|
||||||
blake2b_state copy = blake2bContext;
|
blake2b_state copy = blake2bContext;
|
||||||
result.resizeForOverwrite(length);
|
tmpresult.resizeForOverwrite(length);
|
||||||
blake2b_final(©, reinterpret_cast<uint8_t *>(result.data()), length);
|
blake2b_final(©, tmpresult.data(), length);
|
||||||
} else if (method == QCryptographicHash::Blake2s_128 ||
|
} else if (method == QCryptographicHash::Blake2s_128 ||
|
||||||
method == QCryptographicHash::Blake2s_160 ||
|
method == QCryptographicHash::Blake2s_160 ||
|
||||||
method == QCryptographicHash::Blake2s_224) {
|
method == QCryptographicHash::Blake2s_224) {
|
||||||
const auto length = hashLengthInternal(method);
|
const auto length = hashLengthInternal(method);
|
||||||
blake2s_state copy = blake2sContext;
|
blake2s_state copy = blake2sContext;
|
||||||
result.resizeForOverwrite(length);
|
tmpresult.resizeForOverwrite(length);
|
||||||
blake2s_final(©, reinterpret_cast<uint8_t *>(result.data()), length);
|
blake2s_final(©, tmpresult.data(), length);
|
||||||
} else if (!initializationFailed) {
|
} else if (!initializationFailed) {
|
||||||
result.resizeForOverwrite(EVP_MD_get_size(algorithm.get()));
|
tmpresult.resizeForOverwrite(EVP_MD_get_size(algorithm.get()));
|
||||||
const int ret = EVP_DigestFinal_ex(context.get(), (unsigned char *)result.data(), nullptr);
|
const int ret = EVP_DigestFinal_ex(context.get(), tmpresult.data(), nullptr);
|
||||||
Q_UNUSED(ret);
|
Q_UNUSED(ret);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
switch (method) {
|
switch (method) {
|
||||||
case QCryptographicHash::Sha1: {
|
case QCryptographicHash::Sha1: {
|
||||||
Sha1State copy = sha1Context;
|
Sha1State copy = sha1Context;
|
||||||
result.resizeForOverwrite(20);
|
tmpresult.resizeForOverwrite(20);
|
||||||
sha1FinalizeState(©);
|
sha1FinalizeState(©);
|
||||||
sha1ToHash(©, (unsigned char *)result.data());
|
sha1ToHash(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
#ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
|
||||||
@ -767,52 +773,52 @@ void QCryptographicHashPrivate::finalize() noexcept
|
|||||||
#else
|
#else
|
||||||
case QCryptographicHash::Md4: {
|
case QCryptographicHash::Md4: {
|
||||||
md4_context copy = md4Context;
|
md4_context copy = md4Context;
|
||||||
result.resizeForOverwrite(MD4_RESULTLEN);
|
tmpresult.resizeForOverwrite(MD4_RESULTLEN);
|
||||||
md4_final(©, (unsigned char *)result.data());
|
md4_final(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Md5: {
|
case QCryptographicHash::Md5: {
|
||||||
MD5Context copy = md5Context;
|
MD5Context copy = md5Context;
|
||||||
result.resizeForOverwrite(16);
|
tmpresult.resizeForOverwrite(16);
|
||||||
MD5Final(©, (unsigned char *)result.data());
|
MD5Final(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Sha224: {
|
case QCryptographicHash::Sha224: {
|
||||||
SHA224Context copy = sha224Context;
|
SHA224Context copy = sha224Context;
|
||||||
result.resizeForOverwrite(SHA224HashSize);
|
tmpresult.resizeForOverwrite(SHA224HashSize);
|
||||||
SHA224Result(©, reinterpret_cast<unsigned char *>(result.data()));
|
SHA224Result(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Sha256: {
|
case QCryptographicHash::Sha256: {
|
||||||
SHA256Context copy = sha256Context;
|
SHA256Context copy = sha256Context;
|
||||||
result.resizeForOverwrite(SHA256HashSize);
|
tmpresult.resizeForOverwrite(SHA256HashSize);
|
||||||
SHA256Result(©, reinterpret_cast<unsigned char *>(result.data()));
|
SHA256Result(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Sha384: {
|
case QCryptographicHash::Sha384: {
|
||||||
SHA384Context copy = sha384Context;
|
SHA384Context copy = sha384Context;
|
||||||
result.resizeForOverwrite(SHA384HashSize);
|
tmpresult.resizeForOverwrite(SHA384HashSize);
|
||||||
SHA384Result(©, reinterpret_cast<unsigned char *>(result.data()));
|
SHA384Result(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Sha512: {
|
case QCryptographicHash::Sha512: {
|
||||||
SHA512Context copy = sha512Context;
|
SHA512Context copy = sha512Context;
|
||||||
result.resizeForOverwrite(SHA512HashSize);
|
tmpresult.resizeForOverwrite(SHA512HashSize);
|
||||||
SHA512Result(©, reinterpret_cast<unsigned char *>(result.data()));
|
SHA512Result(©, tmpresult.data());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::RealSha3_224:
|
case QCryptographicHash::RealSha3_224:
|
||||||
case QCryptographicHash::RealSha3_256:
|
case QCryptographicHash::RealSha3_256:
|
||||||
case QCryptographicHash::RealSha3_384:
|
case QCryptographicHash::RealSha3_384:
|
||||||
case QCryptographicHash::RealSha3_512: {
|
case QCryptographicHash::RealSha3_512: {
|
||||||
sha3Finish(8 * hashLengthInternal(method), Sha3Variant::Sha3);
|
sha3Finish(&tmpresult, 8 * hashLengthInternal(method), Sha3Variant::Sha3);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Keccak_224:
|
case QCryptographicHash::Keccak_224:
|
||||||
case QCryptographicHash::Keccak_256:
|
case QCryptographicHash::Keccak_256:
|
||||||
case QCryptographicHash::Keccak_384:
|
case QCryptographicHash::Keccak_384:
|
||||||
case QCryptographicHash::Keccak_512: {
|
case QCryptographicHash::Keccak_512: {
|
||||||
sha3Finish(8 * hashLengthInternal(method), Sha3Variant::Keccak);
|
sha3Finish(&tmpresult, 8 * hashLengthInternal(method), Sha3Variant::Keccak);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Blake2b_160:
|
case QCryptographicHash::Blake2b_160:
|
||||||
@ -821,8 +827,8 @@ void QCryptographicHashPrivate::finalize() noexcept
|
|||||||
case QCryptographicHash::Blake2b_512: {
|
case QCryptographicHash::Blake2b_512: {
|
||||||
const auto length = hashLengthInternal(method);
|
const auto length = hashLengthInternal(method);
|
||||||
blake2b_state copy = blake2bContext;
|
blake2b_state copy = blake2bContext;
|
||||||
result.resizeForOverwrite(length);
|
tmpresult.resizeForOverwrite(length);
|
||||||
blake2b_final(©, reinterpret_cast<uint8_t *>(result.data()), length);
|
blake2b_final(©, tmpresult.data(), length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QCryptographicHash::Blake2s_128:
|
case QCryptographicHash::Blake2s_128:
|
||||||
@ -831,13 +837,18 @@ void QCryptographicHashPrivate::finalize() noexcept
|
|||||||
case QCryptographicHash::Blake2s_256: {
|
case QCryptographicHash::Blake2s_256: {
|
||||||
const auto length = hashLengthInternal(method);
|
const auto length = hashLengthInternal(method);
|
||||||
blake2s_state copy = blake2sContext;
|
blake2s_state copy = blake2sContext;
|
||||||
result.resizeForOverwrite(length);
|
tmpresult.resizeForOverwrite(length);
|
||||||
blake2s_final(©, reinterpret_cast<uint8_t *>(result.data()), length);
|
blake2s_final(©, tmpresult.data(), length);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif // !QT_CONFIG(opensslv30)
|
#endif // !QT_CONFIG(opensslv30)
|
||||||
|
|
||||||
|
// we're called from a const function, so only write to this->result under
|
||||||
|
// a mutex
|
||||||
|
QMutexLocker locker(&finalizeMutex);
|
||||||
|
result = std::move(tmpresult);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Loading…
x
Reference in New Issue
Block a user