From 864778a11b0501778665756ed0603542e56ed6b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 9 Oct 2026 14:25:55 +0200 Subject: [PATCH] FileSystems: Reject parent directory references in savestate file names DirectoryFileSystem reopens the open files' guest paths on load, with their original access mode, so a crafted state could create or truncate host files outside the memstick or game folder. VirtualDiscFileSystem had the same problem for reads. Guest paths never contain ".." here, the meta file system resolves them. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/FileSystems/DirectoryFileSystem.cpp | 8 ++++++++ Core/FileSystems/VirtualDiscFileSystem.cpp | 13 +++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 86203720b4..0ec3e80cde 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -39,6 +39,7 @@ #include "Core/Replay.h" #include "Core/Reporting.h" #include "Core/ELF/ParamSFO.h" +#include "Core/Util/PathUtil.h" #ifdef _WIN32 #include "Common/CommonWindows.h" @@ -1113,6 +1114,13 @@ void DirectoryFileSystem::DoState(PointerWrap &p) { Do(p, key); Do(p, entry.guestFilename); Do(p, entry.access); + // Guest paths never contain "..", the file system resolves them before they get here. + // One from a savestate would be opened (and possibly created or truncated) outside basePath. + if (HasParentDirComponent(entry.guestFilename)) { + ERROR_LOG(Log::FileSystem, "Savestate has a file name with a parent directory reference"); + p.SetError(p.ERROR_FAILURE); + return; + } u32 err; // The original open created the file, so an exclusive create would fail now. bool opened = entry.hFile.Open(basePath, entry.guestFilename, (FileAccess)(entry.access & ~FILEACCESS_EXCL), err); diff --git a/Core/FileSystems/VirtualDiscFileSystem.cpp b/Core/FileSystems/VirtualDiscFileSystem.cpp index ff7a8ce3af..c540e753ee 100644 --- a/Core/FileSystems/VirtualDiscFileSystem.cpp +++ b/Core/FileSystems/VirtualDiscFileSystem.cpp @@ -194,6 +194,12 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p) Do(p, entryCount); Do(p, currentBlockIndex); + if (fileListSize < 0 || entryCount < 0 || !p.CheckRead(fileListSize)) { + ERROR_LOG(Log::FileSystem, "Savestate has an invalid file list size"); + p.SetError(p.ERROR_FAILURE); + return; + } + FileListEntry dummy = {""}; fileList.resize(fileListSize, dummy); @@ -202,6 +208,13 @@ void VirtualDiscFileSystem::DoState(PointerWrap &p) Do(p, fileList[i].fileName); Do(p, fileList[i].firstBlock); Do(p, fileList[i].totalSize); + // Same check as for the index file, these get opened under basePath. + if (HasParentDirComponent(fileList[i].fileName)) { + ERROR_LOG(Log::FileSystem, "Savestate has a file name with a parent directory reference"); + p.SetError(p.ERROR_FAILURE); + fileList[i].fileName.clear(); + return; + } } if (p.mode == p.MODE_READ)