From dedca8df0114c92cd2e0cbd98856bd50a9d3bb4d Mon Sep 17 00:00:00 2001
From: Peter Seiderer <ps.report@gmx.net>
Date: Tue, 19 Feb 2013 19:26:02 +0100
Subject: [PATCH] QDBusPendingCall: fix unprotected access to
 QDBusPendingCallPrivate::pending.

In QDBusConnectionPrivate::waitForFinished() pcall->pending was used
after the protection by pcall->mutex was released. A simultaneous
call to QDBusConnectionPrivate::processFinishedCall() was able
to reset pcall->pending to null before it was used for the
q_dbus_pending_call_block(pcall->pending) call.

There are two use cases:
a) sending via QDBusConnection::asyncCall(const QDBusMessage &message, int timeout)
   and wait for response via  QDBusPendingCall::waitForFinished()
b) sending via QDBusConnection::callWithCallback()
   and receiving the response async via the given slots

Fix for case a):
Remove q_dbus_pending_call_set_notify() call from first
QDBusConnectionPrivate::sendWithReplyAsync() implementation which is used by
QDBusConnection::asyncCall(), always receive response via
q_dbus_pending_call_block() in QDBusConnectionPrivate::waitForFinished()
and call QDBusConnectionPrivate::processFinishedCall() explicit (here no more
need for lock call->mutex, already hold in waitForFinished()).

Fix for case b):
Add q_dbus_pending_call_set_notify() call to second
QDBusConnectionPrivate::sendWithReplyAsync() implementation, which is used by
QDBusConnection::callWithCallback(), add QMutexLocker locker(&call->mutex) to
qDBusResultReceived() before calling QDBusConnectionPrivate::processFinishedCall().

Additional fix:
Add missing call to call->waitForFinishedCondition.wakeAll()).

Fixes https://bugreports.qt-project.org/browse/QTBUG-27809.
---
 src/dbus/qdbusintegrator.cpp | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp
index 0f0b647..7fae872 100644
--- a/src/dbus/qdbusintegrator.cpp
+++ b/src/dbus/qdbusintegrator.cpp
@@ -1759,6 +1759,7 @@ static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
     QDBusPendingCallPrivate *call = reinterpret_cast<QDBusPendingCallPrivate *>(user_data);
     Q_ASSERT(call->pending == pending);
     Q_UNUSED(pending);
+    QMutexLocker locker(&call->mutex);
     QDBusConnectionPrivate::processFinishedCall(call);
 }
 }
@@ -1774,14 +1775,11 @@ void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall)
         pcall->waitForFinishedCondition.wait(&pcall->mutex);
     } else {
         pcall->waitingForFinished = true;
-        pcall->mutex.unlock();
-
         {
             QDBusDispatchLocker locker(PendingCallBlockAction, this);
             q_dbus_pending_call_block(pcall->pending);
-            // QDBusConnectionPrivate::processFinishedCall() is called automatically
+            processFinishedCall(pcall);
         }
-        pcall->mutex.lock();
     }
 }
 
@@ -1789,8 +1787,6 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
 {
     QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
 
-    QMutexLocker locker(&call->mutex);
-
     QDBusMessage &msg = call->replyMessage;
     if (call->pending) {
         // decode the message
@@ -1824,7 +1820,8 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
         q_dbus_pending_call_unref(call->pending);
     call->pending = 0;
 
-    locker.unlock();
+    if (call->waitingForFinished)
+        call->waitForFinishedCondition.wakeAll();
 
     // Are there any watchers?
     if (call->watcherHelper)
@@ -2009,7 +2006,6 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM
             q_dbus_message_unref(msg);
 
             pcall->pending = pending;
-            q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
 
             return pcall;
         } else {
@@ -2032,6 +2028,13 @@ int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObj
     QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout);
     Q_ASSERT(pcall);
 
+    // set callback for dbus result handling (but lock mutex first to
+    // protect against async qDBusResultReceived calls)
+    QMutexLocker locker(&pcall->mutex);
+    if (pcall->pending) {
+        q_dbus_pending_call_set_notify(pcall->pending, qDBusResultReceived, pcall, 0);
+    }
+
     // has it already finished with success (dispatched locally)?
     if (pcall->replyMessage.type() == QDBusMessage::ReplyMessage) {
         pcall->setReplyCallback(receiver, returnMethod);
-- 
1.8.1.4

