Network (HTTPS): prevent recursion among ->close() methods
We observed a stack-trace in which, while handling an error, QHttpNetworkConnectionChannel::close()'s call to its socket->close() triggered (when the socket was a QSslSocket) a flush() which asked its backend to transmit() which tripped over the original error, which duly triggered endless recursion. Transiently clear the socket member, during its ->close(), to prevent this; do the same in abort(), to preserve its structural correspondence to close(). Restructure both so that any recursive call's setting of state is overwritten by the top-level call's, while this still uses the prior socket state (not the state after close() or abort() and any recursion) to determine final state. Task-number: QTBUG-56476 Change-Id: If69e97f7a77a729bf2338ed14214c65aa95f8b05 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
parent
7634225ad2
commit
556b2ee773
@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
#include <qpair.h>
|
#include <qpair.h>
|
||||||
#include <qdebug.h>
|
#include <qdebug.h>
|
||||||
|
#include <QScopedValueRollback>
|
||||||
|
|
||||||
#ifndef QT_NO_HTTP
|
#ifndef QT_NO_HTTP
|
||||||
|
|
||||||
@ -196,41 +197,42 @@ void QHttpNetworkConnectionChannel::init()
|
|||||||
|
|
||||||
void QHttpNetworkConnectionChannel::close()
|
void QHttpNetworkConnectionChannel::close()
|
||||||
{
|
{
|
||||||
if (!socket)
|
// socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
|
||||||
state = QHttpNetworkConnectionChannel::IdleState;
|
// there is no socket yet; we may also clear it, below or in abort, to avoid recursion.
|
||||||
else if (socket->state() == QAbstractSocket::UnconnectedState)
|
const bool idle = !socket || socket->state() == QAbstractSocket::UnconnectedState;
|
||||||
state = QHttpNetworkConnectionChannel::IdleState;
|
const ChannelState final = idle ? IdleState : ClosingState;
|
||||||
else
|
|
||||||
state = QHttpNetworkConnectionChannel::ClosingState;
|
|
||||||
|
|
||||||
// pendingEncrypt must only be true in between connected and encrypted states
|
// pendingEncrypt must only be true in between connected and encrypted states
|
||||||
pendingEncrypt = false;
|
pendingEncrypt = false;
|
||||||
|
|
||||||
if (socket) {
|
if (socket) {
|
||||||
// socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
|
QAbstractSocket *const detached = socket;
|
||||||
// there is no socket yet.
|
QScopedValueRollback<QAbstractSocket *> rollback(socket, nullptr);
|
||||||
socket->close();
|
state = final; // Needed during close(), which may over-write it.
|
||||||
|
detached->close(); // Error states can cause recursion back to this method.
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Set state *after* close(), to override any recursive call's idle setting:
|
||||||
|
state = final;
|
||||||
|
}
|
||||||
|
|
||||||
void QHttpNetworkConnectionChannel::abort()
|
void QHttpNetworkConnectionChannel::abort()
|
||||||
{
|
{
|
||||||
if (!socket)
|
// socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
|
||||||
state = QHttpNetworkConnectionChannel::IdleState;
|
// there is no socket yet; we may also clear it, below or in close, to avoid recursion.
|
||||||
else if (socket->state() == QAbstractSocket::UnconnectedState)
|
const bool idle = !socket || socket->state() == QAbstractSocket::UnconnectedState;
|
||||||
state = QHttpNetworkConnectionChannel::IdleState;
|
const ChannelState final = idle ? IdleState : ClosingState;
|
||||||
else
|
|
||||||
state = QHttpNetworkConnectionChannel::ClosingState;
|
|
||||||
|
|
||||||
// pendingEncrypt must only be true in between connected and encrypted states
|
// pendingEncrypt must only be true in between connected and encrypted states
|
||||||
pendingEncrypt = false;
|
pendingEncrypt = false;
|
||||||
|
|
||||||
if (socket) {
|
if (socket) {
|
||||||
// socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
|
QAbstractSocket *const detached = socket;
|
||||||
// there is no socket yet.
|
QScopedValueRollback<QAbstractSocket *> rollback(socket, nullptr);
|
||||||
socket->abort();
|
state = final;
|
||||||
|
detached->abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set state *after* abort(), to override any recursive call's idle setting:
|
||||||
|
state = final;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user