mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-03-20 04:02:24 -04:00
DSPDisassembler: Fix out-of-bounds read when the last word is an instruction with a large immediate
For instance, ending with 0x009e (which you can do with CW 0x009e) indicates a LRI $ac0.m instruction, but there is no immediate value to load, so before whatever garbage in memory existed after the end of the file was used. The bounds-checking also previously assumed that IRAM or IROM was being used, both of which were exactly 0x1000 long.
This commit is contained in:
@@ -33,9 +33,10 @@ bool DSPDisassembler::Disassemble(const std::vector<u16>& code, std::string& tex
|
||||
|
||||
for (u16 pc = 0; pc < code.size();)
|
||||
{
|
||||
if (!DisassembleOpcode(code.data(), &pc, text))
|
||||
return false;
|
||||
bool failed = !DisassembleOpcode(code, &pc, text);
|
||||
text.append("\n");
|
||||
if (failed)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -139,16 +140,23 @@ std::string DSPDisassembler::DisassembleParameters(const DSPOPCTemplate& opc, u1
|
||||
return buf;
|
||||
}
|
||||
|
||||
bool DSPDisassembler::DisassembleOpcode(const u16* binbuf, u16* pc, std::string& dest)
|
||||
bool DSPDisassembler::DisassembleOpcode(const std::vector<u16>& code, u16* pc, std::string& dest)
|
||||
{
|
||||
if ((*pc & 0x7fff) >= 0x1000)
|
||||
return DisassembleOpcode(code.data(), code.size(), pc, dest);
|
||||
}
|
||||
|
||||
bool DSPDisassembler::DisassembleOpcode(const u16* binbuf, size_t binbuf_size, u16* pc,
|
||||
std::string& dest)
|
||||
{
|
||||
const u16 wrapped_pc = (*pc & 0x7fff);
|
||||
if (wrapped_pc >= binbuf_size)
|
||||
{
|
||||
++pc;
|
||||
dest.append("; outside memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
const u16 op1 = binbuf[*pc & 0x0fff];
|
||||
const u16 op1 = binbuf[wrapped_pc];
|
||||
|
||||
// Find main opcode
|
||||
const DSPOPCTemplate* opc = FindOpInfoByOpcode(op1);
|
||||
@@ -179,14 +187,23 @@ bool DSPDisassembler::DisassembleOpcode(const u16* binbuf, u16* pc, std::string&
|
||||
// printing
|
||||
|
||||
if (settings_.show_pc)
|
||||
dest += fmt::format("{:04x} ", *pc);
|
||||
dest += fmt::format("{:04x} ", wrapped_pc);
|
||||
|
||||
u16 op2;
|
||||
|
||||
// Size 2 - the op has a large immediate.
|
||||
if (opc->size == 2)
|
||||
{
|
||||
op2 = binbuf[(*pc + 1) & 0x0fff];
|
||||
if (wrapped_pc + 1 >= binbuf_size)
|
||||
{
|
||||
if (settings_.show_hex)
|
||||
dest += fmt::format("{:04x} ???? ", op1);
|
||||
dest += fmt::format("; Insufficient data for large immediate");
|
||||
*pc += opc->size;
|
||||
return false;
|
||||
}
|
||||
|
||||
op2 = binbuf[wrapped_pc + 1];
|
||||
if (settings_.show_hex)
|
||||
dest += fmt::format("{:04x} {:04x} ", op1, op2);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user