aboutsummaryrefslogtreecommitdiff
path: root/lib/jobs/basejob.cpp
diff options
context:
space:
mode:
authorNicolas Fella <nicolas.fella@gmx.de>2020-12-27 21:24:06 +0100
committerKitsune Ral <Kitsune-Ral@users.sf.net>2020-12-27 22:25:44 +0100
commitf286ef4c5b3c71510d6ef15e8cc12cada84f3682 (patch)
tree8ff187ab7d3ae3dfa8d653c499ed16338b4a073d /lib/jobs/basejob.cpp
parentd6c2622b0cdc33ad272542ab611c15de07202520 (diff)
downloadlibquotient-f286ef4c5b3c71510d6ef15e8cc12cada84f3682.tar.gz
libquotient-f286ef4c5b3c71510d6ef15e8cc12cada84f3682.zip
Fix use-after-free of QNetworkReply in BaseJob
Usually QNetworkAccessManager expects the user to delete the replies, but when the QNetworkAccessManager itself is deleted it deletes all pending replies (https://code.woboq.org/qt5/qtbase/src/network/access/qnetworkaccessmanager.cpp.html#529). This can lead to use-after-free crashes when d->reply is accessed. By putting the reply into a QPointer the exiting if(d->reply) checks can work properly. (cherry picked from commit 9d854e778d8d6ef8e03e1ea74fe958675b24fd45)
Diffstat (limited to 'lib/jobs/basejob.cpp')
-rw-r--r--lib/jobs/basejob.cpp33
1 files changed, 19 insertions, 14 deletions
diff --git a/lib/jobs/basejob.cpp b/lib/jobs/basejob.cpp
index 3fa1cd94..2ac942f5 100644
--- a/lib/jobs/basejob.cpp
+++ b/lib/jobs/basejob.cpp
@@ -24,6 +24,7 @@
#include <QtCore/QTimer>
#include <QtCore/QStringBuilder>
#include <QtCore/QMetaEnum>
+#include <QtCore/QPointer>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
@@ -76,15 +77,6 @@ QDebug BaseJob::Status::dumpToLog(QDebug dbg) const
return dbg << ": " << message;
}
-struct NetworkReplyDeleter : public QScopedPointerDeleteLater {
- static inline void cleanup(QNetworkReply* reply)
- {
- if (reply && reply->isRunning())
- reply->abort();
- QScopedPointerDeleteLater::cleanup(reply);
- }
-};
-
template <typename... Ts>
constexpr auto make_array(Ts&&... items)
{
@@ -112,6 +104,16 @@ public:
retryTimer.setSingleShot(true);
}
+ ~Private()
+ {
+ if (reply) {
+ if (reply->isRunning()) {
+ reply->abort();
+ }
+ delete reply;
+ }
+ }
+
void sendRequest();
/*! \brief Parse the response byte array into JSON
*
@@ -140,7 +142,10 @@ public:
QByteArrayList expectedKeys;
- QScopedPointer<QNetworkReply, NetworkReplyDeleter> reply;
+ // When the QNetworkAccessManager is destroyed it destroys all pending replies.
+ // Using QPointer allows us to know when that happend.
+ QPointer<QNetworkReply> reply;
+
Status status = Unprepared;
QByteArray rawResponse;
/// Contains a null document in case of non-JSON body (for a successful
@@ -315,16 +320,16 @@ void BaseJob::Private::sendRequest()
switch (verb) {
case HttpVerb::Get:
- reply.reset(connection->nam()->get(req));
+ reply = connection->nam()->get(req);
break;
case HttpVerb::Post:
- reply.reset(connection->nam()->post(req, requestData.source()));
+ reply = connection->nam()->post(req, requestData.source());
break;
case HttpVerb::Put:
- reply.reset(connection->nam()->put(req, requestData.source()));
+ reply = connection->nam()->put(req, requestData.source());
break;
case HttpVerb::Delete:
- reply.reset(connection->nam()->sendCustomRequest(req, "DELETE", requestData.source()));
+ reply = connection->nam()->sendCustomRequest(req, "DELETE", requestData.source());
break;
}
}