@@ -94,15 +94,19 @@ typedef struct sound {
9494} sound_t ;
9595
9696/* SDL-mixer-related and music-related variables */
97- static pthread_t music_thread ;
9897static uint8_t * music_midi_data ;
9998#if RV32_HAS (SDL_MIXER )
10099static Mix_Music * mid ;
101100
102101/* SDL-mixer-related and sfx-related variables */
103- static pthread_t sfx_thread ;
104102static Mix_Chunk * sfx_chunk ;
105103#endif
104+
105+ #ifdef __EMSCRIPTEN__
106+ /* Thread handles only needed for EMSCRIPTEN joinable threads */
107+ static pthread_t music_thread ;
108+ static pthread_t sfx_thread ;
109+ #endif
106110static uint8_t * sfx_samples ;
107111static uint32_t nr_sfx_samples ;
108112static int chan ;
@@ -742,12 +746,16 @@ static void *sfx_handler(void *arg)
742746
743747 memcpy (sfx_samples , ptr , sizeof (uint8_t ) * nr_sfx_samples );
744748 sfx_chunk = Mix_QuickLoad_RAW (sfx_samples , nr_sfx_samples );
745- if (!sfx_chunk )
749+ if (!sfx_chunk ) {
750+ free (sfx );
746751 return NULL ;
752+ }
747753
748754 chan = Mix_PlayChannel (-1 , sfx_chunk , 0 );
749- if (chan == -1 )
755+ if (chan == -1 ) {
756+ free (sfx );
750757 return NULL ;
758+ }
751759
752760 if (* ptr & 0x3 ) {
753761 /* Doom, multiplied by 8 because sfx->volume's max is 15 */
@@ -759,6 +767,7 @@ static void *sfx_handler(void *arg)
759767 Mix_Volume (chan , (sfx -> volume + 1 ) % 128 );
760768 }
761769
770+ free (sfx );
762771 return NULL ;
763772}
764773
@@ -767,23 +776,35 @@ static void *music_handler(void *arg)
767776 sound_t * music = (sound_t * ) arg ;
768777 int looping = music -> looping ? -1 : 1 ;
769778
779+ /* Free previous MIDI data */
770780 free (music_midi_data );
781+ music_midi_data = NULL ;
782+
783+ /* Free previous music to prevent memory leak */
784+ if (mid ) {
785+ Mix_HaltMusic ();
786+ Mix_FreeMusic (mid );
787+ mid = NULL ;
788+ }
771789
772790 music_midi_data = mus2midi (music -> data , (int * ) & music -> size );
773791 if (!music_midi_data ) {
774792 rv_log_error ("mus2midi() failed" );
793+ free (music );
775794 return NULL ;
776795 }
777796
778797 SDL_RWops * rwops = SDL_RWFromMem (music_midi_data , music -> size );
779798 if (!rwops ) {
780799 rv_log_error ("SDL_RWFromMem failed: %s" , SDL_GetError ());
800+ free (music );
781801 return NULL ;
782802 }
783803
784804 mid = Mix_LoadMUSType_RW (rwops , MUS_MID , SDL_TRUE );
785805 if (!mid ) {
786806 rv_log_error ("Mix_LoadMUSType_RW failed: %s" , Mix_GetError ());
807+ free (music );
787808 return NULL ;
788809 }
789810
@@ -794,9 +815,11 @@ static void *music_handler(void *arg)
794815
795816 if (Mix_PlayMusic (mid , looping ) == -1 ) {
796817 rv_log_error ("Mix_PlayMusic failed: %s" , Mix_GetError ());
818+ free (music );
797819 return NULL ;
798820 }
799821
822+ free (music );
800823 return NULL ;
801824}
802825
@@ -808,21 +831,15 @@ static void play_sfx(riscv_t *rv)
808831 int volume = rv_get_reg (rv , rv_reg_a2 );
809832
810833 sfxinfo_t sfxinfo ;
811- uint8_t sfx_data [SFX_SAMPLE_SIZE ];
834+ uint32_t sfx_data_size ;
835+
812836#if RV32_HAS (SYSTEM_MMIO )
813837 uint32_t addr = rv -> io .mem_translate (rv , sfxinfo_addr , R );
814838 memory_read (attr -> mem , (uint8_t * ) & sfxinfo , addr , sizeof (sfxinfo_t ));
815839
816840 uint32_t sfx_data_offset =
817841 * ((uint32_t * ) ((uint8_t * ) attr -> mem -> mem_base + addr ));
818- uint32_t sfx_data_size =
819- * (uint32_t * ) ((uint8_t * ) attr -> mem -> mem_base + addr + 4 );
820- uint32_t sfx_data_vaddr = sfx_data_offset ;
821- uint8_t * sfx_data_ptr = & sfx_data [0 ];
822- remain_size = sfx_data_size ;
823- curr_offset = 0 ;
824-
825- GET_SFX_DATA_FROM_RANDOM_PAGE (sfx_data_vaddr , sfx_data_ptr );
842+ sfx_data_size = * (uint32_t * ) ((uint8_t * ) attr -> mem -> mem_base + addr + 4 );
826843#else
827844 memory_read (attr -> mem , (uint8_t * ) & sfxinfo , sfxinfo_addr ,
828845 sizeof (sfxinfo_t ));
@@ -832,28 +849,57 @@ static void play_sfx(riscv_t *rv)
832849 * various applications when accessing different sfxinfo_t instances.
833850 */
834851 uint32_t sfx_data_offset = * ((uint32_t * ) & sfxinfo );
835- uint32_t sfx_data_size = * (uint32_t * ) ((uint32_t * ) & sfxinfo + 1 );
836- memory_read (attr -> mem , sfx_data , sfx_data_offset ,
837- sizeof (uint8_t ) * sfx_data_size );
852+ sfx_data_size = * (uint32_t * ) ((uint32_t * ) & sfxinfo + 1 );
838853#endif
839854
840- sound_t sfx = {
841- .data = sfx_data ,
842- .size = sfx_data_size ,
843- .volume = volume ,
844- };
845855#if RV32_HAS (SDL_MIXER )
846- pthread_create (& sfx_thread , NULL , sfx_handler , & sfx );
847- sfx_thread_init = true;
856+ /* Validate size to prevent excessive allocation from untrusted guest */
857+ if (sfx_data_size == 0 || sfx_data_size > SFX_SAMPLE_SIZE )
858+ return ;
859+
860+ /* Heap allocate sound_t + data buffer together (thread takes ownership) */
861+ sound_t * sfx = malloc (sizeof (sound_t ) + sfx_data_size );
862+ if (!sfx )
863+ return ;
864+
865+ sfx -> data = (uint8_t * ) (sfx + 1 ); /* Data follows the struct */
866+ sfx -> size = sfx_data_size ;
867+ sfx -> volume = volume ;
868+
869+ #if RV32_HAS (SYSTEM_MMIO )
870+ uint32_t sfx_data_vaddr = sfx_data_offset ;
871+ uint8_t * sfx_data_ptr = sfx -> data ;
872+ remain_size = sfx_data_size ;
873+ curr_offset = 0 ;
874+
875+ GET_SFX_DATA_FROM_RANDOM_PAGE (sfx_data_vaddr , sfx_data_ptr );
876+ #else
877+ memory_read (attr -> mem , sfx -> data , sfx_data_offset ,
878+ sizeof (uint8_t ) * sfx_data_size );
848879#endif
849- /* FIXME: In web browser runtime, web workers in thread pool do not reap
850- * after sfx_handler return, thus we have to join them. sfx_handler does not
851- * contain infinite loop,so do not worry to be stalled by it */
880+
852881#ifdef __EMSCRIPTEN__
853- #if RV32_HAS (SDL_MIXER )
882+ /* Web browser: use joinable thread and wait for completion */
883+ if (pthread_create (& sfx_thread , NULL , sfx_handler , sfx ) != 0 ) {
884+ free (sfx );
885+ return ;
886+ }
854887 pthread_join (sfx_thread , NULL );
888+ /* Thread already joined - don't join again in shutdown_audio */
889+ #else
890+ /* Native: use detached thread for non-blocking playback */
891+ pthread_t thread ;
892+ pthread_attr_t thread_attr ;
893+ pthread_attr_init (& thread_attr );
894+ pthread_attr_setdetachstate (& thread_attr , PTHREAD_CREATE_DETACHED );
895+ if (pthread_create (& thread , & thread_attr , sfx_handler , sfx ) != 0 ) {
896+ pthread_attr_destroy (& thread_attr );
897+ free (sfx );
898+ return ;
899+ }
900+ pthread_attr_destroy (& thread_attr );
855901#endif
856- #endif
902+ #endif /* RV32_HAS(SDL_MIXER) */
857903}
858904
859905static void play_music (riscv_t * rv )
@@ -865,7 +911,8 @@ static void play_music(riscv_t *rv)
865911 int looping = rv_get_reg (rv , rv_reg_a3 );
866912
867913 musicinfo_t musicinfo ;
868- uint8_t music_data [MUSIC_MAX_SIZE ];
914+ uint32_t music_data_size ;
915+
869916#if RV32_HAS (SYSTEM_MMIO )
870917 uint32_t addr = rv -> io .mem_translate (rv , musicinfo_addr , R );
871918 memory_read (attr -> mem , (uint8_t * ) & musicinfo , addr , sizeof (musicinfo_t ));
@@ -876,14 +923,8 @@ static void play_music(riscv_t *rv)
876923 */
877924 uint32_t music_data_offset =
878925 * ((uint32_t * ) ((uint8_t * ) attr -> mem -> mem_base + addr ));
879- uint32_t music_data_size =
926+ music_data_size =
880927 * (uint32_t * ) ((uint8_t * ) attr -> mem -> mem_base + addr + 4 );
881- uint32_t music_data_vaddr = music_data_offset ;
882- uint8_t * music_data_ptr = & music_data [0 ];
883- remain_size = music_data_size ;
884- curr_offset = 0 ;
885-
886- GET_MUSIC_DATA_FROM_RANDOM_PAGE (music_data_vaddr , music_data_ptr );
887928#else
888929 memory_read (attr -> mem , (uint8_t * ) & musicinfo , musicinfo_addr ,
889930 sizeof (musicinfo_t ));
@@ -893,28 +934,57 @@ static void play_music(riscv_t *rv)
893934 * various applications when accessing different sfxinfo_t instances.
894935 */
895936 uint32_t music_data_offset = * ((uint32_t * ) & musicinfo );
896- uint32_t music_data_size = * (uint32_t * ) ((uint32_t * ) & musicinfo + 1 );
897- memory_read (attr -> mem , music_data , music_data_offset , music_data_size );
937+ music_data_size = * (uint32_t * ) ((uint32_t * ) & musicinfo + 1 );
898938#endif
899939
900- sound_t music = {
901- .data = music_data ,
902- .size = music_data_size ,
903- .looping = looping ,
904- .volume = volume ,
905- };
906940#if RV32_HAS (SDL_MIXER )
907- pthread_create (& music_thread , NULL , music_handler , & music );
908- music_thread_init = true;
941+ /* Validate size to prevent excessive allocation from untrusted guest */
942+ if (music_data_size == 0 || music_data_size > MUSIC_MAX_SIZE )
943+ return ;
944+
945+ /* Heap allocate sound_t + data buffer together (thread takes ownership) */
946+ sound_t * music = malloc (sizeof (sound_t ) + music_data_size );
947+ if (!music )
948+ return ;
949+
950+ music -> data = (uint8_t * ) (music + 1 ); /* Data follows the struct */
951+ music -> size = music_data_size ;
952+ music -> looping = looping ;
953+ music -> volume = volume ;
954+
955+ #if RV32_HAS (SYSTEM_MMIO )
956+ uint32_t music_data_vaddr = music_data_offset ;
957+ uint8_t * music_data_ptr = music -> data ;
958+ remain_size = music_data_size ;
959+ curr_offset = 0 ;
960+
961+ GET_MUSIC_DATA_FROM_RANDOM_PAGE (music_data_vaddr , music_data_ptr );
962+ #else
963+ memory_read (attr -> mem , music -> data , music_data_offset , music_data_size );
909964#endif
910- /* FIXME: In web browser runtime, web workers in thread pool do not reap
911- * after music_handler return, thus we have to join them. music_handler does
912- * not contain infinite loop,so do not worry to be stalled by it */
965+
913966#ifdef __EMSCRIPTEN__
914- #if RV32_HAS (SDL_MIXER )
967+ /* Web browser: use joinable thread and wait for completion */
968+ if (pthread_create (& music_thread , NULL , music_handler , music ) != 0 ) {
969+ free (music );
970+ return ;
971+ }
915972 pthread_join (music_thread , NULL );
973+ /* Thread already joined - don't join again in shutdown_audio */
974+ #else
975+ /* Native: use detached thread for non-blocking playback */
976+ pthread_t thread ;
977+ pthread_attr_t thread_attr ;
978+ pthread_attr_init (& thread_attr );
979+ pthread_attr_setdetachstate (& thread_attr , PTHREAD_CREATE_DETACHED );
980+ if (pthread_create (& thread , & thread_attr , music_handler , music ) != 0 ) {
981+ pthread_attr_destroy (& thread_attr );
982+ free (music );
983+ return ;
984+ }
985+ pthread_attr_destroy (& thread_attr );
916986#endif
917- #endif
987+ #endif /* RV32_HAS(SDL_MIXER) */
918988}
919989
920990static void stop_music ()
@@ -965,27 +1035,34 @@ static void init_audio(void)
9651035
9661036static void shutdown_audio ()
9671037{
1038+ #if RV32_HAS (SDL_MIXER )
1039+ /* Stop all playback first */
1040+ Mix_HaltMusic ();
1041+ Mix_HaltChannel (-1 );
1042+
1043+ #ifdef __EMSCRIPTEN__
9681044 /*
969- * sfx_thread and music_thread might contain invalid identifiers.
970- * Additionally, there is no built-in method to verify the validity
971- * of a pthread_t type. Therefore, the sfx_thread_init and music_thread_init
972- * flags are used for validation, ensuring that pthread_join always operates
973- * on a valid pthread_t identifier.
1045+ * For EMSCRIPTEN, threads are joinable. Join them if initialized to ensure
1046+ * handlers have completed before freeing resources.
9741047 */
975-
976- #if RV32_HAS (SDL_MIXER )
977- if (music_thread_init ) {
978- stop_music ();
1048+ if (music_thread_init )
9791049 pthread_join (music_thread , NULL );
1050+ if (sfx_thread_init )
1051+ pthread_join (sfx_thread , NULL );
1052+ #endif
1053+
1054+ /* Free music resources */
1055+ if (mid ) {
9801056 Mix_FreeMusic (mid );
981- free (music_midi_data );
982- music_midi_data = NULL ;
1057+ mid = NULL ;
9831058 }
1059+ free (music_midi_data );
1060+ music_midi_data = NULL ;
9841061
985- if (sfx_thread_init ) {
986- pthread_join (sfx_thread , NULL );
987- Mix_HaltChannel (-1 );
1062+ /* Free sfx resources */
1063+ if (sfx_chunk ) {
9881064 Mix_FreeChunk (sfx_chunk );
1065+ sfx_chunk = NULL ;
9891066 }
9901067
9911068 Mix_CloseAudio ();
0 commit comments