From 00f5f4fc9060a09494779499aecc968377e03485 Mon Sep 17 00:00:00 2001 From: Mark Pearce Date: Tue, 7 Jul 2026 11:02:00 +0800 Subject: [PATCH] libretro: guard output_audio_buffer with a mutex MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit System_AudioPushSamples() runs on the emu thread when one is active (GL/GLCore backends) and appends to / reallocs output_audio_buffer, while retro_run() on the libretro thread reads .data/.size and resets .size in upload_output_audio_buffer(), unsynchronized. A realloc on the emu thread can free the pointer while audio_batch_cb() reads it. Guard the four access sites with a mutex ( was already included in this file, unused). The lock is held across audio_batch_cb() — no second buffer; worst case the emu thread briefly backpressures while the frontend syncs audio. Behavior is otherwise unchanged. --- libretro/libretro.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libretro/libretro.cpp b/libretro/libretro.cpp index 8bc85d61b9..3d0362e7dd 100644 --- a/libretro/libretro.cpp +++ b/libretro/libretro.cpp @@ -73,6 +73,12 @@ static struct { int32_t size; int32_t capacity; } output_audio_buffer = {NULL, 0, 0}; +// output_audio_buffer is filled by System_AudioPushSamples() on the emu +// thread (used with the GL backends, where PSP-side audio pushes arrive +// via the GL command queue) and drained + realloc'd on the libretro +// thread in retro_run()/init/shutdown — every access must hold this +// mutex or a realloc can free the pointer mid-read on the other thread. +static std::mutex output_audio_buffer_mutex; // Calculated swap interval is 'stable' if the same // value is recorded for a number of retro_run() @@ -123,6 +129,7 @@ namespace Libretro static float runSpeed = 0.0f; static s64 runTicksLast = 0; + // Must be called with output_audio_buffer_mutex held. static void ensure_output_audio_buffer_capacity(int32_t capacity) { if (capacity <= output_audio_buffer.capacity) { @@ -136,6 +143,7 @@ namespace Libretro static void init_output_audio_buffer(int32_t capacity) { + std::lock_guard lock(output_audio_buffer_mutex); output_audio_buffer.data = NULL; output_audio_buffer.size = 0; output_audio_buffer.capacity = 0; @@ -144,6 +152,7 @@ namespace Libretro static void free_output_audio_buffer() { + std::lock_guard lock(output_audio_buffer_mutex); free(output_audio_buffer.data); output_audio_buffer.data = NULL; output_audio_buffer.size = 0; @@ -152,6 +161,7 @@ namespace Libretro static void upload_output_audio_buffer() { + std::lock_guard lock(output_audio_buffer_mutex); audio_batch_cb(output_audio_buffer.data, output_audio_buffer.size / 2); output_audio_buffer.size = 0; } @@ -2022,6 +2032,8 @@ inline int16_t Clamp16(int32_t sample) { void System_AudioPushSamples(const int32_t *audio, int numSamples, float volume) { // We ignore volume here, because it's handled by libretro presumably. + std::lock_guard lock(output_audio_buffer_mutex); + // Convert to 16-bit audio for further processing. int16_t buffer[1024 * 2]; int origSamples = numSamples * 2;