From a940efa0bbc5566e52c868dea71830b6b888379e Mon Sep 17 00:00:00 2001 From: Bryan Kruman Date: Fri, 3 Jul 2026 22:27:25 -0400 Subject: [PATCH] Fix out-of-bounds read when naming reloc types beyond the o32 table reloc_names has 8 entries (R_MIPS_NONE..R_MIPS_GPREL16), but reloc.type is set from the raw ELF r_type, so relocatable/PIC inputs carrying GOT16 / CALL16 / GPREL32 / ... (types >= 8) index past the end. reloc_names[type] is then an out-of-bounds std::vector read (UB): benign on some libstdc++ builds, but on the Windows STL it returns a garbage std::string whose huge size makes fmt's format buffer throw std::bad_alloc mid-write, aborting the recompiler after most output is already written (and truncating it). Add a bounds-checked reloc_type_name() that maps out-of-range types to R_MIPS_NONE, and use it at both call sites (dump_context and the overlay reloc table writer). Co-Authored-By: Claude Opus 4.8 --- src/main.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 669c4a1..ce73201 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -158,6 +158,17 @@ std::vector reloc_names { "R_MIPS_GPREL16", }; +// reloc_names only covers the basic o32 relocation types (0..7). A relocatable/PIC input can carry higher +// types (GOT16, CALL16, GPREL32, ...) whose numeric value indexes past the end of reloc_names. Indexing the +// vector directly is then an out-of-bounds read (undefined behavior) — harmless on some libstdc++ builds, +// but on the Windows STL it returns a garbage std::string whose enormous size makes fmt's format buffer +// throw std::bad_alloc mid-write, killing the recompiler after it has written most of its output. Treat any +// out-of-range type as R_MIPS_NONE (which is also how scripts/fix-recompiled.sh normalizes these entries). +static const std::string& reloc_type_name(N64Recomp::RelocType type) { + size_t idx = static_cast(type); + return idx < reloc_names.size() ? reloc_names[idx] : reloc_names[0]; +} + void dump_context(const N64Recomp::Context& context, const std::unordered_map>& data_syms, const std::filesystem::path& func_path, const std::filesystem::path& data_path) { std::ofstream func_context_file {func_path}; std::ofstream data_context_file {data_path}; @@ -211,7 +222,7 @@ void dump_context(const N64Recomp::Context& context, const std::unordered_map(reloc.type)], reloc.address, reloc.target_section_offset + section.ram_addr); + reloc_type_name(reloc.type), reloc.address, reloc.target_section_offset + section.ram_addr); } fmt::print(func_context_file, "]\n\n"); } @@ -996,7 +1007,7 @@ int main(int argc, char** argv) { target_section_offset = reloc.target_section_offset; } fmt::print(overlay_file, " {{ .offset = 0x{:08X}, .target_section_offset = 0x{:08X}, .target_section = {}, .type = {} }}, \n", - reloc.address - section.ram_addr, target_section_offset, reloc.target_section, reloc_names[static_cast(reloc.type)] ); + reloc.address - section.ram_addr, target_section_offset, reloc.target_section, reloc_type_name(reloc.type) ); } }