cellSaveData: Check filename format of savedata files (#11502)

This commit is contained in:
Eladash 2022-02-14 21:03:40 +02:00 committed by GitHub
parent 2d5d5746d1
commit fd6c33980b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -120,6 +120,91 @@ struct savedata_manager
atomic_t<s32> 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<SaveDataEntry> get_save_entries(const std::string& base_dir, const std::string& prefix)
{
std::vector<SaveDataEntry> save_entries;
@ -159,7 +244,7 @@ static std::vector<SaveDataEntry> 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<char> 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<char> dirName, vm
size_kbytes += ::narrow<u32>((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)