SDL_mixer: improve ParseFMT

From e2a8ea1f4234c7b6e6f8056578703b8c4c664c09 Mon Sep 17 00:00:00 2001
From: pionere <[EMAIL REDACTED]>
Date: Wed, 15 Dec 2021 10:34:11 +0100
Subject: [PATCH] improve ParseFMT - ensure the structs used in ParseFMT are
 packed without padding - use stack to load the format information - ensure
 ParseFMT does not read outside of the allocated memory in case of EXT_CODE

---
 src/codecs/music_wav.c | 50 +++++++++++++++++++++++-------------------
 1 file changed, 27 insertions(+), 23 deletions(-)

diff --git a/src/codecs/music_wav.c b/src/codecs/music_wav.c
index 8150188c..340d8550 100644
--- a/src/codecs/music_wav.c
+++ b/src/codecs/music_wav.c
@@ -79,6 +79,7 @@ typedef struct {
 #define WAVE_MONO   1
 #define WAVE_STEREO 2
 
+#pragma pack(push, 1)
 typedef struct {
 /* Not saved in the chunk we read:
     Uint32  chunkID;
@@ -90,7 +91,7 @@ typedef struct {
     Uint32  byterate;       /* Average bytes per second */
     Uint16  blockalign;     /* Bytes per sample block */
     Uint16  bitspersample;      /* One of 8, 12, 16, or 4 for ADPCM */
-} WaveFMT;
+} WaveFMTHeader;
 
 typedef struct {
     Uint16  cbSize;
@@ -107,6 +108,11 @@ typedef struct {
     Uint8  sub_data[8];
 } WaveFMTex;
 
+typedef struct {
+    WaveFMTHeader format;
+    WaveFMTex formatEx;
+} _WaveFMT;
+
 typedef struct {
     Uint32 identifier;
     Uint32 type;
@@ -132,6 +138,7 @@ typedef struct {
     Uint32  sampler_data;
     SampleLoop loops[1];
 } SamplerChunk;
+#pragma pack(pop)
 
 /*********************************************/
 /* Define values for AIFF (IFF audio) format */
@@ -632,33 +639,34 @@ static void WAV_Delete(void *context)
 static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length)
 {
     SDL_AudioSpec *spec = &wave->spec;
-    WaveFMT *format;
-    WaveFMTex *formatEx = NULL;
+    _WaveFMT fmt;
     Uint8 *data;
     int bits;
-    SDL_bool loaded = SDL_FALSE;
 
-    if (chunk_length < sizeof(*format)) {
+    if (chunk_length < sizeof(fmt.format)) {
         Mix_SetError("Wave format chunk too small");
         return SDL_FALSE;
     }
 
-    data = (Uint8 *)SDL_malloc(chunk_length);
-    if (!data) {
-        Mix_SetError("Out of memory");
+    bits = chunk_length >= sizeof(fmt) ? sizeof(fmt) : sizeof(fmt.format);
+    if (!SDL_RWread(wave->src, &fmt, bits, 1)) {
+        Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
         return SDL_FALSE;
     }
-    if (!SDL_RWread(wave->src, data, chunk_length, 1)) {
+    chunk_length -= bits;
+    if (chunk_length != 0 && !SDL_RWseek(wave->src, chunk_length, RW_SEEK_CUR)) {
         Mix_SetError("Couldn't read %d bytes from WAV file", chunk_length);
-        goto done;
+        return SDL_FALSE;
     }
-    format = (WaveFMT *)data;
 
-    wave->encoding = SDL_SwapLE16(format->encoding);
+    wave->encoding = SDL_SwapLE16(fmt.format.encoding);
 
     if (wave->encoding == EXT_CODE) {
-        formatEx = (WaveFMTex*)(data + sizeof(WaveFMT));
-        wave->encoding = (Uint16)SDL_SwapLE32(formatEx->subencoding);
+        if (bits < sizeof(fmt)) {
+            Mix_SetError("Wave format chunk too small");
+            return SDL_FALSE;
+        }
+        wave->encoding = (Uint16)SDL_SwapLE32(fmt.formatEx.subencoding);
     }
 
     /* Decode the audio data format */
@@ -681,8 +689,8 @@ static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length)
             Mix_SetError("Unknown WAVE data format");
             goto done;
     }
-    spec->freq = (int)SDL_SwapLE32(format->frequency);
-    bits = (int) SDL_SwapLE16(format->bitspersample);
+    spec->freq = (int)SDL_SwapLE32(fmt.format.frequency);
+    bits = (int) SDL_SwapLE16(fmt.format.bitspersample);
     switch (bits) {
         case 8:
             switch(wave->encoding) {
@@ -726,9 +734,9 @@ static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length)
         default:
             unknown_bits:
             Mix_SetError("Unknown PCM format with %d bits", bits);
-            goto done;
+            return SDL_FALSE;
     }
-    spec->channels = (Uint8) SDL_SwapLE16(format->channels);
+    spec->channels = (Uint8) SDL_SwapLE16(fmt.format.channels);
     spec->samples = 4096;       /* Good default buffer size */
     wave->samplesize = spec->channels * (bits / 8);
     /* SDL_CalculateAudioSpec */
@@ -736,11 +744,7 @@ static SDL_bool ParseFMT(WAV_Music *wave, Uint32 chunk_length)
     spec->size *= spec->channels;
     spec->size *= spec->samples;
 
-    loaded = SDL_TRUE;
-
-done:
-    SDL_free(data);
-    return loaded;
+    return SDL_TRUE;
 }
 
 static SDL_bool ParseDATA(WAV_Music *wave, Uint32 chunk_length)