From e10ebbeb4461527d6ccf3d1f71da28110d055fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 27 Oct 2026 22:04:45 +0100 Subject: [PATCH] Savedata: Keep the list count at 0 when a name in the list is rejected The multi-name path counted names straight into saveDataListCount, so returning on a path traversal name left a count behind with the list already freed by Clear(). The next savestate then serialized that many entries from a null list and crashed in Do(std::string). Count on the side, and don't write entries for a list that isn't there. Co-Authored-By: Claude Opus 5.5 --- Core/Dialog/SavedataParam.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 9f9a4b39ad..299ec4d351 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -1557,18 +1557,20 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) { saveNameListData = param->saveNameList; - // Get number of fileName in array - saveDataListCount = 0; - while (saveNameListData[saveDataListCount][0] != 0) { + // Get number of fileName in array. Counted on the side, so a rejected name leaves the + // count at 0 to match the list Clear() freed (a savestate walks that many entries). + int count = 0; + while (saveNameListData[count][0] != 0) { // saveName entries become part of host filesystem paths; reject // path separators and bare dot components (path traversal). - const std::string_view entry = StringViewFromFixedSizeField(saveNameListData[saveDataListCount]); + const std::string_view entry = StringViewFromFixedSizeField(saveNameListData[count]); if (HasPathTraversal(entry)) { ERROR_LOG(Log::sceUtility, "SavedataParam: invalid saveName in list: %s", std::string(entry).c_str()); return SCE_ERROR_UTILITY_INVALID_PARAM_SIZE; } - saveDataListCount++; + count++; } + saveDataListCount = count; if (saveDataListCount > 0) { hasMultipleFileName = true; @@ -2015,6 +2017,9 @@ void SavedataParam::DoState(PointerWrap &p) { // pspParam is handled in PSPSaveDialog. Do(p, selectedSave); + if (p.mode != p.MODE_READ && !saveDataList) { + saveDataListCount = 0; + } Do(p, saveDataListCount); Do(p, saveNameListDataCount); if (p.mode == p.MODE_READ) {