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), handle(INVALID_HANDLE_VALUE),
readBufferMaxSize(0), readBufferMaxSize(0),
actualReadBufferSize(0), actualReadBufferSize(0),
stopped(true),
readSequenceStarted(false), readSequenceStarted(false),
pipeBroken(false), pipeBroken(false),
readyReadEmitted(false) readyReadEmitted(false)
@ -69,12 +70,7 @@ static bool qt_cancelIo(HANDLE handle, OVERLAPPED *overlapped)
QWindowsPipeReader::~QWindowsPipeReader() QWindowsPipeReader::~QWindowsPipeReader()
{ {
if (readSequenceStarted) { stop();
if (qt_cancelIo(handle, &overlapped))
dataReadNotifier->waitForNotified(-1, &overlapped);
else
qErrnoWarning("QWindowsPipeReader: qt_cancelIo on handle %x failed.", handle);
}
} }
/*! /*!
@ -87,6 +83,7 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
handle = hPipeReadEnd; handle = hPipeReadEnd;
pipeBroken = false; pipeBroken = false;
readyReadEmitted = false; readyReadEmitted = false;
stopped = false;
if (hPipeReadEnd != INVALID_HANDLE_VALUE) { if (hPipeReadEnd != INVALID_HANDLE_VALUE) {
dataReadNotifier->setHandle(hPipeReadEnd); dataReadNotifier->setHandle(hPipeReadEnd);
dataReadNotifier->setEnabled(true); dataReadNotifier->setEnabled(true);
@ -95,13 +92,24 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
/*! /*!
Stops the asynchronous read sequence. Stops the asynchronous read sequence.
This function assumes that the file already has been closed. If the read sequence is running then the I/O operation is canceled.
It does not cancel any I/O operation.
*/ */
void QWindowsPipeReader::stop() 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; readSequenceStarted = false;
dataReadNotifier->setEnabled(false);
handle = INVALID_HANDLE_VALUE; handle = INVALID_HANDLE_VALUE;
} }
@ -142,7 +150,7 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
} }
if (!pipeBroken) { if (!pipeBroken) {
if (!readSequenceStarted) if (!readSequenceStarted && !stopped)
startAsyncRead(); startAsyncRead();
if (readSoFar == 0) if (readSoFar == 0)
return -2; // signal EWOULDBLOCK return -2; // signal EWOULDBLOCK
@ -185,6 +193,13 @@ void QWindowsPipeReader::notified(quint32 numberOfBytesRead, quint32 errorCode,
} }
readSequenceStarted = false; 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) { if (pipeBroken) {
emit pipeClosed(); emit pipeClosed();
return; return;

View File

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