SDL_mixer: timidity: handle malloc() failures. revise library init.

From 039087a2dde402039312092f7e498f9997aadcd5 Mon Sep 17 00:00:00 2001
From: Ozkan Sezer <[EMAIL REDACTED]>
Date: Wed, 17 Mar 2021 23:56:50 +0300
Subject: [PATCH] timidity: handle malloc() failures. revise library init.

---
 src/codecs/timidity/common.c   |  10 ++-
 src/codecs/timidity/common.h   |   2 +-
 src/codecs/timidity/instrum.c  |  92 ++++++++++++++-----------
 src/codecs/timidity/readmidi.c |  16 +++++
 src/codecs/timidity/resample.c |   4 +-
 src/codecs/timidity/timidity.c | 121 +++++++++++++++++++++++++--------
 src/codecs/timidity/timidity.h |   1 +
 7 files changed, 169 insertions(+), 77 deletions(-)

diff --git a/src/codecs/timidity/common.c b/src/codecs/timidity/common.c
index 527787f..2798284 100644
--- a/src/codecs/timidity/common.c
+++ b/src/codecs/timidity/common.c
@@ -81,22 +81,20 @@ SDL_RWops *open_file(const char *name)
 }
 
 /* This adds a directory to the path list */
-void add_to_pathlist(const char *s, size_t l)
+int add_to_pathlist(const char *s, size_t l)
 {
   PathList *plp = SDL_malloc(sizeof(PathList));
-
-  if (plp == NULL)
-      return;
-
+  if (plp == NULL) return -2;
   plp->path = SDL_malloc(l + 1);
   if (plp->path == NULL) {
       SDL_free (plp);
-      return;
+      return -2;
   }
   SDL_memcpy(plp->path, s, l);
   plp->path[l] = 0;
   plp->next = pathlist;
   pathlist = plp;
+  return 0;
 }
 
 void free_pathlist(void)
diff --git a/src/codecs/timidity/common.h b/src/codecs/timidity/common.h
index f6faa9f..46e68db 100644
--- a/src/codecs/timidity/common.h
+++ b/src/codecs/timidity/common.h
@@ -9,5 +9,5 @@
 */
 
 extern SDL_RWops *open_file(const char *name);
-extern void add_to_pathlist(const char *s, size_t len);
+extern int add_to_pathlist(const char *s, size_t len);
 extern void free_pathlist(void);
diff --git a/src/codecs/timidity/instrum.c b/src/codecs/timidity/instrum.c
index 36da50a..0400199 100644
--- a/src/codecs/timidity/instrum.c
+++ b/src/codecs/timidity/instrum.c
@@ -26,12 +26,13 @@ static void free_instrument(Instrument *ip)
   Sample *sp;
   int i;
   if (!ip) return;
-  for (i=0; i<ip->samples; i++)
-    {
+  if (ip->sample) {
+    for (i=0; i<ip->samples; i++) {
       sp=&(ip->sample[i]);
       SDL_free(sp->data);
     }
-  SDL_free(ip->sample);
+    SDL_free(ip->sample);
+  }
   SDL_free(ip);
 }
 
