From 7e64052914e550341cb63fe8176cecab0276ee9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 11 Aug 2026 01:32:47 +0200 Subject: [PATCH] CachingFileLoader: don't cache blocks the backend failed to fully read SaveIntoCache() discarded backend_->ReadAt()'s return value entirely and unconditionally marked the requested block(s) as cached. A short or failed read from the backend (e.g. a Remote ISO connection dropping mid-file, now that LocalFileLoader/RetryingFileLoader correctly report failures as 0 rather than a huge count) would still get stored as a "valid" cached block, permanently serving its uninitialized tail as if it were real file data on every later read, with no retry. Only insert a block once we've confirmed the backend actually delivered the full BLOCK_SIZE for it. --- Core/FileLoaders/CachingFileLoader.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/Core/FileLoaders/CachingFileLoader.cpp b/Core/FileLoaders/CachingFileLoader.cpp index c35b879565..ca63cdf8e8 100644 --- a/Core/FileLoaders/CachingFileLoader.cpp +++ b/Core/FileLoaders/CachingFileLoader.cpp @@ -174,13 +174,21 @@ void CachingFileLoader::SaveIntoCache(s64 pos, size_t bytes, Flags flags, bool r blocksMutex_.unlock(); u8 *buf = new u8[BLOCK_SIZE]; - backend_->ReadAt(cacheStartPos << BLOCK_SHIFT, BLOCK_SIZE, buf, flags); + size_t readBytes = backend_->ReadAt(cacheStartPos << BLOCK_SHIFT, BLOCK_SIZE, buf, flags); blocksMutex_.lock(); // While blocksMutex_ was unlocked, another thread may have read. // If so, free the one we just read. if (blocks_.find(cacheStartPos) == blocks_.end()) { - blocks_[cacheStartPos] = BlockInfo{buf}; + // Only cache a block we actually fully read - a short/failed read (e.g. a + // dropped connection on a Remote ISO) must not be cached as if valid, or + // every later read of this block would silently return the uninitialized + // tail of `buf` as if it were real file data. + if (readBytes == BLOCK_SIZE) { + blocks_[cacheStartPos] = BlockInfo{buf}; + } else { + delete [] buf; + } } else { delete [] buf; } @@ -188,10 +196,11 @@ void CachingFileLoader::SaveIntoCache(s64 pos, size_t bytes, Flags flags, bool r blocksMutex_.unlock(); u8 *wholeRead = new u8[blocksToRead << BLOCK_SHIFT]; - backend_->ReadAt(cacheStartPos << BLOCK_SHIFT, blocksToRead << BLOCK_SHIFT, wholeRead, flags); + size_t readBytes = backend_->ReadAt(cacheStartPos << BLOCK_SHIFT, blocksToRead << BLOCK_SHIFT, wholeRead, flags); + size_t wholeBlocksRead = readBytes >> BLOCK_SHIFT; blocksMutex_.lock(); - for (size_t i = 0; i < blocksToRead; ++i) { + for (size_t i = 0; i < wholeBlocksRead; ++i) { if (blocks_.find(cacheStartPos + i) != blocks_.end()) { // Written while we were busy, just skip it. Keep the existing block. continue;