Skip to content

Commit 3eedad0

Browse files
committed
runloop_event_deinit_core calls retro_unload_game and retro_deinit
before uninit_libretro_symbols. The autosave_t->retro_buffer field borrows a pointer from core_get_memory(), and the autosave worker thread reads from it continuously via memcpy and intfstream_write. If a worker is mid-read when retro_unload_game / retro_deinit frees the underlying memory, the worker reads freed memory. The standard MAIN_DEINIT path joins the autosave worker at retroarch.c:8508 (autosave_deinit, gated on RUNLOOP_FLAG_USE_SRAM) before reaching CMD_EVENT_CORE_DEINIT at line 8524. That path is safe. The error paths in retroarch_main_init are not safe. The dummy-core fallback at retroarch.c:8314 and the error: label at retroarch.c:8414 both call CMD_EVENT_CORE_DEINIT directly without first joining the autosave worker. event_init_content (the function whose failure typically lands us in those error paths) calls runloop_path_init_savefile -> CMD_EVENT_AUTOSAVE_INIT, which can start a worker thread before later steps in event_init_content fail. That worker is still running when CMD_EVENT_CORE_DEINIT executes. Make runloop_event_deinit_core join the autosave worker itself, gated on RUNLOOP_FLAG_USE_SRAM to match the existing MAIN_DEINIT gate. autosave_deinit is idempotent (loop is empty when autosave_state.num is 0, free(NULL) is a no-op), so this is safe to call from the standard path where the worker was already joined. This makes the retro_buffer borrow lifetime contract local to runloop_event_deinit_core rather than depending on call-site discipline at every CMD_EVENT_CORE_DEINIT site.
1 parent 7c4b483 commit 3eedad0

1 file changed

Lines changed: 19 additions & 0 deletions

File tree

runloop.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4063,6 +4063,25 @@ void runloop_event_deinit_core(void)
40634063
runloop_state_t *runloop_st = &runloop_state;
40644064
settings_t *settings = config_get_ptr();
40654065

4066+
#ifdef HAVE_THREADS
4067+
/* Defensive: ensure the autosave worker thread is joined
4068+
* before we touch core-owned memory. autosave_t->retro_buffer
4069+
* borrows a pointer from core_get_memory(); if a worker is
4070+
* mid-read when retro_unload_game / retro_deinit frees that
4071+
* region, the worker reads freed memory.
4072+
*
4073+
* The standard MAIN_DEINIT path already calls autosave_deinit
4074+
* before reaching here, so this is normally a no-op. The error
4075+
* paths in retroarch_main_init (the dummy-core fallback at
4076+
* retroarch.c:8314 and the error: label at retroarch.c:8414)
4077+
* reach CMD_EVENT_CORE_DEINIT without the explicit teardown,
4078+
* which can leave a worker alive if event_init_content failed
4079+
* after starting one. autosave_deinit is idempotent so the
4080+
* extra call is safe regardless of which path got us here. */
4081+
if (runloop_st->flags & RUNLOOP_FLAG_USE_SRAM)
4082+
autosave_deinit();
4083+
#endif
4084+
40664085
core_unload_game();
40674086

40684087
/* Reset core sensor tracking — the core is going away */

0 commit comments

Comments
 (0)