From 125355b7e15abbe79510fc81e59ad01178298059 Mon Sep 17 00:00:00 2001 From: 1e1 <1e1@users.noreply.github.com> Date: Mon, 29 Jun 2026 23:02:41 +0200 Subject: [PATCH] Filesystem: harden disc/title parsers against malformed input - WUD: compute the index-table size in 64-bit and validate it against the file size before allocating, and null-check the allocation. A crafted uncompressedSize could previously overflow a 32-bit size and under-allocate, leading to out-of-bounds access. - WUHB: guard against a zero-sized hash table (division by zero) and cap hash-chain traversal to avoid an infinite loop on cyclic chains. - WUHB directory listing and FSA ReadDir: use bounded, always-NUL-terminated name copies (a 128-255 byte filename could overflow the guest response). --- src/Cafe/Filesystem/WUD/wud.cpp | 14 +++++++++++++- src/Cafe/Filesystem/WUHB/WUHBReader.cpp | 9 ++++++++- src/Cafe/Filesystem/fscDeviceWuhb.cpp | 6 ++++-- src/Cafe/IOSU/fsa/iosu_fsa.cpp | 3 ++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/Cafe/Filesystem/WUD/wud.cpp b/src/Cafe/Filesystem/WUD/wud.cpp index 07f6b7a3..3e266506 100644 --- a/src/Cafe/Filesystem/WUD/wud.cpp +++ b/src/Cafe/Filesystem/WUD/wud.cpp @@ -43,8 +43,20 @@ wud_t* wud_open(const fs::path& path) wud->offsetSectorArray = (wud->offsetSectorArray + (long long)(wud->sectorSize-1)); wud->offsetSectorArray = wud->offsetSectorArray - (wud->offsetSectorArray%(long long)wud->sectorSize); // read index table - unsigned int indexTableSize = sizeof(unsigned int) * wud->indexTableEntryCount; + uint64 indexTableSize64 = (uint64)sizeof(unsigned int) * (uint64)wud->indexTableEntryCount; + if (indexTableSize64 == 0 || indexTableSize64 > 0x40000000ull || indexTableSize64 > (uint64)inputFileSize) + { + // reject implausible/crafted index table sizes to avoid integer overflow and over-allocation + wud_close(wud); + return nullptr; + } + unsigned int indexTableSize = (unsigned int)indexTableSize64; wud->indexTable = (unsigned int*)malloc(indexTableSize); + if (!wud->indexTable) + { + wud_close(wud); + return nullptr; + } wud->fs->SetPosition(wud->offsetIndexTable); if( wud->fs->readData(wud->indexTable, indexTableSize) != indexTableSize ) { diff --git a/src/Cafe/Filesystem/WUHB/WUHBReader.cpp b/src/Cafe/Filesystem/WUHB/WUHBReader.cpp index e7a4c9be..1ef28011 100644 --- a/src/Cafe/Filesystem/WUHB/WUHBReader.cpp +++ b/src/Cafe/Filesystem/WUHB/WUHBReader.cpp @@ -115,6 +115,8 @@ uint32 WUHBReader::GetHashTableEntryOffset(uint32 hash, bool isFile) const const uint64 hash_table_ofs = (isFile ? m_header.file_hash_table_ofs : m_header.dir_hash_table_ofs); const uint64 hash_table_entry_count = hash_table_size / sizeof(uint32); + if (hash_table_entry_count == 0) + return ROMFS_ENTRY_EMPTY; const uint64 hash_table_entry_offset = hash_table_ofs + (hash % hash_table_entry_count) * sizeof(uint32); m_fileIn->SetPosition(hash_table_entry_offset); @@ -131,10 +133,15 @@ uint32 WUHBReader::GetHashTableEntryOffset(uint32 hash, bool isFile) const template bool WUHBReader::SearchHashList(uint32& entryOffset, const fs::path& targetName) const { - for (;;) + for (uint32 iterations = 0; ; iterations++) { if (entryOffset == ROMFS_ENTRY_EMPTY) return false; + if (iterations >= 0x100000) + { + cemuLog_log(LogType::Force, "WUHB: aborting suspected cyclic hash chain"); + return false; + } auto entry = GetEntry(entryOffset); if (entry.name == targetName) diff --git a/src/Cafe/Filesystem/fscDeviceWuhb.cpp b/src/Cafe/Filesystem/fscDeviceWuhb.cpp index 5e8e6484..35cdc633 100644 --- a/src/Cafe/Filesystem/fscDeviceWuhb.cpp +++ b/src/Cafe/Filesystem/fscDeviceWuhb.cpp @@ -75,7 +75,8 @@ class FSCDeviceWuhbFileCtx : public FSCVirtualFile dirEntry->isDirectory = true; dirEntry->isFile = false; dirEntry->fileSize = 0; - std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH); + std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH - 1); + dirEntry->path[FSC_MAX_DIR_NAME_LENGTH - 1] = '\0'; return true; } } @@ -88,7 +89,8 @@ class FSCDeviceWuhbFileCtx : public FSCVirtualFile dirEntry->isDirectory = false; dirEntry->isFile = true; dirEntry->fileSize = entry.size; - std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH); + std::strncpy(dirEntry->path, entry.name.c_str(), FSC_MAX_DIR_NAME_LENGTH - 1); + dirEntry->path[FSC_MAX_DIR_NAME_LENGTH - 1] = '\0'; return true; } } diff --git a/src/Cafe/IOSU/fsa/iosu_fsa.cpp b/src/Cafe/IOSU/fsa/iosu_fsa.cpp index 1429d083..fee43aa8 100644 --- a/src/Cafe/IOSU/fsa/iosu_fsa.cpp +++ b/src/Cafe/IOSU/fsa/iosu_fsa.cpp @@ -556,7 +556,8 @@ namespace iosu FSCDirEntry fscDirEntry; if (fsc_nextDir(fscFile, &fscDirEntry) == false) return FSA_RESULT::END_OF_DIRECTORY; - strcpy(dirEntryOut->name, fscDirEntry.path); + strncpy(dirEntryOut->name, fscDirEntry.path, sizeof(dirEntryOut->name)); + dirEntryOut->name[sizeof(dirEntryOut->name) - 1] = '\0'; FSFlag statFlag = FSFlag::NONE; dirEntryOut->stat.size = 0; if (fscDirEntry.isDirectory)