1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
// Copyright 2017-2021 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 <QtCore>
#include <QtTest>
#include "SSL.h"
#include <openssl/rand.h>
/// SSLRacer is a thread that runs operations on OpenSSL's
/// RAND infrastructure, in an attempt to crash/segfault
/// the test process.
///
/// The thread can either query OpenSSL for random bytes, or
/// seed the RAND system. This is controlled by the |seed|
/// parameter to the constructor.
class SSLRacer : public QThread {
public:
bool m_seed;
QAtomicInt *m_running;
SSLRacer(QAtomicInt *running, bool seed) : m_seed(seed), m_running(running) {}
void run() {
unsigned char buf[64];
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
while (m_running->loadRelaxed() == 1) {
#else
// Qt 5.14 introduced QAtomicInteger::loadRelaxed() which deprecates QAtomicInteger::load()
while (m_running->load() == 1) {
#endif
for (int i = 0; i < 1024; i++) {
if (m_seed) {
RAND_seed(buf, sizeof(buf));
} else {
RAND_bytes(buf, sizeof(buf));
}
}
}
}
};
class TestSSLLocks : public QObject {
Q_OBJECT
private slots:
void initTestCase();
void cleanupTestCase();
void stress();
};
void TestSSLLocks::initTestCase() {
// For OpenSSL < 1.1, if you comment out this line,
// you'll be running OpenSSL without locking callbacks
// enabled. That'll make this test fail by crashing/segfaulting.
MumbleSSL::initialize();
}
void TestSSLLocks::cleanupTestCase() {
MumbleSSL::destroy();
}
/// Stress test that our locking callbacks for OpenSSL are set up and
/// working correctly.
///
/// We do this by spawning a bunch of SSLStresser threads. Some of the
/// threads will try to read random bytes in a tight loop. Other threads
/// will, at the same time, try to add/seed random bytes to the OpenSSL
/// RAND system.
///
/// The idea is that without proper locking, the data races we're causing
/// should quite quickly cause the process to crash.
void TestSSLLocks::stress() {
std::vector< SSLRacer * > racers;
QAtomicInt running(1);
// Spawn 24 threads in total. 12 readers, and 12 writers.
// Don't be too careful about cleaning up the threads. We'll either
// pass or crash, so the threads will be cleaned up either way.
int nthreads = 24;
for (int i = 0; i < nthreads; i++) {
bool seeder = i % 2;
SSLRacer *racer = new SSLRacer(&running, seeder);
racers.push_back(racer);
racer->start();
}
// Wait 2 seconds for a crash/segfault.
// If we don't crash within 2 seconds, we expect
// that our locking implementation works.
QTest::qSleep(2000);
// Signal to the racers that they should stop.
running.fetchAndStoreOrdered(0);
// Wait for all racers to complete.
for (size_t i = 0; i < racers.size(); i++) {
SSLRacer *racer = racers.at(i);
racer->wait();
delete racer;
}
}
QTEST_MAIN(TestSSLLocks)
#include "TestSSLLocks.moc"
|