@@ -42,7 +43,6 @@ static void free_bank(MidiSong *song, int dr, int b)
   for (i=0; i<MAXBANK; i++)
     if (bank->instrument[i])
       {
-	/* Not that this could ever happen, of course */
 	if (bank->instrument[i] != MAGIC_LOAD_INSTRUMENT)
 	  free_instrument(bank->instrument[i]);
 	bank->instrument[i]=0;
@@ -141,7 +141,8 @@ static void reverse_data(Sint16 *sp, Sint32 ls, Sint32 le)
    undefined.
 
    TODO: do reverse loops right */
-static Instrument *load_instrument(MidiSong *song, const char *name,
+static void load_instrument(MidiSong *song, const char *name,
+				   Instrument **out,
 				   int percussion, int panning,
 				   int amp, int note_to_use,
 				   int strip_loop, int strip_envelope,
@@ -155,7 +156,8 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
   static char *patch_ext[] = PATCH_EXT_LIST;
 
   (void)percussion; /* unused */
-  if (!name) return 0;
+  *out = NULL;
+  if (!name) return;
 
   /* Open patch file */
   if ((rw=open_file(name)) == NULL)
@@ -172,7 +174,7 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
   if (rw == NULL)
     {
       SNDDBG(("Instrument `%s' can't be found.\n", name));
-      return 0;
+      return;
     }
 
   SNDDBG(("Loading instrument %s\n", tmp));
@@ -186,28 +188,30 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
 						      differences are */
     {
       SNDDBG(("%s: not an instrument\n", name));
-      SDL_RWclose(rw);
-      return 0;
+      goto badpat;
     }
 
   if (tmp[82] != 1 && tmp[82] != 0) /* instruments. To some patch makers,
 				       0 means 1 */
     {
       SNDDBG(("Can't handle patches with %d instruments\n", tmp[82]));
-      SDL_RWclose(rw);
-      return 0;
+      goto badpat;
     }
 
   if (tmp[151] != 1 && tmp[151] != 0) /* layers. What's a layer? */
     {
       SNDDBG(("Can't handle instruments with %d layers\n", tmp[151]));
-      SDL_RWclose(rw);
-      return 0;
+      goto badpat;
     }
 
-  ip=SDL_malloc(sizeof(Instrument));
+  *out=SDL_malloc(sizeof(Instrument));
+  ip = *out;
+  if (!ip) goto nomem;
+
   ip->samples = tmp[198];
   ip->sample = SDL_malloc(sizeof(Sample) * ip->samples);
+  if (!ip->sample) goto nomem;
+
   for (i=0; i<ip->samples; i++)
     {
       Uint8 fractions;
@@ -216,28 +220,19 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
       Uint8 tmpchar;
 
 #define READ_CHAR(thing)					\
-  if (1 != SDL_RWread(rw, &tmpchar, 1, 1))  goto fail;		\
+  if (1 != SDL_RWread(rw, &tmpchar, 1, 1))  goto badread;	\
   thing = tmpchar;
 #define READ_SHORT(thing)					\
-  if (1 != SDL_RWread(rw, &tmpshort, 2, 1)) goto fail;		\
+  if (1 != SDL_RWread(rw, &tmpshort, 2, 1)) goto badread;	\
   thing = SDL_SwapLE16(tmpshort);
 #define READ_LONG(thing)					\
-  if (1 != SDL_RWread(rw, &tmplong, 4, 1)) g oto fail;		\
+  if (1 != SDL_RWread(rw, &tmplong, 4, 1))  goto badread;	\
   thing = (Sint32)SDL_SwapLE32((Uint32)tmplong);
 
       SDL_RWseek(rw, 7, RW_SEEK_CUR); /* Skip the wave name */
 
       if (1 != SDL_RWread(rw, &fractions, 1, 1))
-	{
-	fail:
-	  SNDDBG(("Error reading sample %d\n", i));
-	  for (j=0; j<i; j++)
-	    SDL_free(ip->sample[j].data);
-	  SDL_free(ip->sample);
-	  SDL_free(ip);
-	  SDL_RWclose(rw);
-	  return 0;
-	}
+	goto badread;
 
       sp=&(ip->sample[i]);
 
@@ -259,7 +254,8 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
 	sp->panning=(Uint8)(panning & 0x7F);
 
       /* envelope, tremolo, and vibrato */
-      if (18 != SDL_RWread(rw, tmp, 1, 18)) goto fail; 
+      if (18 != SDL_RWread(rw, tmp, 1, 18))
+	goto badread;
 
       if (!tmp[13] || !tmp[14])
 	{
@@ -369,8 +365,10 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
 
       /* Then read the sample data */
       sp->data = (sample_t *) SDL_malloc(sp->data_length+4);
+      if (!sp->data) goto nomem;
+
       if (1 != SDL_RWread(rw, sp->data, sp->data_length, 1))
-	goto fail;
+	goto badread;
 
       if (!(sp->modes & MODES_16BIT)) /* convert to 16-bit data */
 	{
@@ -381,6 +379,7 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
 	  sp->loop_start *= 2;
 	  sp->loop_end *= 2;
 	  tmp16 = new16 = (Uint16 *) SDL_malloc(sp->data_length+4);
+	  if (!new16) goto nomem;
 	  while (k--)
 	    *tmp16++ = (Uint16)(*cp++) << 8;
 	  SDL_free(sp->data);
@@ -476,8 +475,10 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
 
       /* If this instrument will always be played on the same note,
 	 and it's not looped, we can resample it now. */
-      if (sp->note_to_use && !(sp->modes & MODES_LOOPING))
+      if (sp->note_to_use && !(sp->modes & MODES_LOOPING)) {
 	pre_resample(song, sp);
+	if (song->oom) goto fail;
+      }
 
       if (strip_tail==1)
 	{
@@ -488,7 +489,18 @@ static Instrument *load_instrument(MidiSong *song, const char *name,
     }
 
   SDL_RWclose(rw);
-  return ip;
+  return;
+
+nomem:
+  song->oom=1;
+  goto fail;
+badread:
+  SNDDBG(("Error reading sample %d\n", i));
+fail:
+  free_instrument (ip);
+badpat:
+  SDL_RWclose(rw);
+  *out = NULL;
 }
 
 static int fill_bank(MidiSong *song, int dr, int b)
@@ -530,9 +542,11 @@ static int fill_bank(MidiSong *song, int dr, int b)
 	      bank->instrument[i] = 0;
 	      errors++;
 	    }
-	  else if (!(bank->instrument[i] =
-		     load_instrument(song,
+	  else
+	  {
+		load_instrument(song,
 				     bank->tone[i].name, 
+				     &bank->instrument[i],
 				     (dr) ? 1 : 0,
 				     bank->tone[i].pan,
 				     bank->tone[i].amp,
@@ -545,13 +559,14 @@ static int fill_bank(MidiSong *song, int dr, int b)
 				     (bank->tone[i].strip_envelope != -1) ? 
 				     bank->tone[i].strip_envelope :
 				     ((dr) ? 1 : -1),
-				     bank->tone[i].strip_tail )))
-	    {
-	      SNDDBG(("Couldn't load instrument %s (%s %d, program %d)\n",
+				     bank->tone[i].strip_tail);
+	    if (!bank->instrument[i]) {
+		SNDDBG(("Couldn't load instrument %s (%s %d, program %d)\n",
 		   bank->tone[i].name,
 		   (dr)? "drum set" : "tone bank", b, i));
 	      errors++;
 	    }
+	  }
 	}
     }
   return errors;
@@ -584,10 +599,9 @@ void free_instruments(MidiSong *song)
 
 int set_default_instrument(MidiSong *song, const char *name)
 {
-  Instrument *ip;
-  if (!(ip=load_instrument(song, name, 0, -1, -1, -1, 0, 0, 0)))
+  load_instrument(song, name, &song->default_instrument, 0, -1, -1, -1, 0, 0, 0);
+  if (!song->default_instrument)
     return -1;
-  song->default_instrument = ip;
   song->default_program = SPECIAL_PROGRAM;
   return 0;
 }
diff --git a/src/codecs/timidity/readmidi.c b/src/codecs/timidity/readmidi.c
index 237e60b..03d6e12 100644
--- a/src/codecs/timidity/readmidi.c
+++ b/src/codecs/timidity/readmidi.c
@@ -53,11 +53,17 @@ static int dumpstring(SDL_RWops *rw, Sint32 len, Uint8 type)
     "Text event: ", "Text: ", "Copyright: ", "Track name: ",
     "Instrument: ", "Lyric: ", "Marker: ", "Cue point: " };
   signed char *s = SDL_malloc(len+1);
+  if (!s)
+    {
+      SDL_RWseek(song->rw, len, RW_SEEK_CUR);/* should I ? */
+      return -1;
+    }
   if (len != (Sint32) SDL_RWread(rw, s, 1, len))
     {
       SDL_free(s);
       return -1;
     }
+
   s[len]='\0';
   while (len--)
     {
@@ -72,6 +78,7 @@ static int dumpstring(SDL_RWops *rw, Sint32 len, Uint8 type)
 
 #define MIDIEVENT(at,t,ch,pa,pb)				\
   newlist = (MidiEventList *) SDL_malloc(sizeof(MidiEventList));\
+  if (!newlist) {song->oom = 1; return NULL;}			\
   newlist->event.time = at;					\
   newlist->event.type = t;					\
   newlist->event.channel = ch;					\
@@ -364,6 +371,11 @@ static MidiEvent *groom_list(MidiSong *song, Sint32 divisions,Sint32 *eventsp,
 
   /* This may allocate a bit more than we need */
   groomed_list=lp=SDL_malloc(sizeof(MidiEvent) * (song->event_count+1));
+  if (!groomed_list) {
+    song->oom=1;
+    free_midi_list(song);
+    return NULL;
+  }
   meep=song->evlist;
 
   our_event_count=0;
@@ -579,6 +591,10 @@ MidiEvent *read_midi_file(MidiSong *song, Sint32 *count, Sint32 *sp)
 
   /* Put a do-nothing event first in the list for easier processing */
   song->evlist=SDL_calloc(1, sizeof(MidiEventList));
+  if (!song->evlist) {
+    song->oom=1;
+    return NULL;
+  }
   song->event_count++;
 
   switch(format)
diff --git a/src/codecs/timidity/resample.c b/src/codecs/timidity/resample.c
index 7fd9ac3..4372ca6 100644
--- a/src/codecs/timidity/resample.c
+++ b/src/codecs/timidity/resample.c
@@ -552,8 +552,10 @@ void pre_resample(MidiSong *song, Sample *sp)
   }
 
   dest = newdata = (Sint16 *) SDL_malloc((newlen >> (FRACTION_BITS - 1)) + 2);
-  if (!dest)
+  if (!dest) {
+    song->oom = 1;
     return;
+  }
 
   if (--count)
     *dest++ = src[0];
diff --git a/src/codecs/timidity/timidity.c b/src/codecs/timidity/timidity.c
index cddeabc..fe16e9c 100644
--- a/src/codecs/timidity/timidity.c
+++ b/src/codecs/timidity/timidity.c
@@ -189,7 +189,8 @@ static int read_config_file(const char *name, int rcf_count)
 	goto fail;
       }
       for (i=1; i<words; i++) {
-	add_to_pathlist(w[i], SDL_strlen(w[i]));
+	if (add_to_pathlist(w[i], SDL_strlen(w[i])) < 0)
+	  goto fail;
       }
     }
     else if (!SDL_strcmp(w[0], "source"))
@@ -234,7 +235,9 @@ static int read_config_file(const char *name, int rcf_count)
       if (!master_drumset[i])
       {
 	master_drumset[i] = SDL_calloc(1, sizeof(ToneBank));
+	if (!master_drumset[i]) goto fail;
 	master_drumset[i]->tone = SDL_calloc(128, sizeof(ToneBankElement));
+	if (!master_drumset[i]->tone) goto fail;
       }
       bank=master_drumset[i];
     }
@@ -255,7 +258,9 @@ static int read_config_file(const char *name, int rcf_count)
       if (!master_tonebank[i])
       {
 	master_tonebank[i] = SDL_calloc(1, sizeof(ToneBank));
+	if (!master_tonebank[i]) goto fail;
 	master_tonebank[i]->tone = SDL_calloc(128, sizeof(ToneBankElement));
+	if (!master_tonebank[i]->tone) goto fail;
       }
       bank=master_tonebank[i];
     }
@@ -283,6 +288,7 @@ static int read_config_file(const char *name, int rcf_count)
       SDL_free(bank->tone[i].name);
       sz = SDL_strlen(w[1])+1;
       bank->tone[i].name = SDL_malloc(sz);
+      if (!bank->tone[i].name) goto fail;
       SDL_memcpy(bank->tone[i].name,w[1],sz);
       bank->tone[i].note=bank->tone[i].amp=bank->tone[i].pan=
       bank->tone[i].strip_loop=bank->tone[i].strip_envelope=
@@ -378,62 +384,106 @@ static int read_config_file(const char *name, int rcf_count)
   return r;
 }
 
-int Timidity_Init_NoConfig(void)
+#if defined(_WIN32)||defined(__CYGWIN__)||defined(__OS2__)
+/* FIXME: What about C:FOO ? */
+static SDL_INLINE char *get_last_dirsep (const char *p) {
+  char *p1 = SDL_strrchr(p, '/');
+  char *p2 = SDL_strrchr(p, '\\');
+  if (!p1) return p2;
+  if (!p2) return p1;
+  return (p1 > p2)? p1 : p2;
+}
+#else /* assumed UNIX-ish : */
+static SDL_INLINE char *get_last_dirsep (const char *p) {
+  return SDL_strrchr(p, '/');
+}
+#endif
+
+static int init_alloc_banks(void)
 {
   /* Allocate memory for the standard tonebank and drumset */
   master_tonebank[0] = SDL_calloc(1, sizeof(ToneBank));
+  if (!master_tonebank[0]) goto _nomem;
   master_tonebank[0]->tone = SDL_calloc(128, sizeof(ToneBankElement));
+  if (!master_tonebank[0]->tone) goto _nomem;
 
   master_drumset[0] = SDL_calloc(1, sizeof(ToneBank));
+  if (!master_drumset[0]) goto _nomem;
   master_drumset[0]->tone = SDL_calloc(128, sizeof(ToneBankElement));
+  if (!master_drumset[0]->tone) goto _nomem;
 
   return 0;
+_nomem:
+  SNDDBG(("Out of memory\n"));
+  Timidity_Exit ();
+  return -2;
 }
 
-int Timidity_Init(const char *config_file)
+static int init_begin_config(const char *cf)
 {
-  const char *p;
-
-  Timidity_Init_NoConfig();
+  const char *p = get_last_dirsep(cf);
+  if (p != NULL)
+      return add_to_pathlist(cf, p - cf + 1); /* including DIRSEP */
+  return 0;
+}
 
-  if (config_file == NULL || *config_file == '\0')
-      config_file = TIMIDITY_CFG;
+static int init_with_config(const char *cf)
+{
+  int rc = init_begin_config(cf);
+  if (rc != 0) {
+      Timidity_Exit ();
+      return rc;
+  }
+  rc = read_config_file(cf, 0);
+  if (rc != 0) {
+      Timidity_Exit ();
+  }
+  return rc;
+}
 
-  p = SDL_strrchr(config_file, '/');
-#if defined(__WIN32__)||defined(__OS2__)
-  if (!p) p = SDL_strrchr(config_file, '\\');
-#endif
-  if (p != NULL)
-    add_to_pathlist(config_file, p - config_file + 1);
+int Timidity_Init_NoConfig(void)
+{
+  master_tonebank[0] = NULL;
+  master_drumset[0] = NULL;
+  return init_alloc_banks();
+}
 
-  if (read_config_file(config_file, 0) < 0) {
-      Timidity_Exit();
-      return -1;
+int Timidity_Init(const char *config_file)
+{
+  int rc = Timidity_Init_NoConfig();
+  if (rc != 0) {
+      return rc;
   }
-  return 0;
+  if (config_file == NULL || *config_file == '\0') {
+      return init_with_config(TIMIDITY_CFG);
+  }
+  return init_with_config(config_file);
 }
 
-MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
+static void do_song_load(SDL_RWops *rw, SDL_AudioSpec *audio, MidiSong **out)
 {
   MidiSong *song;
   int i;
 
+  *out = NULL;
   if (rw == NULL)
-      return NULL;
+      return;
 
   /* Allocate memory for the song */
   song = (MidiSong *)SDL_calloc(1, sizeof(*song));
   if (song == NULL)
-      return NULL;
+      return;
 
   for (i = 0; i < MAXBANK; i++)
   {
     if (master_tonebank[i]) {
       song->tonebank[i] = SDL_calloc(1, sizeof(ToneBank));
+      if (!song->tonebank[i]) goto fail;
       song->tonebank[i]->tone = master_tonebank[i]->tone;
     }
     if (master_drumset[i]) {
       song->drumset[i] = SDL_calloc(1, sizeof(ToneBank));
+      if (!song->drumset[i]) goto fail;
       song->drumset[i]->tone = master_drumset[i]->tone;
     }
   }
@@ -456,8 +506,7 @@ MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
       song->encoding |= PE_MONO;
   else if (audio->channels > 2) {
       SDL_SetError("Surround sound not supported");
-      SDL_free(song);
-      return NULL;
+      goto fail;
   }
   switch (audio->format) {
   case AUDIO_S8:
@@ -489,13 +538,14 @@ MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
     break;
   default:
     SDL_SetError("Unsupported audio format");
-    SDL_free(song);
-    return NULL;
+    goto fail;
   }
 
   song->buffer_size = audio->samples;
   song->resample_buffer = SDL_malloc(audio->samples * sizeof(sample_t));
+  if (!song->resample_buffer) goto fail;
   song->common_buffer = SDL_malloc(audio->samples * 2 * sizeof(Sint32));
+  if (!song->common_buffer) goto fail;
 
   song->control_ratio = audio->freq / CONTROLS_PER_SECOND;
   if (song->control_ratio < 1)
@@ -510,10 +560,8 @@ MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
       &song->samples);
 
   /* Make sure everything is okay */
-  if (!song->events) {
-    SDL_free(song);
-    return(NULL);
-  }
+  if (!song->events)
+    goto fail;
 
   song->default_instrument = NULL;
   song->default_program = DEFAULT_PROGRAM;
@@ -523,13 +571,26 @@ MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
 
   load_missing_instruments(song);
 
-  return(song);
+  if (! song->oom)
+      *out = song;
+  else {
+fail: Timidity_FreeSong(song);
+  }
+}
+
+MidiSong *Timidity_LoadSong(SDL_RWops *rw, SDL_AudioSpec *audio)
+{
+  MidiSong *song;
+  do_song_load(rw, audio, &song);
+  return song;
 }
 
 void Timidity_FreeSong(MidiSong *song)
 {
   int i;
 
+  if (!song) return;
+
   free_instruments(song);
 
   for (i = 0; i < 128; i++) {
diff --git a/src/codecs/timidity/timidity.h b/src/codecs/timidity/timidity.h
index 9fb0f20..a3d94c9 100644
--- a/src/codecs/timidity/timidity.h
+++ b/src/codecs/timidity/timidity.h
@@ -106,6 +106,7 @@ typedef struct _MidiEventList {
 } MidiEventList;
 
 typedef struct {
+    int oom; /* malloc() failed */
     int playing;
     SDL_RWops *rw;
     Sint32 rate;