Fix UBSAN warnings

Convert tagged pointer to an encapsulated tagged pointer type, and clean
up where it is used.

Change-Id: I9cf5b8d1dfa345eebaa185e44d762d5008769480
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Allan Sandfeld Jensen 2023-03-15 12:18:54 +01:00
parent f6d7065093
commit 0cab9b56e9
3 changed files with 39 additions and 29 deletions

View File

@ -369,16 +369,16 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
c->prevConnectionList->nextConnectionList.storeRelaxed(n);
c->prevConnectionList = nullptr;
Q_ASSERT(c != orphaned.loadRelaxed());
Q_ASSERT(c != static_cast<Connection *>(orphaned.load(std::memory_order_relaxed)));
// add c to orphanedConnections
Connection *o = nullptr;
TaggedSignalVector o = nullptr;
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
* matter if the tail changes.
*/
o = orphaned.load(std::memory_order_acquire);
do {
o = orphaned.loadRelaxed();
c->nextInOrphanList = o;
} while (!orphaned.testAndSetRelease(o, c));
} while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
#ifndef QT_NO_DEBUG
found = false;
@ -396,7 +396,7 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
{
QBasicMutex *senderMutex = signalSlotLock(sender);
ConnectionOrSignalVector *c = nullptr;
TaggedSignalVector c = nullptr;
{
std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
if (lockPolicy == NeedToLock)
@ -407,7 +407,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
// that nothing can reference the orphaned connection objects anymore and they can
// be safely deleted
c = orphaned.fetchAndStoreRelaxed(nullptr);
c = orphaned.exchange(nullptr, std::memory_order_relaxed);
}
if (c) {
// Deleting c might run arbitrary user code, so we must not hold the lock
@ -421,11 +421,11 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
}
}
inline void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o)
inline void QObjectPrivate::ConnectionData::deleteOrphaned(TaggedSignalVector o)
{
while (o) {
QObjectPrivate::ConnectionOrSignalVector *next = nullptr;
if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) {
TaggedSignalVector next = nullptr;
if (SignalVector *v = static_cast<SignalVector *>(o)) {
next = v->nextInOrphanList;
free(v);
} else {

View File

@ -111,6 +111,7 @@ public:
struct ConnectionOrSignalVector;
struct SignalVector;
struct Sender;
struct TaggedSignalVector;
/*
This contains the all connections from and to an object.

View File

@ -34,25 +34,35 @@ struct QObjectPrivate::ConnectionList
static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
struct QObjectPrivate::TaggedSignalVector
{
quintptr c;
TaggedSignalVector() = default;
TaggedSignalVector(std::nullptr_t) noexcept : c(0) {}
TaggedSignalVector(Connection *v) noexcept : c(reinterpret_cast<quintptr>(v)) { Q_ASSERT(v && (reinterpret_cast<quintptr>(v) & 0x1) == 0); }
TaggedSignalVector(SignalVector *v) noexcept : c(reinterpret_cast<quintptr>(v) | quintptr(1u)) { Q_ASSERT(v); }
explicit operator SignalVector *() const noexcept
{
if (c & 0x1)
return reinterpret_cast<SignalVector *>(c & ~quintptr(1u));
return nullptr;
}
explicit operator Connection *() const noexcept
{
return reinterpret_cast<Connection *>(c);
}
operator uintptr_t() const noexcept { return c; }
};
struct QObjectPrivate::ConnectionOrSignalVector
{
union {
// linked list of orphaned connections that need cleaning up
ConnectionOrSignalVector *nextInOrphanList;
TaggedSignalVector nextInOrphanList;
// linked list of connections connected to slots in this object
Connection *next;
};
static SignalVector *asSignalVector(ConnectionOrSignalVector *c)
{
if (reinterpret_cast<quintptr>(c) & 1)
return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));
return nullptr;
}
static Connection *fromSignalVector(SignalVector *v)
{
return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));
}
};
static_assert(std::is_trivial_v<QObjectPrivate::ConnectionOrSignalVector>);
@ -132,12 +142,12 @@ struct QObjectPrivate::ConnectionData
QAtomicPointer<SignalVector> signalVector;
Connection *senders = nullptr;
Sender *currentSender = nullptr; // object currently activating the object
QAtomicPointer<Connection> orphaned;
std::atomic<TaggedSignalVector> orphaned = {};
~ConnectionData()
{
Q_ASSERT(ref.loadRelaxed() == 0);
auto *c = orphaned.fetchAndStoreRelaxed(nullptr);
TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
if (c)
deleteOrphaned(c);
SignalVector *v = signalVector.loadRelaxed();
@ -159,7 +169,7 @@ struct QObjectPrivate::ConnectionData
};
void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
{
if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)
if (orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
cleanOrphanedConnectionsImpl(sender, lockPolicy);
}
void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
@ -194,15 +204,14 @@ struct QObjectPrivate::ConnectionData
signalVector.storeRelaxed(newVector);
if (vector) {
Connection *o = nullptr;
TaggedSignalVector o = nullptr;
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
* matter if the tail changes.
*/
o = orphaned.load(std::memory_order_acquire);
do {
o = orphaned.loadRelaxed();
vector->nextInOrphanList = o;
} while (!orphaned.testAndSetRelease(
o, ConnectionOrSignalVector::fromSignalVector(vector)));
} while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
}
}
int signalVectorCount() const
@ -210,7 +219,7 @@ struct QObjectPrivate::ConnectionData
return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
}
static void deleteOrphaned(ConnectionOrSignalVector *c);
static void deleteOrphaned(TaggedSignalVector o);
};
struct QObjectPrivate::Sender