QNetworkConnectionMonitor[Mac]: Fix potential use-after/during-free
There is a potential race condition on Mac that can lead to crashes when destroying `QNetworkConnectionMonitor`. This issue arises when `QNetworkConnectionMonitorPrivate` has already been destroyed, but is still being accessed through `QNCMP::probeCallback()` from another thread. To fix the issue, a reference counter is used. This counter indicates whether the callback is being used in monitoring. It increases when information is retained (when we set the callback, the info retains to 1) and decreases when information is released (the info releases to 0, when QNCMP object is not used in monitoring). A waitCondition is used to notify all threads when the reference counter reaches zero. The QNCMP::reset function waits until the probeCallback is disconnected and the reference counter is zero. This indicates that the info resource is free, allowing us to safely destroy the QNCMP object. The conditional variable protects the QNetworkConnectionMonitorPrivate object and ensures that the callback is disconnected properly. Fixes: QTBUG-130552 Pick-to: 6.5 Change-Id: I1259c429e92bc20c382604192243d6d8fadb5c25 Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Vladimir Belyavsky <belyavskyv@gmail.com> (cherry picked from commit 8a535cc1049c4304754b380daaefdece3a7c2e5b) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
5642b6fe7c
commit
b9750a98ff
@ -11,6 +11,9 @@
|
||||
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qwaitcondition.h>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
@ -85,6 +88,10 @@ public:
|
||||
SCNetworkReachabilityFlags state = kSCNetworkReachabilityFlagsIsLocalAddress;
|
||||
bool scheduled = false;
|
||||
|
||||
QWaitCondition refCounterWaitCondition;
|
||||
QMutex refCounterMutex;
|
||||
quint32 refCounter = 0;
|
||||
|
||||
void updateState(SCNetworkReachabilityFlags newState);
|
||||
void reset();
|
||||
bool isReachable() const;
|
||||
@ -92,7 +99,30 @@ public:
|
||||
bool isWwan() const;
|
||||
#endif
|
||||
|
||||
void retain()
|
||||
{
|
||||
QMutexLocker locker(&refCounterMutex);
|
||||
++refCounter;
|
||||
}
|
||||
|
||||
void release()
|
||||
{
|
||||
QMutexLocker locker(&refCounterMutex);
|
||||
if (--refCounter == 0)
|
||||
refCounterWaitCondition.wakeAll();
|
||||
}
|
||||
|
||||
void waitForRefCountZero()
|
||||
{
|
||||
QMutexLocker locker(&refCounterMutex);
|
||||
while (refCounter > 0) {
|
||||
refCounterWaitCondition.wait(&refCounterMutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void probeCallback(SCNetworkReachabilityRef probe, SCNetworkReachabilityFlags flags, void *info);
|
||||
static const void *retainInfo(const void* info);
|
||||
static void releaseInfo(const void* info);
|
||||
|
||||
Q_DECLARE_PUBLIC(QNetworkConnectionMonitor)
|
||||
};
|
||||
@ -130,6 +160,7 @@ void QNetworkConnectionMonitorPrivate::reset()
|
||||
|
||||
state = kSCNetworkReachabilityFlagsIsLocalAddress;
|
||||
scheduled = false;
|
||||
waitForRefCountZero();
|
||||
}
|
||||
|
||||
bool QNetworkConnectionMonitorPrivate::isReachable() const
|
||||
@ -154,6 +185,19 @@ void QNetworkConnectionMonitorPrivate::probeCallback(SCNetworkReachabilityRef pr
|
||||
monitorPrivate->updateState(flags);
|
||||
}
|
||||
|
||||
const void *QNetworkConnectionMonitorPrivate::retainInfo(const void *info)
|
||||
{
|
||||
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate*>(const_cast<void*>(info));
|
||||
monitorPrivate->retain();
|
||||
return info;
|
||||
}
|
||||
|
||||
void QNetworkConnectionMonitorPrivate::releaseInfo(const void *info)
|
||||
{
|
||||
auto monitorPrivate = static_cast<QNetworkConnectionMonitorPrivate*>(const_cast<void*>(info));
|
||||
monitorPrivate->release();
|
||||
}
|
||||
|
||||
QNetworkConnectionMonitor::QNetworkConnectionMonitor()
|
||||
: QObject(*new QNetworkConnectionMonitorPrivate)
|
||||
{
|
||||
@ -235,6 +279,8 @@ bool QNetworkConnectionMonitor::startMonitoring()
|
||||
|
||||
SCNetworkReachabilityContext context = {};
|
||||
context.info = d;
|
||||
context.retain = QNetworkConnectionMonitorPrivate::retainInfo;
|
||||
context.release = QNetworkConnectionMonitorPrivate::releaseInfo;
|
||||
if (!SCNetworkReachabilitySetCallback(d->probe, QNetworkConnectionMonitorPrivate::probeCallback, &context)) {
|
||||
qCWarning(lcNetMon, "Failed to set a reachability callback");
|
||||
return false;
|
||||
|
Loading…
x
Reference in New Issue
Block a user