diff options
author | Shane Kearns <shane.kearns@accenture.com> | 2011-01-17 15:13:52 (GMT) |
---|---|---|
committer | Shane Kearns <shane.kearns@accenture.com> | 2011-01-17 15:47:28 (GMT) |
commit | 4fb5c42ede2b33c3092d70aa3e32cf6c677bd4d8 (patch) | |
tree | d96c7aa1c882619e75cb2a85a7a67d73d601c424 | |
parent | b1477e557681372d2c7119d637048123f0bad120 (diff) | |
download | Qt-4fb5c42ede2b33c3092d70aa3e32cf6c677bd4d8.zip Qt-4fb5c42ede2b33c3092d70aa3e32cf6c677bd4d8.tar.gz Qt-4fb5c42ede2b33c3092d70aa3e32cf6c677bd4d8.tar.bz2 |
Fix uncaught exception crash when socket ready read signal throws
When the application (in this case, an autotest) ran out of memory by
calling readAll() in a slot, the exception propagated to the symbian
active scheduler where it caused an assertion failure.
Additionally, QAbstractSocket was left in a bad state because its
member variables that guard against re-entrancy were left set.
1. Catch exceptions and convert to leaves in QAsyncSelect
2. Implement RunError function to set the socket engine error state
3. Use QScopedValueRollback in QAbstractSocket to ensure the reentrancy
guards are reverted if an exception is thrown.
Reviewed-by: Markus Goetz
Reviewed-by: mread
-rw-r--r-- | src/network/socket/qabstractsocket.cpp | 10 | ||||
-rw-r--r-- | src/network/socket/qsymbiansocketengine.cpp | 37 | ||||
-rw-r--r-- | src/network/socket/qsymbiansocketengine_p.h | 3 |
3 files changed, 40 insertions, 10 deletions
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index c638e2a..156b905 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -375,6 +375,7 @@ #include <qpointer.h> #include <qtimer.h> #include <qelapsedtimer.h> +#include <qscopedvaluerollback.h> #ifndef QT_NO_OPENSSL #include <QtNetwork/qsslsocket.h> @@ -592,6 +593,7 @@ bool QAbstractSocketPrivate::canReadNotification() socketEngine->setReadNotificationEnabled(false); } } + QScopedValueRollback<bool> rsncrollback(readSocketNotifierCalled); readSocketNotifierCalled = true; if (!isBuffered) @@ -605,7 +607,6 @@ bool QAbstractSocketPrivate::canReadNotification() #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full"); #endif - readSocketNotifierCalled = false; return false; } @@ -617,7 +618,6 @@ bool QAbstractSocketPrivate::canReadNotification() qDebug("QAbstractSocketPrivate::canReadNotification() disconnecting socket"); #endif q->disconnectFromHost(); - readSocketNotifierCalled = false; return false; } newBytes = readBuffer.size() - newBytes; @@ -637,9 +637,9 @@ bool QAbstractSocketPrivate::canReadNotification() ; if (!emittedReadyRead && hasData) { + QScopedValueRollback<bool> r(emittedReadyRead); emittedReadyRead = true; emit q->readyRead(); - emittedReadyRead = false; } // If we were closed as a result of the readyRead() signal, @@ -648,7 +648,6 @@ bool QAbstractSocketPrivate::canReadNotification() #if defined (QABSTRACTSOCKET_DEBUG) qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning"); #endif - readSocketNotifierCalled = false; return true; } @@ -662,7 +661,6 @@ bool QAbstractSocketPrivate::canReadNotification() socketEngine->setReadNotificationEnabled(readSocketNotifierState); readSocketNotifierStateSet = false; } - readSocketNotifierCalled = false; return true; } @@ -768,9 +766,9 @@ bool QAbstractSocketPrivate::flush() if (written > 0) { // Don't emit bytesWritten() recursively. if (!emittedBytesWritten) { + QScopedValueRollback<bool> r(emittedBytesWritten); emittedBytesWritten = true; emit q->bytesWritten(written); - emittedBytesWritten = false; } } diff --git a/src/network/socket/qsymbiansocketengine.cpp b/src/network/socket/qsymbiansocketengine.cpp index 2f991de..455bf5e 100644 --- a/src/network/socket/qsymbiansocketengine.cpp +++ b/src/network/socket/qsymbiansocketengine.cpp @@ -1308,6 +1308,9 @@ void QSymbianSocketEnginePrivate::setError(TInt symbianError) case KErrNotSupported: setError(QAbstractSocket::UnsupportedSocketOperationError, OperationUnsupportedErrorString); break; + case KErrNoMemory: + setError(QAbstractSocket::SocketResourceError, ResourceErrorString); + break; default: socketError = QAbstractSocket::NetworkError; socketErrorString = QString::number(symbianError); @@ -1564,22 +1567,47 @@ void QAsyncSelect::DoCancel() void QAsyncSelect::RunL() { + QT_TRYCATCH_LEAVING(run()); +} + +//RunError is called by the active scheduler if RunL leaves. +//Typically this will happen if a std::bad_alloc propagates down from the application +TInt QAsyncSelect::RunError(TInt aError) +{ + if (engine) { + QT_TRY { + engine->d_func()->setError(aError); + QEvent e(QEvent::SockAct); + if (iExcN) + iExcN->event(&e); + if (iReadN) + iReadN->event(&e); + } + QT_CATCH(...) {} + } +#ifdef QNATIVESOCKETENGINE_DEBUG + qDebug() << "QAsyncSelect::RunError" << aError; +#endif + return KErrNone; +} + +void QAsyncSelect::run() +{ //TODO: block when event loop demands it //if (maybeQueueForLater()) // return; - m_inSocketEvent = true; m_selectBuf() &= m_selectFlags; //the select ioctl reports everything, so mask to only what we requested //TODO: KSockSelectReadContinuation does what? - if (m_selectBuf() & KSockSelectRead) { + if (iReadN && (m_selectBuf() & KSockSelectRead)) { QEvent e(QEvent::SockAct); iReadN->event(&e); } - if (m_selectBuf() & KSockSelectWrite) { + if (iWriteN && (m_selectBuf() & KSockSelectWrite)) { QEvent e(QEvent::SockAct); iWriteN->event(&e); } - if ((m_selectBuf() & KSockSelectExcept) || iStatus != KErrNone) { + if (iExcN && ((m_selectBuf() & KSockSelectExcept) || iStatus != KErrNone)) { QEvent e(QEvent::SockAct); iExcN->event(&e); } @@ -1598,6 +1626,7 @@ void QAsyncSelect::deleteLater() iExcN = 0; iReadN = 0; iWriteN = 0; + engine = 0; m_deleteLater = true; } else { delete this; diff --git a/src/network/socket/qsymbiansocketengine_p.h b/src/network/socket/qsymbiansocketengine_p.h index bc85f9c..f36b7dd 100644 --- a/src/network/socket/qsymbiansocketengine_p.h +++ b/src/network/socket/qsymbiansocketengine_p.h @@ -72,6 +72,7 @@ class QNetworkInterface; class Q_AUTOTEST_EXPORT QSymbianSocketEngine : public QAbstractSocketEngine { Q_OBJECT + friend class QAsyncSelect; public: QSymbianSocketEngine(QObject *parent = 0); ~QSymbianSocketEngine(); @@ -167,6 +168,8 @@ public: protected: void DoCancel(); void RunL(); + void run(); + TInt RunError(TInt aError); private: QReadNotifier* iReadN; |