@@ -372,66 +372,109 @@ int main(int argc, char **argv)
372372 * RASTATE format. */
373373 if (state_load_path )
374374 {
375- FILE * stf = fopen (state_load_path , "rb" );
375+ FILE * stf = NULL ;
376+ long st_total = 0 ;
377+ uint8_t * st_buf = NULL ;
378+ const uint8_t * payload = NULL ;
379+ size_t payload_size = 0 ;
380+ size_t expected ;
381+ const char * state_err = NULL ;
382+
383+ stf = fopen (state_load_path , "rb" );
376384 if (!stf )
377385 {
378- fprintf ( stderr , "ERROR: cannot open state file: %s\n" , state_load_path ) ;
379- return 1 ;
386+ state_err = " cannot open state file" ;
387+ goto state_fail ;
380388 }
389+ if (fseek (stf , 0 , SEEK_END ) != 0 )
381390 {
382- long st_total ;
383- uint8_t * st_buf ;
384- const uint8_t * payload ;
385- size_t payload_size ;
386- size_t expected ;
387- fseek (stf , 0 , SEEK_END );
388- st_total = ftell (stf );
389- fseek (stf , 0 , SEEK_SET );
390- st_buf = (uint8_t * )malloc (st_total );
391- if (fread (st_buf , 1 , st_total , stf ) != (size_t )st_total )
392- {
393- fprintf (stderr , "ERROR: short read on state file\n" );
394- free (st_buf ); fclose (stf ); return 1 ;
395- }
396- fclose (stf );
397- payload = st_buf ;
398- payload_size = (size_t )st_total ;
399- /* RASTATE container? "RASTATE" + 1 version byte, then chunks. */
400- if (st_total >= 16 && memcmp (st_buf , "RASTATE" , 7 ) == 0 )
391+ state_err = "fseek to end failed" ;
392+ goto state_fail ;
393+ }
394+ st_total = ftell (stf );
395+ if (st_total <= 0 )
396+ {
397+ state_err = "ftell failed or empty state file" ;
398+ goto state_fail ;
399+ }
400+ if (fseek (stf , 0 , SEEK_SET ) != 0 )
401+ {
402+ state_err = "fseek to start failed" ;
403+ goto state_fail ;
404+ }
405+ st_buf = (uint8_t * )malloc ((size_t )st_total );
406+ if (!st_buf )
407+ {
408+ state_err = "malloc failed for state buffer" ;
409+ goto state_fail ;
410+ }
411+ if (fread (st_buf , 1 , (size_t )st_total , stf ) != (size_t )st_total )
412+ {
413+ state_err = "short read on state file" ;
414+ goto state_fail ;
415+ }
416+ fclose (stf );
417+ stf = NULL ;
418+ payload = st_buf ;
419+ payload_size = (size_t )st_total ;
420+ /* RASTATE container? "RASTATE" + 1 version byte, then chunks. */
421+ if (st_total >= 16 && memcmp (st_buf , "RASTATE" , 7 ) == 0 )
422+ {
423+ const uint8_t * p = st_buf + 8 ; /* past magic+version */
424+ const uint8_t * end = st_buf + st_total ;
425+ int found = 0 ;
426+ /* Each chunk: 4-byte type + 4-byte LE size + payload. */
427+ while (p + 8 <= end )
401428 {
402- const uint8_t * p = st_buf + 8 ; /* past magic+version */
403- const uint8_t * end = st_buf + st_total ;
404- int found = 0 ;
405- while ( p + 8 <= end )
429+ uint32_t chunk_size = ( uint32_t ) p [ 4 ] | (( uint32_t ) p [ 5 ] << 8 )
430+ | (( uint32_t ) p [ 6 ] << 16 ) | (( uint32_t ) p [ 7 ] << 24 ) ;
431+ /* Bounds-check the declared chunk size against the buffer. */
432+ if ( chunk_size > ( uint32_t )( end - ( p + 8 )) )
406433 {
407- uint32_t chunk_size = (uint32_t )p [4 ] | ((uint32_t )p [5 ] << 8 )
408- | ((uint32_t )p [6 ] << 16 ) | ((uint32_t )p [7 ] << 24 );
409- if (memcmp (p , "MEM " , 4 ) == 0 )
410- {
411- payload = p + 8 ;
412- payload_size = chunk_size ;
413- found = 1 ;
414- break ;
415- }
416- p += 8 + chunk_size ;
434+ state_err = "RASTATE chunk size overruns buffer" ;
435+ goto state_fail ;
417436 }
418- if (! found )
437+ if (memcmp ( p , "MEM " , 4 ) == 0 )
419438 {
420- fprintf (stderr , "ERROR: no MEM chunk in RASTATE file\n" );
421- free (st_buf ); return 1 ;
439+ payload = p + 8 ;
440+ payload_size = chunk_size ;
441+ found = 1 ;
442+ break ;
422443 }
423- fprintf ( stderr , "--- RASTATE: extracted MEM chunk (%zu bytes) ---\n" , payload_size ) ;
444+ p += 8 + chunk_size ;
424445 }
425- expected = pretro_serialize_size ();
426- fprintf (stderr , "--- State payload: %zu bytes (core expects %zu) ---\n" ,
427- payload_size , expected );
428- if (!pretro_unserialize (payload , payload_size ))
446+ if (!found )
429447 {
430- fprintf ( stderr , "ERROR: retro_unserialize failed\n" ) ;
431- free ( st_buf ); return 1 ;
448+ state_err = "no MEM chunk in RASTATE file" ;
449+ goto state_fail ;
432450 }
433- fprintf (stderr , "--- State loaded from %s ---\n" , state_load_path );
434- free (st_buf );
451+ fprintf (stderr , "--- RASTATE: extracted MEM chunk (%zu bytes) ---\n" , payload_size );
452+ }
453+ expected = pretro_serialize_size ();
454+ fprintf (stderr , "--- State payload: %zu bytes (core expects %zu) ---\n" ,
455+ payload_size , expected );
456+ if (!pretro_unserialize (payload , payload_size ))
457+ {
458+ state_err = "retro_unserialize failed" ;
459+ goto state_fail ;
460+ }
461+ fprintf (stderr , "--- State loaded from %s ---\n" , state_load_path );
462+ free (st_buf );
463+ st_buf = NULL ;
464+
465+ if (0 )
466+ {
467+ state_fail :
468+ fprintf (stderr , "ERROR: %s: %s\n" ,
469+ state_err ? state_err : "state load error" ,
470+ state_load_path );
471+ if (stf ) fclose (stf );
472+ if (st_buf ) free (st_buf );
473+ pretro_unload_game ();
474+ pretro_deinit ();
475+ free ((void * )info .data );
476+ dlclose (handle );
477+ return 1 ;
435478 }
436479 }
437480
0 commit comments