From 8aafad9cf3904360581dc814aeda5346db174321 Mon Sep 17 00:00:00 2001 From: Acts1631 Date: Wed, 2 Sep 2026 14:32:16 -0400 Subject: [PATCH] Bounds-check Cg binary byte swapping --- rpcs3/Emu/RSX/Program/CgBinaryProgram.cpp | 54 ++++++++++++++++++++--- rpcs3/Emu/RSX/Program/CgBinaryProgram.h | 2 +- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/rpcs3/Emu/RSX/Program/CgBinaryProgram.cpp b/rpcs3/Emu/RSX/Program/CgBinaryProgram.cpp index ae576bb21c59..72a6b6c2ba21 100644 --- a/rpcs3/Emu/RSX/Program/CgBinaryProgram.cpp +++ b/rpcs3/Emu/RSX/Program/CgBinaryProgram.cpp @@ -77,13 +77,21 @@ std::string CgBinaryDisasm::GetCgParamValue(u32 offset, u32 end_offset) const return fmt::format("num %d ", num) + offsets; } -void CgBinaryDisasm::ConvertToLE(CgBinaryProgram& prog) +bool CgBinaryDisasm::ConvertToLE(CgBinaryProgram& prog) { // BE payload, requires that data be swapped const auto be_profile = prog.profile; auto swap_be32 = [&](u32 start_offset, size_t size_bytes) { + if (static_cast(start_offset) > m_buffer.size() + || size_bytes > m_buffer.size() - start_offset + || start_offset % alignof(u32) + || size_bytes % sizeof(u32)) + { + return false; + } + auto start = reinterpret_cast(m_buffer.data() + start_offset); auto end = reinterpret_cast(m_buffer.data() + start_offset + size_bytes); @@ -91,20 +99,40 @@ void CgBinaryDisasm::ConvertToLE(CgBinaryProgram& prog) { *data = std::bit_cast>(*data); } + + return true; }; // 1. Swap the header - swap_be32(0, sizeof(CgBinaryProgram)); + if (!swap_be32(0, sizeof(CgBinaryProgram))) + { + return false; + } // 2. Swap parameters - swap_be32(prog.parameterArray, sizeof(CgBinaryParameter) * prog.parameterCount); + if (static_cast(prog.parameterCount) > static_cast(umax) / sizeof(CgBinaryParameter) + || !swap_be32(prog.parameterArray, sizeof(CgBinaryParameter) * prog.parameterCount)) + { + return false; + } // 3. Swap the ucode - swap_be32(prog.ucode, m_buffer.size() - prog.ucode); + if (static_cast(prog.ucode) > m_buffer.size() + || !swap_be32(prog.ucode, m_buffer.size() - prog.ucode)) + { + return false; + } // 4. Swap the domain header if (be_profile == 7004u) { + if (static_cast(prog.program) > m_buffer.size() + || sizeof(CgBinaryFragmentProgram) > m_buffer.size() - prog.program + || prog.program % alignof(u32)) + { + return false; + } + // Need to swap each field individually auto& fprog = GetCgRef(prog.program); fprog.instructionCount = std::bit_cast>(fprog.instructionCount); @@ -117,20 +145,34 @@ void CgBinaryDisasm::ConvertToLE(CgBinaryProgram& prog) else { // Swap entire header block as all fields are u32 - swap_be32(prog.program, sizeof(CgBinaryVertexProgram)); + if (!swap_be32(prog.program, sizeof(CgBinaryVertexProgram))) + { + return false; + } } + + return true; } void CgBinaryDisasm::BuildShaderBody(bool include_glsl) { ParamArray param_array; + if (m_buffer.size() < sizeof(CgBinaryProgram)) + { + return; + } + auto& prog = GetCgRef(0); if (const u32 be_profile = std::bit_cast>(prog.profile); be_profile == 7003u || be_profile == 7004u) { - ConvertToLE(prog); + if (!ConvertToLE(prog)) + { + return; + } + ensure(be_profile == prog.profile); } diff --git a/rpcs3/Emu/RSX/Program/CgBinaryProgram.h b/rpcs3/Emu/RSX/Program/CgBinaryProgram.h index 65e4deee97eb..d3e2bd9155f1 100644 --- a/rpcs3/Emu/RSX/Program/CgBinaryProgram.h +++ b/rpcs3/Emu/RSX/Program/CgBinaryProgram.h @@ -208,7 +208,7 @@ class CgBinaryDisasm std::string GetCgParamSemantic(u32 offset) const; std::string GetCgParamValue(u32 offset, u32 end_offset) const; - void ConvertToLE(CgBinaryProgram& prog); + bool ConvertToLE(CgBinaryProgram& prog); void BuildShaderBody(bool include_glsl = true); static u32 GetData(const u32 d) { return d << 16 | d >> 16; }