Skip to content

Commit 2e05407

Browse files
committed
Address CodeRabbit fourth review round on PR 2013
- drawFilesystemTree now carries a visited-LBA set and skips recursing into already-seen directories, guarding against infinite recursion on malformed ISOs with directory cycles. Matches the guard already in place on collectFlatEntries. - Extraction coroutine now checks src->failed() up front and verifies every read/write return, aborting with a printed error instead of silently marking progress as complete on disk-full or short-write. - Hex editor ReadFn defaults to 0 and returns 0 on short reads; the BulkReadFn zero-fills any tail bytes that readAt didn't cover so I/O errors don't expose stale buffer contents in the hex view. Signed-off-by: Nicolas 'Pixel' Noble <nicolas@nobis-crew.org>
1 parent 3113760 commit 2e05407

2 files changed

Lines changed: 41 additions & 12 deletions

File tree

src/gui/widgets/isobrowser.cc

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include <algorithm>
2525
#include <chrono>
26+
#include <cstring>
2627

2728
#include "cdrom/cdriso.h"
2829
#include "cdrom/ppf.h"
@@ -57,7 +58,8 @@ PCSX::Coroutine<> PCSX::Widgets::IsoBrowser::computeCRC(PCSX::CDRIso* iso) {
5758
m_fullCRC = fullCRC;
5859
};
5960

60-
void PCSX::Widgets::IsoBrowser::drawFilesystemTree(const ISO9660LowLevel::DirEntry& entry, const std::string& path) {
61+
void PCSX::Widgets::IsoBrowser::drawFilesystemTree(const ISO9660LowLevel::DirEntry& entry, const std::string& path,
62+
std::unordered_set<uint32_t>& visitedDirs) {
6163
auto entries = m_reader->listAllEntriesFrom(entry);
6264

6365
for (auto& [dirEntry, xa] : entries) {
@@ -79,7 +81,10 @@ void PCSX::Widgets::IsoBrowser::drawFilesystemTree(const ISO9660LowLevel::DirEnt
7981
ImGui::TableSetColumnIndex(2);
8082
ImGui::TextUnformatted(_("<DIR>"));
8183
if (open) {
82-
drawFilesystemTree(dirEntry, fullPath);
84+
// Guard against malformed ISOs with directory cycles pointing back to an ancestor.
85+
if (visitedDirs.insert(lba).second) {
86+
drawFilesystemTree(dirEntry, fullPath, visitedDirs);
87+
}
8388
ImGui::TreePop();
8489
}
8590
} else {
@@ -636,18 +641,32 @@ significantly by caching the files beforehand.)"));
636641
std::string dest) -> Coroutine<> {
637642
auto time = std::chrono::steady_clock::now();
638643
IO<File> src(new CDRIsoFile(iso, lba, size, mode));
644+
if (src->failed()) {
645+
g_system->printf(_("ISO extract: failed to open source region.\n"));
646+
co_return;
647+
}
639648
IO<File> out(new UvFile(dest, FileOps::TRUNCATE));
640-
if (out->failed()) co_return;
649+
if (out->failed()) {
650+
g_system->printf(_("ISO extract: failed to open destination file.\n"));
651+
co_return;
652+
}
641653
uint8_t buffer[2048];
642654
uint32_t remaining = size;
643655
uint32_t written = 0;
644656
while (remaining > 0) {
645657
uint32_t chunk = std::min(remaining, (uint32_t)sizeof(buffer));
646658
auto read = src->read(buffer, chunk);
647-
if (read <= 0) break;
648-
out->write(buffer, read);
649-
remaining -= read;
650-
written += read;
659+
if (read <= 0) {
660+
g_system->printf(_("ISO extract: failed while reading ISO data.\n"));
661+
co_return;
662+
}
663+
auto wrote = out->write(buffer, read);
664+
if (wrote != read) {
665+
g_system->printf(_("ISO extract: failed while writing destination file.\n"));
666+
co_return;
667+
}
668+
remaining -= static_cast<uint32_t>(read);
669+
written += static_cast<uint32_t>(read);
651670
if (std::chrono::steady_clock::now() - time > std::chrono::milliseconds(50)) {
652671
self->m_extractionProgress = (float)written / (float)size;
653672
co_yield self->m_extractionCoroutine.awaiter();
@@ -752,7 +771,9 @@ significantly by caching the files beforehand.)"));
752771
ImGui::TableSetupColumn(_("LBA"), ImGuiTableColumnFlags_WidthFixed, 80.0f);
753772
ImGui::TableSetupColumn(_("Size"), ImGuiTableColumnFlags_WidthFixed, 100.0f);
754773
ImGui::TableHeadersRow();
755-
drawFilesystemTree(m_reader->getRootDirEntry(), "");
774+
std::unordered_set<uint32_t> visitedDirs;
775+
visitedDirs.insert(m_reader->getRootDirEntry().get<ISO9660LowLevel::DirEntry_LBA>());
776+
drawFilesystemTree(m_reader->getRootDirEntry(), "", visitedDirs);
756777
ImGui::EndTable();
757778
}
758779
}
@@ -780,13 +801,20 @@ significantly by caching the files beforehand.)"));
780801
}
781802
auto size = inst.m_file->size();
782803
inst.m_editor.ReadFn = [&inst](size_t off) -> ImU8 {
783-
ImU8 b;
784-
inst.m_file->readAt(&b, 1, off);
804+
ImU8 b = 0;
805+
auto r = inst.m_file->readAt(&b, 1, off);
806+
if (r != 1) b = 0;
785807
return b;
786808
};
787809
inst.m_editor.WriteFn = [&inst](size_t off, ImU8 d) { inst.m_file->writeAt(&d, 1, off); };
788810
inst.m_editor.Cache.BulkReadFn = [&inst](void* dest, size_t off, size_t len) {
789-
inst.m_file->readAt(dest, len, off);
811+
auto r = inst.m_file->readAt(dest, len, off);
812+
// Zero-fill anything we didn't read so stale buffer contents
813+
// never leak into the hex view on short reads or errors.
814+
if (r < 0) r = 0;
815+
if (static_cast<size_t>(r) < len) {
816+
std::memset(static_cast<uint8_t*>(dest) + r, 0, len - static_cast<size_t>(r));
817+
}
790818
};
791819
inst.m_editor.DrawWindow(inst.m_title.c_str(), size);
792820
++it;

src/gui/widgets/isobrowser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ class IsoBrowser {
9898
Coroutine<> m_gapScanCoroutine;
9999
float m_gapScanProgress = 0.0f;
100100

101-
void drawFilesystemTree(const ISO9660LowLevel::DirEntry& entry, const std::string& path);
101+
void drawFilesystemTree(const ISO9660LowLevel::DirEntry& entry, const std::string& path,
102+
std::unordered_set<uint32_t>& visitedDirs);
102103
void drawFilesystemFlat();
103104
void collectFlatEntries(const ISO9660LowLevel::DirEntry& entry, const std::string& path,
104105
std::unordered_set<uint32_t>& visitedDirs);

0 commit comments

Comments
 (0)