Browse Source

chatgui: fix sound_manager use-after-free crash on Windows

- Keep mp3Data + ma_decoder alive for sound lifetime (was freed before audio thread)
- Remove MA_SOUND_FLAG_DECODE — stream from decoder instead of pre-decoding
- Shutdown in reverse order: sound → decoder → mp3Data
topo_upd
Evgeny 2 months ago
parent
commit
1593079da8
  1. 27
      tools/chatgui/src/sound_manager.cpp
  2. 4
      tools/chatgui/src/sound_manager.h

27
tools/chatgui/src/sound_manager.cpp

@ -35,6 +35,10 @@ void SoundManager::shutdown() {
ma_sound_uninit(it->sound); ma_sound_uninit(it->sound);
delete it->sound; delete it->sound;
} }
if (it->decoder) {
ma_decoder_uninit(it->decoder);
delete it->decoder;
}
} }
m_sounds.clear(); m_sounds.clear();
@ -81,33 +85,36 @@ bool SoundManager::loadSound(const QString& name, const QString& qrcPath) {
qWarning("SoundManager: cannot open %s", qPrintable(qrcPath)); qWarning("SoundManager: cannot open %s", qPrintable(qrcPath));
return false; return false;
} }
QByteArray mp3Data = f.readAll();
LoadedSound ls;
ls.mp3Data = f.readAll();
f.close(); f.close();
ls.decoder = new ma_decoder;
ma_decoder_config decConfig = ma_decoder_config_init_default(); ma_decoder_config decConfig = ma_decoder_config_init_default();
ma_decoder decoder; ma_result result = ma_decoder_init_memory(ls.mp3Data.constData(), ls.mp3Data.size(),
ma_result result = ma_decoder_init_memory(mp3Data.constData(), mp3Data.size(), &decConfig, &decoder); &decConfig, ls.decoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
qWarning("SoundManager: ma_decoder_init_memory failed for %s (%d)", qPrintable(name), result); qWarning("SoundManager: ma_decoder_init_memory failed for %s (%d)", qPrintable(name), result);
delete ls.decoder;
return false; return false;
} }
LoadedSound ls;
ls.sound = new ma_sound; ls.sound = new ma_sound;
result = ma_sound_init_from_data_source(m_engine, (ma_data_source*)&decoder.ds, result = ma_sound_init_from_data_source(m_engine, (ma_data_source*)&ls.decoder->ds,
MA_SOUND_FLAG_DECODE, 0, NULL, ls.sound);
NULL, ls.sound);
ma_decoder_uninit(&decoder);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
qWarning("SoundManager: sound init failed for %s (%d)", qPrintable(name), result); qWarning("SoundManager: sound init failed for %s (%d)", qPrintable(name), result);
ma_decoder_uninit(ls.decoder);
delete ls.decoder;
delete ls.sound; delete ls.sound;
return false; return false;
} }
ma_sound_set_volume(ls.sound, m_volume);
m_sounds.insert(name, ls); m_sounds.insert(name, ls);
qDebug("SoundManager: loaded '%s' from %s (%lld bytes)", qDebug("SoundManager: loaded '%s' from %s (%lld bytes)",
qPrintable(name), qPrintable(qrcPath), (long long)mp3Data.size()); qPrintable(name), qPrintable(qrcPath), (long long)ls.mp3Data.size());
return true; return true;
} }

4
tools/chatgui/src/sound_manager.h

@ -3,8 +3,10 @@
#include <QObject> #include <QObject>
#include <QHash> #include <QHash>
#include <QString> #include <QString>
#include <QByteArray>
struct ma_engine; struct ma_engine;
struct ma_decoder;
struct ma_sound; struct ma_sound;
class SoundManager : public QObject { class SoundManager : public QObject {
@ -28,6 +30,8 @@ private:
~SoundManager(); ~SoundManager();
struct LoadedSound { struct LoadedSound {
QByteArray mp3Data;
ma_decoder* decoder = nullptr;
ma_sound* sound = nullptr; ma_sound* sound = nullptr;
}; };

Loading…
Cancel
Save