QDesktopServices: fix ABA problem in QOpenUrlHandlerRegistry

The registry is designed to be thread-safe, as is openUrl(), and
handlers are required to be thread-safe, too: "the handler will
always be called from within the same thread that calls
QDesktopServices::openUrl()".

The handlerDestroyed() slot was invoked using AutoConnection
semantics, which, if the registry and the handler happen to not be on
the same thread, is QueuedConnection, which means there's a window in
which the handler is destroyed, but it's still listed in the registry,
and a call to openUrl() may make a call to the (deleted) handler.

Worse, if a handler is deleted and new one registered they may end up
on the same address (ABA, or rather, AA problem), and then the delayed
call to handlerDestroyed() may remove the entries to the new handler,
as well as those to the old.

Fix by using Qt::DirectConnection. This fixes the ABA problem, but
doesn't completely fix the partially-destroyed problem, since
QObject::destroyed() is simply too late. We need to require explicit
unsetUrlHandler() calls, which should happen before destruction of the
handler object starts.

[ChangeLog][QtGui][QDesktopServices] Fixed a bug where destroying and
re-creating a handler object in quick succession could cause the
registration for the handler to be lost.

Fixes: QTBUG-100778
Pick-to: 6.3 6.2 5.15
Change-Id: I76fd81724504cc7f38ac262b43e3e9539fe5ebca
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2022-02-11 09:56:04 +01:00
parent 42e13b7c61
commit f482e2a456

View File

@ -324,7 +324,8 @@ void QDesktopServices::setUrlHandler(const QString &scheme, QObject *receiver, c
h.name = method;
registry->handlers.insert(scheme.toLower(), h);
QObject::connect(receiver, SIGNAL(destroyed(QObject*)),
registry, SLOT(handlerDestroyed(QObject*)));
registry, SLOT(handlerDestroyed(QObject*)),
Qt::DirectConnection);
}
/*!