fuzz_thumb: Off by one error

This commit is contained in:
MerryMage 2018-01-14 20:21:47 +00:00
parent ecebe14a01
commit c34639f33d

View file

@ -80,6 +80,8 @@ static u16 MemoryRead16(u32 vaddr) {
static u32 MemoryRead32(u32 vaddr) { static u32 MemoryRead32(u32 vaddr) {
if (vaddr < code_mem.size() * sizeof(u16)) { if (vaddr < code_mem.size() * sizeof(u16)) {
size_t index = vaddr / sizeof(u16); size_t index = vaddr / sizeof(u16);
if (index + 1 >= code_mem.size())
return code_mem[index];
return code_mem[index] | (code_mem[index+1] << 16); return code_mem[index] | (code_mem[index+1] << 16);
} }
return vaddr; return vaddr;
@ -90,6 +92,8 @@ static u64 MemoryRead64(u32 vaddr) {
static u32 MemoryReadCode(u32 vaddr) { static u32 MemoryReadCode(u32 vaddr) {
if (vaddr < code_mem.size() * sizeof(u16)) { if (vaddr < code_mem.size() * sizeof(u16)) {
size_t index = vaddr / sizeof(u16); size_t index = vaddr / sizeof(u16);
if (index + 1 >= code_mem.size())
return code_mem[index];
return code_mem[index] | (code_mem[index + 1] << 16); return code_mem[index] | (code_mem[index + 1] << 16);
} }
return 0xE7FEE7FE; // b +#0, b +#0 return 0xE7FEE7FE; // b +#0, b +#0
@ -198,6 +202,90 @@ static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::A32::Ji
&& interp_write_records == jit_write_records; && interp_write_records == jit_write_records;
} }
static void RunInstance(size_t run_number, ARMul_State& interp, Dynarmic::A32::Jit& jit, const std::array<u32, 16>& initial_regs, size_t instruction_count, size_t instructions_to_execute_count) {
interp.instruction_cache.clear();
InterpreterClearCache();
jit.ClearCache();
// Setup initial state
interp.Cpsr = 0x000001F0;
interp.Reg = initial_regs;
jit.SetCpsr(0x000001F0);
jit.Regs() = initial_regs;
// Run interpreter
write_records.clear();
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
InterpreterMainLoop(&interp);
auto interp_write_records = write_records;
{
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
}
// Run jit
write_records.clear();
jit_num_ticks = instructions_to_execute_count;
jit.Run();
auto jit_write_records = write_records;
// Compare
if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) {
printf("Failed at execution number %zu\n", run_number);
printf("\nInstruction Listing: \n");
for (size_t i = 0; i < instruction_count; i++) {
printf("%04x %s\n", code_mem[i], Dynarmic::A32::DisassembleThumb16(code_mem[i]).c_str());
}
printf("\nInitial Register Listing: \n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x\n", i, initial_regs[i]);
}
printf("\nFinal Register Listing: \n");
printf(" interp jit\n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
}
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
printf("\nInterp Write Records:\n");
for (auto& record : interp_write_records) {
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data);
}
printf("\nJIT Write Records:\n");
for (auto& record : jit_write_records) {
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data);
}
Dynarmic::A32::PSR cpsr;
cpsr.T(true);
size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) {
Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, Dynarmic::A32::FPSCR{}};
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, &MemoryReadCode);
Dynarmic::Optimization::A32GetSetElimination(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, GetUserCallbacks().memory);
Dynarmic::Optimization::ConstantPropagation(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::VerificationPass(ir_block);
printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str());
printf("\n\nx86_64:\n%s", jit.Disassemble(descriptor).c_str());
num_insts += ir_block.CycleCount();
}
#ifdef _MSC_VER
__debugbreak();
#endif
FAIL();
}
}
void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) { void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function<u16()> instruction_generator) {
// Prepare memory // Prepare memory
code_mem.fill(0xE7FE); // b +#0 code_mem.fill(0xE7FE); // b +#0
@ -208,95 +296,13 @@ void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_e
Dynarmic::A32::Jit jit{GetUserCallbacks()}; Dynarmic::A32::Jit jit{GetUserCallbacks()};
for (size_t run_number = 0; run_number < run_count; run_number++) { for (size_t run_number = 0; run_number < run_count; run_number++) {
interp.instruction_cache.clear();
InterpreterClearCache();
jit.ClearCache();
// Setup initial state
std::array<u32, 16> initial_regs; std::array<u32, 16> initial_regs;
std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); }); std::generate_n(initial_regs.begin(), 15, []{ return RandInt<u32>(0, 0xFFFFFFFF); });
initial_regs[15] = 0; initial_regs[15] = 0;
interp.Cpsr = 0x000001F0;
interp.Reg = initial_regs;
jit.SetCpsr(0x000001F0);
jit.Regs() = initial_regs;
std::generate_n(code_mem.begin(), instruction_count, instruction_generator); std::generate_n(code_mem.begin(), instruction_count, instruction_generator);
// Run interpreter RunInstance(run_number, interp, jit, initial_regs, instruction_count, instructions_to_execute_count);
write_records.clear();
interp.NumInstrsToExecute = static_cast<unsigned>(instructions_to_execute_count);
InterpreterMainLoop(&interp);
auto interp_write_records = write_records;
{
bool T = Dynarmic::Common::Bit<5>(interp.Cpsr);
interp.Reg[15] &= T ? 0xFFFFFFFE : 0xFFFFFFFC;
}
// Run jit
write_records.clear();
jit_num_ticks = instructions_to_execute_count;
jit.Run();
auto jit_write_records = write_records;
// Compare
if (!DoesBehaviorMatch(interp, jit, interp_write_records, jit_write_records)) {
printf("Failed at execution number %zu\n", run_number);
printf("\nInstruction Listing: \n");
for (size_t i = 0; i < instruction_count; i++) {
printf("%s\n", Dynarmic::A32::DisassembleThumb16(code_mem[i]).c_str());
}
printf("\nInitial Register Listing: \n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x\n", i, initial_regs[i]);
}
printf("\nFinal Register Listing: \n");
printf(" interp jit\n");
for (int i = 0; i <= 15; i++) {
printf("%4i: %08x %08x %s\n", i, interp.Reg[i], jit.Regs()[i], interp.Reg[i] != jit.Regs()[i] ? "*" : "");
}
printf("CPSR: %08x %08x %s\n", interp.Cpsr, jit.Cpsr(), interp.Cpsr != jit.Cpsr() ? "*" : "");
printf("\nInterp Write Records:\n");
for (auto& record : interp_write_records) {
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data);
}
printf("\nJIT Write Records:\n");
for (auto& record : jit_write_records) {
printf("%zu [%x] = %" PRIu64 "\n", record.size, record.address, record.data);
}
Dynarmic::A32::PSR cpsr;
cpsr.T(true);
size_t num_insts = 0;
while (num_insts < instructions_to_execute_count) {
Dynarmic::A32::LocationDescriptor descriptor = {u32(num_insts * 4), cpsr, Dynarmic::A32::FPSCR{}};
Dynarmic::IR::Block ir_block = Dynarmic::A32::Translate(descriptor, &MemoryReadCode);
Dynarmic::Optimization::A32GetSetElimination(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::A32ConstantMemoryReads(ir_block, GetUserCallbacks().memory);
Dynarmic::Optimization::ConstantPropagation(ir_block);
Dynarmic::Optimization::DeadCodeElimination(ir_block);
Dynarmic::Optimization::VerificationPass(ir_block);
printf("\n\nIR:\n%s", Dynarmic::IR::DumpBlock(ir_block).c_str());
printf("\n\nx86_64:\n%s", jit.Disassemble(descriptor).c_str());
num_insts += ir_block.CycleCount();
}
#ifdef _MSC_VER
__debugbreak();
#endif
FAIL();
}
if (run_number % 10 == 0) printf("%zu\r", run_number);
} }
} }
@ -380,3 +386,40 @@ TEST_CASE("Fuzz Thumb instructions set 2 (affects PC)", "[JitX64][Thumb]") {
FuzzJitThumb(1, 1, 10000, instruction_select); FuzzJitThumb(1, 1, 10000, instruction_select);
} }
TEST_CASE("Verify fix for off by one error in MemoryRead32 worked", "[Thumb]") {
// Prepare memory
code_mem.fill(0xE7FE); // b +#0
// Prepare test subjects
ARMul_State interp{USER32MODE};
interp.user_callbacks = GetUserCallbacks();
Dynarmic::A32::Jit jit{GetUserCallbacks()};
std::array<u32, 16> initial_regs {
0xe90ecd70,
0x3e3b73c3,
0x571616f9,
0x0b1ef45a,
0xb3a829f2,
0x915a7a6a,
0x579c38f4,
0xd9ffe391,
0x55b6682b,
0x458d8f37,
0x8f3eb3dc,
0xe18c0e7d,
0x6752657a,
0x00001766,
0xdbbf23e3,
0x00000000,
};
code_mem[0] = 0x40B8; // lsls r0, r7, #0
code_mem[1] = 0x01CA; // lsls r2, r1, #7
code_mem[2] = 0x83A1; // strh r1, [r4, #28]
code_mem[3] = 0x708A; // strb r2, [r1, #2]
code_mem[4] = 0xBCC4; // pop {r2, r6, r7}
RunInstance(1, interp, jit, initial_regs, 5, 5);
}