From fd6c33980b14adf8379d29e5393731ecda02c9c9 Mon Sep 17 00:00:00 2001 From: Eladash Date: Mon, 14 Feb 2022 21:03:40 +0200 Subject: [PATCH] cellSaveData: Check filename format of savedata files (#11502) --- rpcs3/Emu/Cell/Modules/cellSaveData.cpp | 180 +++++++++++++----------- 1 file changed, 94 insertions(+), 86 deletions(-) diff --git a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp index 46f5e714a3..b8014f68dc 100644 --- a/rpcs3/Emu/Cell/Modules/cellSaveData.cpp +++ b/rpcs3/Emu/Cell/Modules/cellSaveData.cpp @@ -120,6 +120,91 @@ struct savedata_manager atomic_t last_cbresult_error_dialog{0}; // CBRESULT errors are negative }; +int check_filename(std::string_view file_path, bool disallow_system_files, bool account_sfo_pfd) +{ + if (file_path.size() >= CELL_SAVEDATA_FILENAME_SIZE) + { + // ****** sysutil savedata parameter error : 71 ****** + return 71; + } + + auto dotpos = file_path.find_last_of('.'); + + if (dotpos == umax) + { + // Point to end of string instead + dotpos = file_path.size(); + } + + if (file_path.empty() || dotpos > 8u || file_path.size() - dotpos > 4u) + { + // ****** sysutil savedata parameter error : 70 ****** + return 70; + } + + if (file_path == "."sv || (!account_sfo_pfd && (file_path == "PARAM.SFO"sv || file_path == "PARAM.PFD"sv))) + { + // ****** sysutil savedata parameter error : 70 ****** + return 70; + } + + char name[CELL_SAVEDATA_FILENAME_SIZE - 3]; + + if (dotpos) + { + // Copy file name + std::span dst(name, dotpos + 1); + strcpy_trunc(dst, file_path); + + // Allow multiple '.' even though sysutil_check_name_string does not + std::replace(name, name + dotpos, '.', '-'); + + // Allow '_' at start even though sysutil_check_name_string does not + if (name[0] == '_') + { + name[0] = '-'; + } + + if (disallow_system_files && ((dotpos >= 5u && std::memcmp(name, "PARAM", 5) == 0) || + (dotpos >= 4u && std::memcmp(name, "ICON", 4) == 0) || + (dotpos >= 3u && std::memcmp(name, "PIC", 3) == 0) || + (dotpos >= 3u && std::memcmp(name, "SND", 3) == 0))) + { + // ****** sysutil savedata parameter error : 70 ****** + return 70; + } + + // Check filename + if (sysutil_check_name_string(name, 1, 9) == -1) + { + // ****** sysutil savedata parameter error : 70 ****** + return 70; + } + } + + if (file_path.size() > dotpos + 1) + { + // Copy file extension + std::span dst(name, file_path.size() - dotpos); + strcpy_trunc(dst, file_path.substr(dotpos + 1)); + + // Allow '_' at start even though sysutil_check_name_string does not + if (name[0] == '_') + { + name[0] = '-'; + } + + // Check file extension + if (sysutil_check_name_string(name, 1, 4) == -1) + { + // ****** sysutil savedata parameter error : 70 ****** + return 70; + } + } + + return 0; +} + static std::vector get_save_entries(const std::string& base_dir, const std::string& prefix) { std::vector save_entries; @@ -159,7 +244,7 @@ static std::vector get_save_entries(const std::string& base_dir, for (const auto& entry2 : fs::dir(base_dir + entry.name)) { - if (entry2.is_directory) + if (entry2.is_directory || check_filename(vfs::unescape(entry2.name), false, true)) { continue; } @@ -686,7 +771,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v for (const auto& entry2 : fs::dir(base_dir + entry.name)) { - if (entry2.is_directory) + if (entry2.is_directory || check_filename(vfs::unescape(entry2.name), false, true)) { continue; } @@ -1350,12 +1435,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v if (!entry.is_directory) { - if (entry.name == "."sv) - { - continue; - } - - if (entry.name == "PARAM.SFO"sv || entry.name == "PARAM.PFD"sv) + if (check_filename(entry.name, false, false)) { continue; // system files are not included in the file list } @@ -1575,7 +1655,7 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v // Read file into a vector and make a memory file entry.name = vfs::unescape(entry.name); - if (entry.name == ".") + if (check_filename(entry.name, false, true)) { continue; } @@ -1647,85 +1727,12 @@ static NEVER_INLINE error_code savedata_op(ppu_thread& ppu, u32 operation, u32 v break; } - auto dotpos = file_path.find_last_of('.'); - - if (dotpos == umax) + if (int error = check_filename(file_path, true, false)) { - // Point to end of string instead - dotpos = file_path.size(); - } - - if (file_path.empty() || dotpos > 8u || file_path.size() - dotpos > 4u) - { - cellSaveData.error("savedata_op(): fileSet->fileName is illegal ('%s')", file_path); - - // ****** sysutil savedata parameter error : 70 ****** - savedata_result = {CELL_SAVEDATA_ERROR_PARAM, "70"}; + savedata_result = {CELL_SAVEDATA_ERROR_PARAM, "%d", error}; break; } - char name[10]; - - if (dotpos) - { - // Copy file name - std::span dst(name, dotpos + 1); - strcpy_trunc(dst, file_path); - - // Allow multiple '.' even though sysutil_check_name_string does not - std::replace(name, name + dotpos, '.', '-'); - - // Allow '_' at start even though sysutil_check_name_string does not - if (name[0] == '_') - { - name[0] = '-'; - } - - if ((dotpos >= 5u && std::memcmp(name, "PARAM", 5) == 0) || - (dotpos >= 4u && std::memcmp(name, "ICON", 4) == 0) || - (dotpos >= 3u && std::memcmp(name, "PIC", 3) == 0) || - (dotpos >= 3u && std::memcmp(name, "SND", 3) == 0)) - { - // ****** sysutil savedata parameter error : 70 ****** - cellSaveData.error("savedata_op(): fileSet->fileName is set to a system file name (%s)", file_path); - savedata_result = {CELL_SAVEDATA_ERROR_PARAM, "70"}; - break; - } - - // Check filename - if (sysutil_check_name_string(name, 1, 9) == -1) - { - cellSaveData.error("savedata_op(): fileSet->fileName is illegal due to file name ('%s')", file_path); - - // ****** sysutil savedata parameter error : 70 ****** - savedata_result = {CELL_SAVEDATA_ERROR_PARAM, "70"}; - break; - } - } - - if (file_path.size() > dotpos + 1) - { - // Copy file extension - std::span dst(name, file_path.size() - dotpos); - strcpy_trunc(dst, file_path.operator std::string_view().substr(dotpos + 1)); - - // Allow '_' at start even though sysutil_check_name_string does not - if (name[0] == '_') - { - name[0] = '-'; - } - - // Check file extension - if (sysutil_check_name_string(name, 1, 4) == -1) - { - cellSaveData.error("savedata_op(): fileSet->fileName is illegal due to file extension ('%s')", file_path); - - // ****** sysutil savedata parameter error : 70 ****** - savedata_result = {CELL_SAVEDATA_ERROR_PARAM, "70"}; - break; - } - } - if (type == CELL_SAVEDATA_FILETYPE_SECUREFILE) { cellSaveData.notice("SECUREFILE: %s -> %s", file_path, fileSet->secureFileId); @@ -2087,7 +2094,7 @@ static NEVER_INLINE error_code savedata_get_list_item(vm::cptr dirName, vm for (const auto& entry : fs::dir(save_path)) { - if (entry.is_directory) + if (entry.is_directory || check_filename(vfs::unescape(entry.name), false, false)) { continue; } @@ -2095,7 +2102,8 @@ static NEVER_INLINE error_code savedata_get_list_item(vm::cptr dirName, vm size_kbytes += ::narrow((entry.size + 1023) / 1024); // firmware rounds this value up } - *sizeKB = size_kbytes; + // Add a seemingly constant allocation disk space of PARAM.SFO + PARAM.PFD + *sizeKB = size_kbytes + 35; } if (bind)