FIX(server): Stale user pointers in whisper cache

Since the whisper cache entry was computed while holding the read-lock,
it was possible for a user in that cache to get deleted while the voice
thread drops the read lock in order to upgrade it to a write lock. In
such a case, the cache entry would contain an invalid (stale) user
pointer, which can lead to crashes when using the cache later on.

By moving the cache entry computation into the block in which we already
hold a read lock, we turn computing the cache and adding it to the cache
store into an atomic operation. As soon as the cache entry is in the
store, deleting a user is no longer an issue as that will implicitly
clear all whisper caches (thereby preventing stale cache entries).
This commit is contained in:
Robert Adam 2024-04-01 14:50:54 +02:00
parent d99bd47b28
commit e7000febd3

View File

@ -1230,6 +1230,14 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
} else {
ZoneScopedN(TracyConstants::AUDIO_WHISPER_CACHE_CREATE);
const unsigned int uiSession = u->uiSession;
qrwlVoiceThread.unlock();
qrwlVoiceThread.lockForWrite();
if (!qhUsers.contains(uiSession)) {
return;
}
const WhisperTarget &wt = u->qmTargets.value(static_cast< int >(audioData.targetOrContext));
if (!wt.qlChannels.isEmpty()) {
QMutexLocker qml(&qmCache);
@ -1302,13 +1310,9 @@ void Server::processMsg(ServerUser *u, Mumble::Protocol::AudioData audioData, Au
}
}
unsigned int uiSession = u->uiSession;
qrwlVoiceThread.unlock();
qrwlVoiceThread.lockForWrite();
u->qmTargetCache.insert(static_cast< int >(audioData.targetOrContext),
{ channel, direct, cachedListeners });
if (qhUsers.contains(uiSession))
u->qmTargetCache.insert(static_cast< int >(audioData.targetOrContext),
{ channel, direct, cachedListeners });
qrwlVoiceThread.unlock();
qrwlVoiceThread.lockForRead();
if (!qhUsers.contains(uiSession))