SDL_mixer: fix memory leak in Mix_FreeChunk + race condition in Mix_HaltChannel

From 5d3c84e5dd4c91093255ad5533b8457ecc40684f Mon Sep 17 00:00:00 2001
From: pionere <[EMAIL REDACTED]>
Date: Thu, 23 Dec 2021 12:01:03 +0100
Subject: [PATCH] fix memory leak in Mix_FreeChunk + race condition in
 Mix_HaltChannel

---
 src/mixer.c | 33 +++++++++++++++++++--------------
 1 file changed, 19 insertions(+), 14 deletions(-)

diff --git a/src/mixer.c b/src/mixer.c
index 4eff625e..133c7a20 100644
--- a/src/mixer.c
+++ b/src/mixer.c
@@ -834,6 +834,20 @@ Mix_Chunk *Mix_QuickLoad_RAW(Uint8 *mem, Uint32 len)
     return(chunk);
 }
 
+/* MAKE SURE you hold the audio lock (Mix_LockAudio()) before calling this! */
+static void  Mix_HaltChannel_locked(int which)
+{
+    if (Mix_Playing(which)) {
+        _Mix_channel_done_playing(which);
+        mix_channel[which].playing = 0;
+        mix_channel[which].looping = 0;
+    }
+    mix_channel[which].expire = 0;
+    if (mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
+        mix_channel[which].volume = mix_channel[which].fade_volume_reset;
+    mix_channel[which].fading = MIX_NO_FADING;
+}
+
 /* Free an audio chunk previously loaded */
 void Mix_FreeChunk(Mix_Chunk *chunk)
 {
@@ -846,8 +860,7 @@ void Mix_FreeChunk(Mix_Chunk *chunk)
         if (mix_channel) {
             for (i=0; i<num_channels; ++i) {
                 if (chunk == mix_channel[i].chunk) {
-                    mix_channel[i].playing = 0;
-                    mix_channel[i].looping = 0;
+                    Mix_HaltChannel_locked(i);
                 }
             }
         }
@@ -1106,23 +1119,15 @@ int Mix_HaltChannel(int which)
 {
     int i;
 
+    Mix_LockAudio();
     if (which == -1) {
         for (i=0; i<num_channels; ++i) {
-            Mix_HaltChannel(i);
+            Mix_HaltChannel_locked(i);
         }
     } else if (which < num_channels) {
-        Mix_LockAudio();
-        if (Mix_Playing(which)) {
-            _Mix_channel_done_playing(which);
-            mix_channel[which].playing = 0;
-            mix_channel[which].looping = 0;
-        }
-        mix_channel[which].expire = 0;
-        if (mix_channel[which].fading != MIX_NO_FADING) /* Restore volume */
-            mix_channel[which].volume = mix_channel[which].fade_volume_reset;
-        mix_channel[which].fading = MIX_NO_FADING;
-        Mix_UnlockAudio();
+        Mix_HaltChannel_locked(which);
     }
+    Mix_UnlockAudio();
     return(0);
 }