From f31b530703722b333577e4ae6d53e1c1bfe632c7 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Thu, 7 Jul 2016 19:01:47 +0800 Subject: [PATCH] Fuzz thumb instructions --- src/backend_x64/emit_x64.cpp | 4 + src/backend_x64/emit_x64.h | 2 + src/backend_x64/interface_x64.cpp | 8 ++ tests/CMakeLists.txt | 2 +- tests/arm/fuzz_thumb.cpp | 180 +++++++++++++++++++++++++- tests/arm/test_thumb_instructions.cpp | 14 +- tests/rand_int.h | 20 +++ 7 files changed, 215 insertions(+), 15 deletions(-) create mode 100644 tests/rand_int.h diff --git a/src/backend_x64/emit_x64.cpp b/src/backend_x64/emit_x64.cpp index 369c8d3b..e3b7675c 100644 --- a/src/backend_x64/emit_x64.cpp +++ b/src/backend_x64/emit_x64.cpp @@ -458,5 +458,9 @@ void EmitX64::EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor init ASSERT_MSG(0, "Unimplemented"); } +void EmitX64::ClearCache() { + basic_blocks.clear(); +} + } // namespace BackendX64 } // namespace Dynarmic diff --git a/src/backend_x64/emit_x64.h b/src/backend_x64/emit_x64.h index f4d3a81b..4e4a2840 100644 --- a/src/backend_x64/emit_x64.h +++ b/src/backend_x64/emit_x64.h @@ -62,6 +62,8 @@ public: void EmitTerminalPopRSBHint(IR::Term::PopRSBHint terminal, Arm::LocationDescriptor initial_location); void EmitTerminalIf(IR::Term::If terminal, Arm::LocationDescriptor initial_location); + void ClearCache(); + private: std::set inhibit_emission; RegAlloc reg_alloc; diff --git a/src/backend_x64/interface_x64.cpp b/src/backend_x64/interface_x64.cpp index 7e678e16..3e053357 100644 --- a/src/backend_x64/interface_x64.cpp +++ b/src/backend_x64/interface_x64.cpp @@ -78,6 +78,14 @@ size_t Jit::Run(size_t cycle_count) { void Jit::ClearCache(bool poison_memory) { ASSERT(!is_executing); + + if (poison_memory) { + impl->block_of_code.ClearCodeSpace(); + } else { + impl->block_of_code.ResetCodePtr(); + } + + impl->emitter.ClearCache(); } void Jit::HaltExecution() { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4fa857af..801a7d37 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -14,7 +14,7 @@ set(SRCS skyeye_interpreter/skyeye_common/vfp/vfpdouble.cpp skyeye_interpreter/skyeye_common/vfp/vfpinstr.cpp skyeye_interpreter/skyeye_common/vfp/vfpsingle.cpp - ) + rand_int.h) set(HEADERS skyeye_interpreter/dyncom/arm_dyncom_dec.h diff --git a/tests/arm/fuzz_thumb.cpp b/tests/arm/fuzz_thumb.cpp index b3b7ccde..635bf56c 100644 --- a/tests/arm/fuzz_thumb.cpp +++ b/tests/arm/fuzz_thumb.cpp @@ -4,20 +4,24 @@ * General Public License version 2 or any later version. */ +#include + #include +#include #include "common/common_types.h" #include "interface/interface.h" +#include "rand_int.h" #include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "skyeye_interpreter/skyeye_common/armstate.h" -std::array code_mem{}; +static std::array code_mem{}; -u32 MemoryRead32(u32 vaddr); -void InterpreterFallback(u32 pc, Dynarmic::Jit* jit); -Dynarmic::UserCallbacks GetUserCallbacks(); +static u32 MemoryRead32(u32 vaddr); +static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit); +static Dynarmic::UserCallbacks GetUserCallbacks(); -u32 MemoryRead32(u32 vaddr) { +static u32 MemoryRead32(u32 vaddr) { if (vaddr < code_mem.size() * sizeof(u16)) { size_t index = vaddr / sizeof(u16); return code_mem[index] | (code_mem[index+1] << 16); @@ -25,7 +29,7 @@ u32 MemoryRead32(u32 vaddr) { return vaddr; } -void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) { +static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) { ARMul_State interp_state{USER32MODE}; interp_state.user_callbacks = GetUserCallbacks(); interp_state.NumInstrsToExecute = 1; @@ -41,10 +45,172 @@ void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) { jit->Cpsr() = interp_state.Cpsr; } -Dynarmic::UserCallbacks GetUserCallbacks() { +static void Fail() { + FAIL(); +} + +static Dynarmic::UserCallbacks GetUserCallbacks() { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.MemoryRead32 = &MemoryRead32; user_callbacks.InterpreterFallback = &InterpreterFallback; + user_callbacks.CallSVC = (bool (*)(u32)) &Fail; + user_callbacks.IsReadOnlyMemory = (bool (*)(u32)) &Fail; + user_callbacks.MemoryRead8 = (u8 (*)(u32)) &Fail; + user_callbacks.MemoryRead16 = (u16 (*)(u32)) &Fail; + user_callbacks.MemoryRead64 = (u64 (*)(u32)) &Fail; + user_callbacks.MemoryWrite8 = (void (*)(u32, u8)) &Fail; + user_callbacks.MemoryWrite16 = (void (*)(u32, u16)) &Fail; + user_callbacks.MemoryWrite32 = (void (*)(u32, u32)) &Fail; + user_callbacks.MemoryWrite64 = (void (*)(u32, u64)) &Fail; return user_callbacks; } +static std::pair FromBitString16(const char* str) { + REQUIRE(strlen(str) == 16); + + u16 bits = 0; + u16 mask = 0; + for (int i = 0; i < 16; i++) { + const u16 bit = 1 << (15 - i); + switch (str[i]) { + case '0': + mask |= bit; + break; + case '1': + bits |= bit; + mask |= bit; + break; + default: + // Do nothing + break; + } + } + return { bits, mask }; +} + +static bool DoesBehaviorMatch(const ARMul_State& interp, const Dynarmic::Jit& jit) { + const auto interp_regs = interp.Reg; + const auto jit_regs = jit.Regs(); + + return std::equal(interp_regs.begin(), interp_regs.end(), jit_regs.begin(), jit_regs.end()) + && interp.Cpsr == jit.Cpsr(); +} + + +void FuzzJitThumb(const size_t instruction_count, const size_t instructions_to_execute_count, const size_t run_count, const std::function instruction_generator) { + // Prepare memory + code_mem.fill(0xE7FE); // b +#0 + + // Prepare test subjects + ARMul_State interp{USER32MODE}; + interp.user_callbacks = GetUserCallbacks(); + Dynarmic::Jit jit{GetUserCallbacks()}; + + for (size_t run_number = 0; run_number < run_count; run_number++) { + interp.instruction_cache.clear(); + InterpreterClearCache(); + jit.ClearCache(); + + // Setup initial state + + std::array initial_regs; + std::generate_n(initial_regs.begin(), 15, []{ return RandInt(0, 0xFFFFFFFF); }); + initial_regs[15] = 0; + + interp.Cpsr = 0x000001F0; + interp.Reg = initial_regs; + jit.Cpsr() = 0x000001F0; + jit.Regs() = initial_regs; + + std::generate_n(code_mem.begin(), instruction_count, instruction_generator); + + // Run interpreter + interp.NumInstrsToExecute = instructions_to_execute_count; + InterpreterMainLoop(&interp); + + // Run jit + jit.Run(instructions_to_execute_count); + + // Compare + if (!DoesBehaviorMatch(interp, jit)) { + printf("Failed at execution number %zu\n", run_number); + + printf("\nInstruction Listing: \n"); + for (size_t i = 0; i < instruction_count; i++) { + printf("%04x\n", code_mem[i]); + } + + printf("\nFinal Register Listing: \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() ? "*" : ""); + +#ifdef _MSC_VER + DebugBreak(); +#endif + FAIL(); + } + } +} + +TEST_CASE("Fuzz Thumb instructions set 1", "[JitX64][Thumb]") { + const std::array, 16> instructions = {{ + FromBitString16("00000xxxxxxxxxxx"), // LSL , , # + FromBitString16("00001xxxxxxxxxxx"), // LSR , , # + FromBitString16("00010xxxxxxxxxxx"), // ASR , , # + FromBitString16("000110oxxxxxxxxx"), // ADD/SUB_reg + FromBitString16("000111oxxxxxxxxx"), // ADD/SUB_imm + FromBitString16("001ooxxxxxxxxxxx"), // ADD/SUB/CMP/MOV_imm + FromBitString16("010000ooooxxxxxx"), // Data Processing + FromBitString16("010001000hxxxxxx"), // ADD (high registers) + FromBitString16("010001010hxxxxxx"), // CMP (high registers) + FromBitString16("01000101h0xxxxxx"), // CMP (high registers) + FromBitString16("010001100hxxxxxx"), // MOV (high registers) + FromBitString16("10110000oxxxxxxx"), // Adjust stack pointer + FromBitString16("10110010ooxxxxxx"), // SXT/UXT + FromBitString16("1011101000xxxxxx"), // REV + FromBitString16("1011101001xxxxxx"), // REV16 + FromBitString16("1011101011xxxxxx"), // REVSH + //FromBitString16("01001xxxxxxxxxxx"), // LDR Rd, [PC, #] + //FromBitString16("0101oooxxxxxxxxx"), // LDR/STR Rd, [Rn, Rm] + //FromBitString16("011xxxxxxxxxxxxx"), // LDR(B)/STR(B) Rd, [Rn, #] + //FromBitString16("1000xxxxxxxxxxxx"), // LDRH/STRH Rd, [Rn, #offset] + //FromBitString16("1001xxxxxxxxxxxx"), // LDR/STR Rd, [SP, #] + //FromBitString16("1011x100xxxxxxxx"), // PUSH/POP (R = 0) + //FromBitString16("1100xxxxxxxxxxxx"), // STMIA/LDMIA + //FromBitString16("101101100101x000"), // SETEND + }}; + + auto instruction_select = [&]() -> u16 { + size_t inst_index = RandInt(0, instructions.size() - 1); + + if (inst_index == 22) { + u16 L = RandInt(0, 1); + u16 Rn = RandInt(0, 7); + u16 reg_list = RandInt(1, 0xFF); + if (!L && (reg_list & (1 << Rn))) { + reg_list &= ~((1 << Rn) - 1); + if (reg_list == 0) reg_list = 0x80; + } + u16 random = (L << 11) | (Rn << 8) | reg_list; + return instructions[inst_index].first | (random &~instructions[inst_index].second); + } else if (inst_index == 21) { + u16 L = RandInt(0, 1); + u16 reg_list = RandInt(1, 0xFF); + u16 random = (L << 11) | reg_list; + return instructions[inst_index].first | (random &~instructions[inst_index].second); + } else { + u16 random = RandInt(0, 0xFFFF); + return instructions[inst_index].first | (random &~instructions[inst_index].second); + } + }; + + SECTION("short blocks") { + FuzzJitThumb(5, 6, 10000, instruction_select); + } + + SECTION("long blocks") { + FuzzJitThumb(1024, 1025, 15, instruction_select); + } +} diff --git a/tests/arm/test_thumb_instructions.cpp b/tests/arm/test_thumb_instructions.cpp index cd3c7244..95d5232d 100644 --- a/tests/arm/test_thumb_instructions.cpp +++ b/tests/arm/test_thumb_instructions.cpp @@ -11,13 +11,13 @@ #include "skyeye_interpreter/dyncom/arm_dyncom_interpreter.h" #include "skyeye_interpreter/skyeye_common/armstate.h" -std::array code_mem{}; +static std::array code_mem{}; -u32 MemoryRead32(u32 vaddr); -void InterpreterFallback(u32 pc, Dynarmic::Jit* jit); -Dynarmic::UserCallbacks GetUserCallbacks(); +static u32 MemoryRead32(u32 vaddr); +static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit); +static Dynarmic::UserCallbacks GetUserCallbacks(); -u32 MemoryRead32(u32 vaddr) { +static u32 MemoryRead32(u32 vaddr) { if (vaddr < code_mem.size() * sizeof(u16)) { size_t index = vaddr / sizeof(u16); return code_mem[index] | (code_mem[index+1] << 16); @@ -25,7 +25,7 @@ u32 MemoryRead32(u32 vaddr) { return vaddr; } -void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) { +static void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) { ARMul_State interp_state{USER32MODE}; interp_state.user_callbacks = GetUserCallbacks(); interp_state.NumInstrsToExecute = 1; @@ -41,7 +41,7 @@ void InterpreterFallback(u32 pc, Dynarmic::Jit* jit) { jit->Cpsr() = interp_state.Cpsr; } -Dynarmic::UserCallbacks GetUserCallbacks() { +static Dynarmic::UserCallbacks GetUserCallbacks() { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.MemoryRead32 = &MemoryRead32; user_callbacks.InterpreterFallback = &InterpreterFallback; diff --git a/tests/rand_int.h b/tests/rand_int.h new file mode 100644 index 00000000..3d7ea667 --- /dev/null +++ b/tests/rand_int.h @@ -0,0 +1,20 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include + +template +T RandInt(T min, T max) { + static_assert(std::is_integral::value, "T must be an integral type."); + static_assert(!std::is_same::value && !std::is_same::value, + "Using char with uniform_int_distribution is undefined behavior."); + + static std::random_device rd; + static std::mt19937 mt(rd()); + std::uniform_int_distribution rand(min, max); + return rand(mt); +}