mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-11-24 10:23:21 +00:00
Push and wait on WorkQueueThread items using PushBlocking. Previously we created a Common::Event sync_event on the caller's stack, called Wait on it, then had the WorkQueueThread call Set on the sync_event once the thread was done. In addition to being simpler the new way avoids a use-after-free that could happen in convoluted and unlikely yet possible thread scheduling sequences. One such case can be triggered as follows: * Set your audio backend to Cubeb * In CubebStream::SetVolume set a breakpoint at the call to Wait and at the call to cubeb_stream_set_volume. * Start a game. * Continue until the Cubeb Worker thread hits the cubeb_stream_set_volume breakpoint and Emuthread hits the Wait breakpoint, freezing each thread when it hits its breakpoint. * Unfreeze Cubeb Worker. * In Event::Set set a breakpoint at the end of the scope containing the lock_guard such that the guard has been constructed but not destructed when the breakpoint is hit. * Continue until that breakpoint is hit by Cubeb Worker. If other threads hit it first keep going. * Freeze Cubeb Worker. * For convenience remove the breakpoint in Event::Set so other threads don't trigger it. * In CubebStream::SetRunning set a breakpoint at the call to Wait. * Unfreeze Emuthread and continue until the breakpoint is hit. * In Cubeb Worker go to Event::Set and examine the values of m_mutex's member variables. In Visual Studio Debug these are locking_thread_id == 0xcccccc01 and ownership_levels == 0xcccccccc. This is the result of Visual Studio overwriting the memory used on the stack by sync_event in CubebStream::SetVolume with cc bytes to represent uninitialized memory on the stack (since that function already returned), and then allocating enough memory on the stack when calling AudioCommon::SetSoundStreamRunning and then CubebStream::SetRunning that it overwrote one byte of the memory formerly occupied by locking_thread_id. * If you unfreeze Cubeb Worker at this point it will trigger the lock guard's destructor which will then try to unlock m_mutex. Since m_mutex is no longer in scope this is a use-after-free, and in VS debug triggers a debug assert due to locking_thread_id not matching the current thread id. |
||
|---|---|---|
| .. | ||
| AlsaSoundStream.cpp | ||
| AlsaSoundStream.h | ||
| AudioCommon.cpp | ||
| AudioCommon.h | ||
| CMakeLists.txt | ||
| CubebStream.cpp | ||
| CubebStream.h | ||
| CubebUtils.cpp | ||
| CubebUtils.h | ||
| Enums.h | ||
| Mixer.cpp | ||
| Mixer.h | ||
| NullSoundStream.cpp | ||
| NullSoundStream.h | ||
| OpenALStream.cpp | ||
| OpenALStream.h | ||
| OpenSLESStream.cpp | ||
| OpenSLESStream.h | ||
| PulseAudioStream.cpp | ||
| PulseAudioStream.h | ||
| SoundStream.h | ||
| SurroundDecoder.cpp | ||
| SurroundDecoder.h | ||
| WASAPIStream.cpp | ||
| WASAPIStream.h | ||
| WaveFile.cpp | ||
| WaveFile.h | ||