From 158781ff2555fabcb9af6b47c887519ade5aba50 Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Tue, 6 Sep 2016 12:36:55 +0200 Subject: QSslSocket: respect read buffer's max size (SecureTransport) 1. QSslSocketBackendPrivate::transmit was ignoring 'readBufferMaxSize'; as a result, we can have a user trying to set read buffer's size to a small value (and more important - reading slowly in a small chunks from this socket), but SSL itself socket reading 'too fast', potentially growing its internal buffer to a huge size. This also results in auto-tests failing - whenever we're trying to limit read rate in some test. 2. Update qsslsocket auto-test. Task-number: QTBUG-43388 Task-number: QTBUG-55170 Change-Id: Iedece26df0ac5b3b7cad62cc8c98aedc28e7ca5b Reviewed-by: Richard J. Moore --- src/network/ssl/qsslsocket_mac.cpp | 2 +- .../auto/network/ssl/qsslsocket/tst_qsslsocket.cpp | 63 ++++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp index 8aa9269f4b..233f7b5d15 100644 --- a/src/network/ssl/qsslsocket_mac.cpp +++ b/src/network/ssl/qsslsocket_mac.cpp @@ -668,7 +668,7 @@ void QSslSocketBackendPrivate::transmit() if (connectionEncrypted) { QVarLengthArray data; - while (context) { + while (context && (!readBufferMaxSize || buffer.size() < readBufferMaxSize)) { size_t readBytes = 0; data.resize(4096); const OSStatus err = SSLRead(context, data.data(), data.size(), &readBytes); diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 2d35081dff..9b544e0db8 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -229,6 +229,7 @@ private slots: void ecdhServer(); void verifyClientCertificate_data(); void verifyClientCertificate(); + void readBufferMaxSize(); void setEmptyDefaultConfiguration(); // this test should be last #ifndef QT_NO_OPENSSL @@ -3043,6 +3044,68 @@ void tst_QSslSocket::verifyClientCertificate() QCOMPARE(client->isEncrypted(), works); } +void tst_QSslSocket::readBufferMaxSize() +{ +#ifdef QT_SECURETRANSPORT + // QTBUG-55170: + // SecureTransport back-end was ignoring read-buffer + // size limit, resulting (potentially) in a constantly + // growing internal buffer. + // The test's logic is: we set a small read buffer size on a client + // socket (to some ridiculously small value), server sends us + // a bunch of bytes , we ignore readReady signal so + // that socket's internal buffer size stays + // >= readBufferMaxSize, we wait for a quite long time + // (which previously would be enough to read completely) + // and we check socket's bytesAvaiable to be less than sent. + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + return; + + SslServer server; + QVERIFY(server.listen()); + + QEventLoop loop; + + QSslSocketPtr client(new QSslSocket); + socket = client.data(); + connect(socket, SIGNAL(error(QAbstractSocket::SocketError)), &loop, SLOT(quit())); + connect(socket, SIGNAL(sslErrors(QList)), this, SLOT(ignoreErrorSlot())); + connect(socket, SIGNAL(encrypted()), &loop, SLOT(quit())); + + client->connectToHostEncrypted(QHostAddress(QHostAddress::LocalHost).toString(), + server.serverPort()); + + // Wait for 'encrypted' first: + QTimer::singleShot(5000, &loop, SLOT(quit())); + loop.exec(); + + QCOMPARE(client->state(), QAbstractSocket::ConnectedState); + QCOMPARE(client->mode(), QSslSocket::SslClientMode); + + client->setReadBufferSize(10); + const QByteArray message(int(0xffff), 'a'); + server.socket->write(message); + + QTimer::singleShot(5000, &loop, SLOT(quit())); + loop.exec(); + + int readSoFar = client->bytesAvailable(); + QVERIFY(readSoFar > 0 && readSoFar < message.size()); + // Now, let's check that we still can read the rest of it: + QCOMPARE(client->readAll().size(), readSoFar); + + client->setReadBufferSize(0); + + QTimer::singleShot(1500, &loop, SLOT(quit())); + loop.exec(); + + QCOMPARE(client->bytesAvailable() + readSoFar, message.size()); +#else + // Not needed, QSslSocket works correctly with other back-ends. +#endif +} + void tst_QSslSocket::setEmptyDefaultConfiguration() // this test should be last, as it has some side effects { // used to produce a crash in QSslConfigurationPrivate::deepCopyDefaultConfiguration, QTBUG-13265 -- cgit v1.2.3