let QWindowsPipeReader::stop() cancel the current I/O operation

In rare cases the I/O operation was still running after the destructor
was running, which then modified free'd memory and caused a malformed
heap. To prevent this, we ensure that QWindowsPipeReader::stop() cancels
a running I/O operation and sets the readSequenceStarted flag correctly.
Also, we prevent the start of a new read operation after we called stop().

Change-Id: If8a28bdf23a39a0e88c1770a6f66e2b24ea426bb
Task-number: QTBUG-45601
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
This commit is contained in:
Joerg Bornemann 2015-04-15 15:37:24 +02:00
parent a7f1c97d60
commit 5ce567c536
2 changed files with 26 additions and 10 deletions

View File

@ -44,6 +44,7 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
handle(INVALID_HANDLE_VALUE),
readBufferMaxSize(0),
actualReadBufferSize(0),
stopped(true),
readSequenceStarted(false),
pipeBroken(false),
readyReadEmitted(false)
@ -69,12 +70,7 @@ static bool qt_cancelIo(HANDLE handle, OVERLAPPED *overlapped)
QWindowsPipeReader::~QWindowsPipeReader()
{
if (readSequenceStarted) {
if (qt_cancelIo(handle, &overlapped))
dataReadNotifier->waitForNotified(-1, &overlapped);
else
qErrnoWarning("QWindowsPipeReader: qt_cancelIo on handle %x failed.", handle);
}
stop();
}
/*!
@ -87,6 +83,7 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
handle = hPipeReadEnd;
pipeBroken = false;
readyReadEmitted = false;
stopped = false;
if (hPipeReadEnd != INVALID_HANDLE_VALUE) {
dataReadNotifier->setHandle(hPipeReadEnd);
dataReadNotifier->setEnabled(true);
@ -95,13 +92,24 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
/*!
Stops the asynchronous read sequence.
This function assumes that the file already has been closed.
It does not cancel any I/O operation.
If the read sequence is running then the I/O operation is canceled.
*/
void QWindowsPipeReader::stop()
{
dataReadNotifier->setEnabled(false);
stopped = true;
if (readSequenceStarted) {
if (qt_cancelIo(handle, &overlapped)) {
dataReadNotifier->waitForNotified(-1, &overlapped);
} else {
const DWORD dwError = GetLastError();
if (dwError != ERROR_NOT_FOUND) {
qErrnoWarning(dwError, "QWindowsPipeReader: qt_cancelIo on handle %x failed.",
handle);
}
}
}
readSequenceStarted = false;
dataReadNotifier->setEnabled(false);
handle = INVALID_HANDLE_VALUE;
}
@ -142,7 +150,7 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
}
if (!pipeBroken) {
if (!readSequenceStarted)
if (!readSequenceStarted && !stopped)
startAsyncRead();
if (readSoFar == 0)
return -2; // signal EWOULDBLOCK
@ -185,6 +193,13 @@ void QWindowsPipeReader::notified(quint32 numberOfBytesRead, quint32 errorCode,
}
readSequenceStarted = false;
// After the reader was stopped, the only reason why this function can be called is the
// completion of a cancellation. No signals should be emitted, and no new read sequence should
// be started in this case.
if (stopped)
return;
if (pipeBroken) {
emit pipeClosed();
return;

View File

@ -97,6 +97,7 @@ private:
qint64 readBufferMaxSize;
QRingBuffer readBuffer;
int actualReadBufferSize;
bool stopped;
bool readSequenceStarted;
bool pipeBroken;
bool readyReadEmitted;