Skip to content

Commit 3741954

Browse files
committed
task_save_handler and task_load_handler run on the threaded task
worker. Their handlers call into core function pointers: content_get_serialized_data invokes core->retro_serialize, and task_load_handler invokes core_unserialize which calls core->retro_unserialize. CMD_EVENT_CORE_DEINIT runs runloop_event_deinit_core which calls uninit_libretro_symbols, which calls dylib_close. If a save/load state task is still running on the worker when the dylib closes, the worker's next call into the core dispatches through a freed function pointer. The existing wait at retroarch.c:4379 only fires when settings->bools.savestate_auto_save is enabled, and only covers the auto-state save kicked off three lines earlier. It does not cover a manually-triggered save state (menu, hotkey, netplay request) that was already in flight when the user closed content with savestate_auto_save disabled. It also does not cover load state tasks at all. Add unconditional waits for both save and load state tasks before runloop_event_deinit_core runs. task_queue_wait is a no-op when no matching task is in flight, so this is cheap on the common path. When a task is in flight, we let it finish - canceling mid-write would risk a truncated state file, which is worse than the brief delay during content close. Closes a real worker-thread / dylib_close UAF that triggers any time the user manually saves a state and closes content before the write completes.
1 parent 3eedad0 commit 3741954

1 file changed

Lines changed: 17 additions & 0 deletions

File tree

retroarch.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4379,6 +4379,23 @@ bool command_event(enum event_command cmd, void *data)
43794379
content_wait_for_save_state_task();
43804380
}
43814381

4382+
/* Wait for any in-flight save / load state tasks before
4383+
* tearing down the core. Both task_save_handler and
4384+
* task_load_handler run on the threaded task worker and
4385+
* call into core function pointers (retro_serialize via
4386+
* content_get_serialized_data, retro_unserialize via
4387+
* core_unserialize). If a worker is mid-call when
4388+
* runloop_event_deinit_core runs uninit_libretro_symbols,
4389+
* the worker dispatches into a closed dylib.
4390+
*
4391+
* The autosave_auto_save wait above only covers the
4392+
* auto-state save we just kicked off; it does not cover
4393+
* a manually-triggered save state task (menu / hotkey /
4394+
* netplay) that was already in flight when the user
4395+
* closed content with savestate_auto_save disabled. */
4396+
content_wait_for_save_state_task();
4397+
content_wait_for_load_state_task();
4398+
43824399
/* Save last selected disk index, if required */
43834400
if (sys_info)
43844401
disk_control_save_image_index(&sys_info->disk_control);

0 commit comments

Comments
 (0)